Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enh: add bot authorization token, use cloudflare custom domain with WAF #20

Merged
merged 2 commits into from
Sep 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pulumi/Pulumi.production.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ config:
environment: production
project: erfiume
aws:region: eu-west-1
erfiume:telegram-authorization-token:
secure: AAABAAtA6vkP45ayorXzotOCFcQKQoeCjsLAFRG06Dr/DY1k3XbPLCdwAhiXtIh8T8b7rH0E/Ct9IrnvHbvJb7WyvTW4EG/92at/kVy8jBhjSp9uZ++Z3vCOI8rid2eP
erfiume:telegram-bot-token:
secure: AAABAPwvcC722Zh0BGmJZ73kUE7l0G9DDM40Q34uSQWJciCW9YdzWiqgzO/UvouqD7IGD6/O3q/KCnfphKeUQVC5L/89r+pjKOiAZSR0
2 changes: 2 additions & 0 deletions pulumi/Pulumi.staging.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -268,5 +268,7 @@ config:
aws:secretKey: test
aws:skipCredentialsValidation: "true"
aws:skipRequestingAccountId: "true"
erfiume:telegram-authorization-token:
secure: AAABANBm/MQpNhcald3hBBL7UPUAhiYz5jxH+zeC4suyb48OpZcrnZHIhKl3qcPMRqjKtmdLGVoit/Cs+GVrfDprpQ2S4g1c7aujoehK2WTHuJwkznfqRzjyEXMxRd45
erfiume:telegram-bot-token:
secure: AAABAJCtZcMcH6RjMHY80HjCxKpVe2pLIHvj6g/v9efhMAZIg3HiNYmofthrZhjrSnkSMKzSrVcoqpvOgMUvV+zsZ1GUonnFmBYZ/VvA
60 changes: 57 additions & 3 deletions pulumi/__main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""An AWS Python Pulumi program"""

import pulumi
import pulumi_cloudflare
from pulumi_aws import (
apigatewayv2,
cloudwatch,
Expand All @@ -19,6 +20,7 @@
SYNC_MINUTES_RATE_NORMAL = 24 * 60 # Once a day
SYNC_MINUTES_RATE_EMERGENCY = 20
EMERGENCY = False
CUSTOM_DOMAIN_NAME = "erfiume.thedodo.xyz"

stazioni_table = dynamodb.Table(
f"{RESOURCES_PREFIX}-stazioni",
Expand Down Expand Up @@ -146,7 +148,7 @@
"ENVIRONMENT": pulumi.get_stack(),
},
},
memory_size=1024,
memory_size=768,
timeout=50,
)

Expand All @@ -164,7 +166,7 @@
"ENVIRONMENT": pulumi.get_stack(),
},
},
memory_size=1024,
memory_size=768,
timeout=10,
)

Expand Down Expand Up @@ -237,11 +239,21 @@
)

if pulumi.get_stack() == "production":
gw_domain_name = apigatewayv2.DomainName(
f"{RESOURCES_PREFIX}-bot",
domain_name=CUSTOM_DOMAIN_NAME,
domain_name_configuration=apigatewayv2.DomainNameDomainNameConfigurationArgs(
certificate_arn="arn:aws:acm:eu-west-1:841162699174:certificate/109ca827-8d70-4e11-8995-0b3dbdbd0510",
endpoint_type="REGIONAL",
security_policy="TLS_1_2",
),
)
bot_webhook_gw = apigatewayv2.Api(
f"{RESOURCES_PREFIX}-webhook",
protocol_type="HTTP",
route_key="POST /erfiume_bot",
target=bot_lambda.arn,
disable_execute_api_endpoint=True,
)
lambda_.Permission(
f"{RESOURCES_PREFIX}-lambda-bot-api-gateway",
Expand All @@ -250,9 +262,51 @@
principal="apigateway.amazonaws.com",
source_arn=bot_webhook_gw.execution_arn.apply(lambda arn: f"{arn}/*/*"),
)
gw_api_mapping = apigatewayv2.ApiMapping(
f"{RESOURCES_PREFIX}-bot-domain-mapping",
api_id=bot_webhook_gw.id,
domain_name=gw_domain_name.domain_name,
stage="$default",
)

pulumi_cloudflare.Record(
f"{RESOURCES_PREFIX}-api-gw-cname",
name="erfiume",
type="CNAME",
zone_id="cec5bf01afed114303a536c264a1f394",
proxied=True,
content=gw_domain_name.domain_name_configuration.target_domain_name,
)

telegram_authorization_token = pulumi.Config().require_secret(
"telegram-authorization-token"
)
pulumi_cloudflare.Ruleset(
f"{RESOURCES_PREFIX}-waf",
zone_id="cec5bf01afed114303a536c264a1f394",
name="erfiume-bot-check-authorization-header",
description="erfiume_bot Block Invalid Authorization Header",
kind="zone",
phase="http_request_firewall_custom",
rules=[
pulumi_cloudflare.RulesetRuleArgs(
action="block",
expression="(cf.client.bot)",
enabled=True,
),
pulumi_cloudflare.RulesetRuleArgs(
action="block",
expression=telegram_authorization_token.apply(
lambda header: f'(all(http.request.headers["x-telegram-bot-api-secret-token"][*] ne "{header}") and http.host eq "{CUSTOM_DOMAIN_NAME}")' # noqa: E501
),
enabled=True,
),
],
)

Webhook(
f"{RESOURCES_PREFIX}-apigateway-registration",
token=pulumi.Config().require_secret("telegram-bot-token"),
url=bot_webhook_gw.api_endpoint.apply(lambda url: f"{url}/erfiume_bot"),
authorization_token=telegram_authorization_token,
url=f"https://{CUSTOM_DOMAIN_NAME}/erfiume_bot",
)
18 changes: 17 additions & 1 deletion pulumi/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pulumi/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package-mode = false
python = "^3.12"
pulumi-aws = "^6.52.0"
pulumi-command = "^1.0.1"
pulumi-cloudflare = "^5.39.0"

[tool.poetry.group.dev.dependencies]
awscli-local = "^0.22.0"
Expand Down
23 changes: 19 additions & 4 deletions pulumi/telegram_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ class _TelegramWebhookProvider(ResourceProvider):
def create(self, props: dict[str, Any]) -> CreateResult:
webhook_url = props["url"]
token = props["token"]
secret_token = props["authorization_token"]
response = requests.post(
f"https://api.telegram.org/bot{token}/setWebhook",
json={"url": webhook_url},
json={
"url": webhook_url,
"secret_token": secret_token,
},
timeout=10,
)
if response.status_code != requests.codes.OK:
raise requests.RequestException(response.text)
return CreateResult(id_="-", outs={})
return CreateResult(id_="-")


class Webhook(Resource):
Expand All @@ -34,8 +38,19 @@ class Webhook(Resource):
"""

def __init__(
self, name: str, token: str | pulumi.Output[str], url: str | pulumi.Output[str]
self,
name: str,
token: str | pulumi.Output[str],
url: str | pulumi.Output[str],
authorization_token: str | pulumi.Output[str] | None = None,
) -> None:
super().__init__(
_TelegramWebhookProvider(), name, {"token": token, "url": url}, None
_TelegramWebhookProvider(),
name,
{
"token": token,
"url": url,
"authorization_token": authorization_token,
},
None,
)
Loading