Skip to content
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
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
.git
dkim.key
Dockerfile*
test/
Makefile
18 changes: 15 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@ RUN set -x \
&& go install -tags nosystemd,nodocker \
;

# Postfix SMTP Relay

# Debian Trixie
FROM debian:13
FROM debian:13 AS base

EXPOSE 25 587 2525

Expand Down Expand Up @@ -61,5 +59,19 @@ RUN set -x \
&& chmod 0644 /etc/postfix/header_checks \
;

# Development image
FROM base AS development

# Install packages
RUN set -x \
&& export DEBIAN_FRONTEND=noninteractive \
&& apt-get update \
&& apt-get install -y --no-install-recommends bats \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
;

# Postfix SMTP Relay
FROM base AS build
ENTRYPOINT ["/entry.sh"]
CMD ["/usr/bin/s6-svscan", "/etc/s6"]
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ NAME := postfix
TAG := latest
IMAGE_NAME := panubo/$(NAME)

.PHONY: help bash run run-* build push clean _ci_test
.PHONY: help bash run run-* build push clean test _ci_test

help:
@printf "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)\n"
Expand Down Expand Up @@ -55,7 +55,11 @@ push: ## Pushes the docker image to hub.docker.com
clean: ## Remove built image
docker rmi $(IMAGE_NAME):$(TAG)

_ci_test:
test: ## Build a test image and run bats tests in docker
docker build --target development -t $(IMAGE_NAME):test-dev .
docker run --rm -v $(shell pwd):/app -w /app $(IMAGE_NAME):test-dev bats test/

_ci_test: test
true

dkim.key:
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ echo -e "To: Bob <bob@example.com>\nFrom: Bill <bill@example.com>\nSubject: Test

See the `Makefile` for make targets.

To run the BATS tests, use the `make test` command. This will build a test Docker image and execute the tests within it.


## Releases

For production usage, please use a versioned release rather than the floating 'latest' tag.
Expand Down
6 changes: 3 additions & 3 deletions etc/opendkim.conf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

set -e

OUTPUT='/etc/opendkim.conf'
: ${DKIM_CONF:='/etc/opendkim.conf'}

# exit if config already exists
[ -f "${OUTPUT}" ] && exit 0
[ -f "${DKIM_CONF}" ] && exit 0

# defaults
: ${DKIM_KEYFILE:='/etc/opendkim/dkim.key'}
Expand Down Expand Up @@ -40,7 +40,7 @@ echo "dkim >> Setting DKIM_DOMAINS to $DKIM_DOMAINS"
echo "dkim >> Setting DKIM_SELECTOR to $DKIM_SELECTOR"

# Render the dkim config
cat > ${OUTPUT} <<EOF
cat > "${DKIM_CONF}" <<EOF
# This is a basic configuration that can easily be adapted to suit a standard
# installation. For more advanced options, see opendkim.conf(5) and/or
# /usr/share/doc/opendkim/examples/opendkim.conf.sample.
Expand Down
2 changes: 1 addition & 1 deletion etc/s6/postfix/run
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ rm -f /var/spool/postfix/pid/*

# check postfix is happy (also will fix some things)
echo "postfix >> Checking Postfix Configuration"
postfix check
postfix check || { echo "postfix >> Error: check failed, dumping config"; cat /etc/postfix/main.cf; exit 1; }

echo "postfix >> Starting postfix"
exec /usr/sbin/postfix start-fg
67 changes: 67 additions & 0 deletions test/opendkim.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env bats

# This setup function runs before each test.
setup() {
# Create a temporary directory for our test files.
BATS_TMPDIR=$(mktemp -d -t bats-XXXXXXXX)
export BATS_TMPDIR

# Mock the s6-svscanctl command so the script doesn't fail if it's not installed.
# We create a fake executable that does nothing and returns success.
ln -s /usr/bin/true "${BATS_TMPDIR}/s6-svscanctl"
export PATH="${BATS_TMPDIR}:${PATH}"
}

# This teardown function runs after each test to clean up.
teardown() {
rm -rf "${BATS_TMPDIR}"
}

@test "should generate config with default values" {
# Define test-specific variables
local output_file="${BATS_TMPDIR}/opendkim.conf"
local dkim_keyfile="${BATS_TMPDIR}/dkim.key"
touch "${dkim_keyfile}" # Create the dummy key file

# Run the script in a subshell with a clean environment and execution tracing (-x)
(
export MAILNAME="example.com"
export MYNETWORKS="127.0.0.1"
export DKIM_CONF="${output_file}"
export DKIM_KEYFILE="${dkim_keyfile}"
bash -x etc/opendkim.conf.sh
)

# Check that the generated file contains the expected lines.
run grep "Domain example.com" "${output_file}"
[ "$status" -eq 0 ] # Succeeds if grep finds the line

run grep "Selector mail" "${output_file}"
[ "$status" -eq 0 ]

run grep "InternalHosts 127.0.0.1" "${output_file}"
[ "$status" -eq 0 ]
}

@test "should use DKIM_DOMAINS and DKIM_SELECTOR from environment" {
# Define test-specific variables
local output_file="${BATS_TMPDIR}/opendkim.conf"
local dkim_keyfile="${BATS_TMPDIR}/dkim.key"
touch "${dkim_keyfile}"

# Run the script in a subshell with a clean environment and execution tracing (-x)
(
export DKIM_DOMAINS="custom.net,another.org"
export DKIM_SELECTOR="dkim2024"
export DKIM_CONF="${output_file}"
export DKIM_KEYFILE="${dkim_keyfile}"
bash -x etc/opendkim.conf.sh
)

# Check for the custom values.
run grep "Domain custom.net,another.org" "${output_file}"
[ "$status" -eq 0 ]

run grep "Selector dkim2024" "${output_file}"
[ "$status" -eq 0 ]
}
91 changes: 91 additions & 0 deletions test/postfix.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/usr/bin/env bats

# Test for /etc/s6/postfix/run

setup() {
BATS_TMPDIR=$(mktemp -d -t bats-XXXXXXXX)
export BATS_TMPDIR

# Backup original files that the script might modify
[ -f /etc/postfix/main.cf ] && cp /etc/postfix/main.cf "${BATS_TMPDIR}/main.cf.bak"
[ -f /etc/sasldb2 ] && cp /etc/sasldb2 "${BATS_TMPDIR}/sasldb2.bak"
cp /usr/lib/postfix/configure-instance.sh "${BATS_TMPDIR}/configure-instance.sh.bak"

# Create a version of the run script without the final 'exec' to prevent blocking
sed 's/exec \/usr\/sbin\/postfix start-fg//' /etc/s6/postfix/run > "${BATS_TMPDIR}/run"
chmod +x "${BATS_TMPDIR}/run"

# The script might create these files, so ensure the directory exists
mkdir -p /etc/postfix/sasl
}

teardown() {
# Restore original files
[ -f "${BATS_TMPDIR}/main.cf.bak" ] && mv "${BATS_TMPDIR}/main.cf.bak" /etc/postfix/main.cf
[ -f "${BATS_TMPDIR}/sasldb2.bak" ] && mv "${BATS_TMPDIR}/sasldb2.bak" /etc/sasldb2
mv "${BATS_TMPDIR}/configure-instance.sh.bak" /usr/lib/postfix/configure-instance.sh
rm -rf "${BATS_TMPDIR}"
}

@test "should configure main.cf with custom values from environment variables" {
# Run the modified script in a subshell with custom environment variables
(
# Set env vars to test against
export MAILNAME="test.example.com"
export MYNETWORKS="10.0.0.0/8 127.0.0.0/8"
export SIZELIMIT="20480000"
export RELAYHOST="[mail.example.com]:587"
export POSTFIX_ADD_MISSING_HEADERS="yes"
export INET_PROTOCOLS="ipv4"
export DISABLE_VRFY_COMMAND="no"
export USE_TLS="no" # Disable TLS to avoid slow cert generation

# Execute the setup script
bash -x "${BATS_TMPDIR}/run"
)

# Use postconf to verify the settings in /etc/postfix/main.cf
run postconf -h myhostname
[ "$status" -eq 0 ]
[ "$output" = "test.example.com" ]

run postconf -h mynetworks
[ "$status" -eq 0 ]
[ "$output" = "10.0.0.0/8 127.0.0.0/8" ]

run postconf -h message_size_limit
[ "$status" -eq 0 ]
[ "$output" = "20480000" ]

run postconf -h relayhost
[ "$status" -eq 0 ]
[ "$output" = "[mail.example.com]:587" ]

run postconf -h always_add_missing_headers
[ "$status" -eq 0 ]
[ "$output" = "yes" ]

run postconf -h inet_protocols
[ "$status" -eq 0 ]
[ "$output" = "ipv4" ]

run postconf -h disable_vrfy_command
[ "$status" -eq 0 ]
[ "$output" = "no" ]
}

@test "should enable DKIM milter when USE_DKIM is 'yes'" {
(
export MAILNAME="test.example.com"
export USE_DKIM="yes"
bash -x "${BATS_TMPDIR}/run"
)

run postconf -h smtpd_milters
[ "$status" -eq 0 ]
[ "$output" = "inet:localhost:8891" ]

run postconf -h non_smtpd_milters
[ "$status" -eq 0 ]
[ "$output" = "inet:localhost:8891" ]
}