Skip to content

Commit

Permalink
entry_checker: add sender validation lambda
Browse files Browse the repository at this point in the history
  • Loading branch information
dezeroku committed Dec 21, 2024
1 parent 011bda6 commit c014e2b
Show file tree
Hide file tree
Showing 20 changed files with 384 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
name: Run tests
name: Run tests (Go)

on:
push:
Expand Down
32 changes: 32 additions & 0 deletions .github/workflows/tests-python.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
name: Run tests (Python)
on:
push:
workflow_dispatch:
pull_request:
jobs:
pytest:
strategy:
fail-fast: false
matrix:
cfg:
- component: "lambda/entry_checker"
runs-on: ubuntu-latest
env:
python_version: "1.12"

steps:
- uses: actions/checkout@v4
- name: Install poetry
run: pipx install poetry
- uses: actions/setup-python@v5
with:
python-version: ${{ env.python_version }}
cache: "poetry"
cache-dependency-path: |
${{ matrix.cfg.component }}/pyproject.toml
${{ matrix.cfg.component }}/poetry.lock
- name: Run pytest
run: |
cd ${{ matrix.cfg.component }}
poetry run pytest
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ This repository consists of two parts:

Terraform module is provided in `terraform` directory.
It can be used to create necessary components on AWS's end.
It's required to set up [SES email receiving](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-setting-up.html) first and
It's required to set up [SES email receiving](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-setting-up.html) first,
configuring this part is outside the scope of this project.

The minimum to get started is:
Expand All @@ -55,6 +55,14 @@ The minimum to get started is:
recipients = ["[email protected]", "[email protected]"]
```

It's also recommended (but optional) to set a regex describing allowed senders.
Emails sent from different addresses will simply be ignored.
It can be set as below:

```
senders = ".*@example.com"
```

2. run `terraform init`
3. run `terraform apply`
4. plug in the values obtained via `terraform output` as env variables in the following section
Expand Down
5 changes: 5 additions & 0 deletions lambda/entry_checker/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
venv
.venv
_pycache_

payload
Empty file added lambda/entry_checker/README.md
Empty file.
Empty file.
45 changes: 45 additions & 0 deletions lambda/entry_checker/entry_checker/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import logging
import os
from entry_checker.validate import validate_sender

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)


def main(event, context):
allowed_senders_regex = os.environ["ALLOWED_SENDERS_REGEX"]

ses_notification = event["Records"].pop()["ses"]
receipt = ses_notification["receipt"]

logger.debug(receipt)

stop_email = False

if receipt["spfVerdict"]["status"] == "FAIL":
logger.debug("SPF FAIL detected, cutting off")
stop_email = True

elif receipt["dkimVerdict"]["status"] == "FAIL":
logger.debug("DKIM FAIL detected, cutting off")
stop_email = True

elif receipt["spamVerdict"]["status"] == "FAIL":
logger.debug("SPAM FAIL detected, cutting off")
stop_email = True

elif receipt["virusVerdict"]["status"] == "FAIL":
logger.debug("VIRUS FAIL detected, cutting off")
stop_email = True

else:
# Such validation is not perfect, but paired with the above conditions
# that rely on Amazon checks, it's definitely better than nothing.
sender = ses_notification["mail"]["commonHeaders"]["from"].pop()

if not validate_sender(allowed_senders_regex, sender):
logger.debug("sender validation FAIL detected, cutting off")
stop_email = True

if stop_email:
return {"disposition": "STOP_RULE_SET"}
11 changes: 11 additions & 0 deletions lambda/entry_checker/entry_checker/validate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import re


def validate_sender(allowed_senders_regex: str, sender: str):
# Get rid of the "quotation marks"
sender = sender.replace("<", "").replace(">", "")

if re.search(allowed_senders_regex, sender):
return True

return False
74 changes: 74 additions & 0 deletions lambda/entry_checker/poetry.lock

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

16 changes: 16 additions & 0 deletions lambda/entry_checker/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[tool.poetry]
name = "entry-checker"
version = "0.1.0"
description = ""
authors = ["dezeroku <[email protected]>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.12"

[tool.poetry.group.dev.dependencies]
pytest = "^8.3.4"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Empty file.
17 changes: 17 additions & 0 deletions lambda/entry_checker/tests/validate_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from entry_checker.validate import validate_sender

import pytest

allowed_senders_regex = ".*@example.com"


@pytest.mark.parametrize("sender", ["[email protected]", "<[email protected]>"])
def test_validate_sender_success(sender):
assert validate_sender(allowed_senders_regex, sender)


@pytest.mark.parametrize(
"sender", ["[email protected]", "<[email protected]>"]
)
def test_validate_sender_fail(sender):
assert not validate_sender(allowed_senders_regex, sender)
1 change: 1 addition & 0 deletions terraform/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.terraform*
terraform*
!.terraform.lock.hcl
!terraform.tfvars.development
51 changes: 35 additions & 16 deletions terraform/.terraform.lock.hcl

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

4 changes: 2 additions & 2 deletions terraform/bucket.tf
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ resource "aws_s3_bucket" "bucket" {

resource "aws_s3_bucket_policy" "allow_ses_access" {
bucket = aws_s3_bucket.bucket.id
policy = data.aws_iam_policy_document.allow_ses_access.json
policy = data.aws_iam_policy_document.bucket_allow_ses_access.json
}

data "aws_iam_policy_document" "allow_ses_access" {
data "aws_iam_policy_document" "bucket_allow_ses_access" {
statement {
principals {
type = "Service"
Expand Down
Loading

0 comments on commit c014e2b

Please sign in to comment.