diff --git a/pulumi/Pulumi.production.yaml b/pulumi/Pulumi.production.yaml index 27f09fe..64a8044 100644 --- a/pulumi/Pulumi.production.yaml +++ b/pulumi/Pulumi.production.yaml @@ -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 diff --git a/pulumi/Pulumi.staging.yaml b/pulumi/Pulumi.staging.yaml index 7ef97e9..9486402 100644 --- a/pulumi/Pulumi.staging.yaml +++ b/pulumi/Pulumi.staging.yaml @@ -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 diff --git a/pulumi/__main__.py b/pulumi/__main__.py index 73d714c..6555445 100644 --- a/pulumi/__main__.py +++ b/pulumi/__main__.py @@ -1,6 +1,7 @@ """An AWS Python Pulumi program""" import pulumi +import pulumi_cloudflare from pulumi_aws import ( apigatewayv2, cloudwatch, @@ -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", @@ -146,7 +148,7 @@ "ENVIRONMENT": pulumi.get_stack(), }, }, - memory_size=1024, + memory_size=768, timeout=50, ) @@ -164,7 +166,7 @@ "ENVIRONMENT": pulumi.get_stack(), }, }, - memory_size=1024, + memory_size=768, timeout=10, ) @@ -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", @@ -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", ) diff --git a/pulumi/poetry.lock b/pulumi/poetry.lock index 6c27d71..3f6b125 100644 --- a/pulumi/poetry.lock +++ b/pulumi/poetry.lock @@ -371,6 +371,22 @@ parver = ">=0.2.1" pulumi = ">=3.0.0,<4.0.0" semver = ">=2.8.1" +[[package]] +name = "pulumi-cloudflare" +version = "5.39.0" +description = "A Pulumi package for creating and managing Cloudflare cloud resources." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pulumi_cloudflare-5.39.0-py3-none-any.whl", hash = "sha256:9921f0a3fb442e6a446ccd76fc0ba532b9f327e768e17391c71e59d4981c5242"}, + {file = "pulumi_cloudflare-5.39.0.tar.gz", hash = "sha256:e8088415418ddb0d6cf21fa7ba42b920414b58e6a4224751e6b40c56754b5edd"}, +] + +[package.dependencies] +parver = ">=0.2.1" +pulumi = ">=3.0.0,<4.0.0" +semver = ">=2.8.1" + [[package]] name = "pulumi-command" version = "1.0.1" @@ -698,4 +714,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "c431a4af3ab7eefbd7440be15dd2cf119e6a66d40c3658cd1ce2f412e550d80d" +content-hash = "8e116b20677ce8a01ab503412bf2f67a3027c784d30de6a2c67955df7f31af17" diff --git a/pulumi/pyproject.toml b/pulumi/pyproject.toml index d99ba1b..1a090d7 100644 --- a/pulumi/pyproject.toml +++ b/pulumi/pyproject.toml @@ -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" diff --git a/pulumi/telegram_provider.py b/pulumi/telegram_provider.py index 66b7b7d..cf8ac83 100644 --- a/pulumi/telegram_provider.py +++ b/pulumi/telegram_provider.py @@ -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): @@ -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, )