Skip to content

Commit

Permalink
Add the option to log to rsyslog as plain or JSON and restructure tests
Browse files Browse the repository at this point in the history
- It's now possible to choose the logging type - either 'plain' or
  'json'
- The code is ready to support multiple integration tests (with
  different configurations)
- `OPENDKIM_` and `POSTFIX_` variables are handled properly and recorded
  in the corresponding files. (This had a downfall that `bash` now needs
  to be installed, so we can probably simplify some of the shell
  scripts.)
  • Loading branch information
bokysan committed Jul 1, 2020
1 parent 9b1902c commit ff2d080
Show file tree
Hide file tree
Showing 17 changed files with 211 additions and 17 deletions.
7 changes: 4 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ ENV MESSAGE_SIZE_LIMIT=
ENV INBOUND_DEBUGGING=
# DKIM domain selector. If not set, the default (mail) will be used
ENV DKIM_SELECTOR=
# Logformat. Defaults to "plain". Can be either "plain" or "json".
ENV LOG_FORMAT=

# Install supervisor, postfix
# Install postfix first to get the first account (101)
Expand All @@ -37,13 +39,12 @@ RUN true && \
apk add --no-cache --upgrade cyrus-sasl cyrus-sasl-plain cyrus-sasl-login && \
apk add --no-cache postfix && \
apk add --no-cache opendkim && \
apk add --no-cache ca-certificates tzdata supervisor rsyslog && \
apk add --no-cache --upgrade musl musl-utils && \
apk add --no-cache --upgrade ca-certificates tzdata supervisor rsyslog musl musl-utils bash && \
(rm "/tmp/"* 2>/dev/null || true) && (rm -rf /var/cache/apk/* 2>/dev/null || true)

# Set up configuration
COPY /configs/supervisord.conf /etc/supervisord.conf
COPY /configs/rsyslog.conf /etc/rsyslog.conf
COPY /configs/rsyslog*.conf /etc/
COPY /configs/opendkim.conf /etc/opendkim/opendkim.conf
COPY /configs/smtp_header_checks /etc/postfix/smtp_header_checks
COPY /scripts/*.sh /
Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ This means:
## Extending the image
### Using custom init scripts
If you need to add custom configuration to postfix or have it do something outside of the scope of this configuration, simply
add your scripts to `/docker-init.db/`: All files with the `.sh` extension will be executed automatically at the end of the
startup script.
Expand All @@ -243,7 +245,37 @@ For example, your script could contain something like this:
postconf -e "address_verify_negative_cache=yes"
```
### Overriding specific postfix settings
Any Postfix [configuration option](http://www.postfix.org/postconf.5.html) can be overriden using `POSTFIX_<name>` environment variables, e.g.
`POSTFIX_allow_mail_to_commands=alias,forward,include`. Specifying no content (empty variable) will remove that variable from postfix config.
Any OpenDKIM [configuration option](http://opendkim.org/opendkim.conf.5.html) can be overriden using `OPENDKIM_<name>` environment variables, e.g.
`OPENDKIM_RequireSafeKeys=yes`. Specifying no content (empty variable) will remove that variable from OpenDKIM config.
## Log format
The image will by default output logs in human-readable (`plain`) format. If you are deploying the image to Kubernetes, it might be worth chaging
the output format to `json` as it's more easily parsable by tools such as [Prometheus](https://prometheus.io/).
To change the log format, set the (unsuprisingly named) variable `LOG_FORMAT=json`.
## Security
Postfix will run the master proces as `root`, because that's how it's designed. Subprocesses will run under the `postfix` account
which will use `UID:GID` of `100:101`. `opendkim` will run under account `102:103`.
## Similar projects
There are may other project offering similar functionality. The aim of this project, however, is:
- to make it as simple as possible to run the relay, without going too much into postfix configuration details
- to make as small image as possible (hence basing on Alpine linux)
- to make the image and the corresponding code testable
The other projects are, in completely random order:
- [wader/postfix-relay](https://github.com/wader/postfix-relay)
- [catatnight/postfix](https://github.com/catatnight/docker-postfix)
- [juanluisbaptiste/docker-postfix](https://github.com/juanluisbaptiste/docker-postfix)
- [docker-mail-relay](https://github.com/alterrebe/docker-mail-relay)
19 changes: 15 additions & 4 deletions configs/rsyslog.conf
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ $Umask 0022

template (name="devicelog" type="string" string="/dev/stdout")

template(name="json_syslog"
type="list") {
template(name="json" type="list") {
constant(value="{")
constant(value="\"@timestamp\":\"") property(name="timereported" dateFormat="rfc3339")
constant(value="\",\"type\":\"syslog_json")
Expand All @@ -26,17 +25,29 @@ template(name="json_syslog"
constant(value="\",\"facility\":\"") property(name="syslogfacility")
constant(value="\",\"severity_label\":\"") property(name="syslogseverity-text")
constant(value="\",\"facility_label\":\"") property(name="syslogfacility-text")
constant(value="\",\"message\":\"") property(name="rawmsg" format="json")
constant(value="\",\"message\":\"") property(name="msg" format="json")
constant(value="\",\"end_msg\":\"")
constant(value="\"}\n")
}

template(name="plain" type="list") {
property(name="timereported" dateFormat="rfc3339")
constant(value=" ")
property(name="syslogseverity-text" caseconversion="upper" fixedwidth="on" position.to="7")
constant(value=" ")
property(name="syslogtag")
property(name="msg" spifno1stsp="on")
property(name="msg" droplastlf="on")
constant(value="\n")
}

if $syslogseverity <= '6' then {
# matching logs will be saved
action(type="omfile" DynaFile="devicelog" template="json_syslog" DirCreateMode="0755" FileCreateMode="0644")
action(type="omfile" DynaFile="devicelog" template="<log-format>" DirCreateMode="0755" FileCreateMode="0644")
# enable below to stop processing further this log
stop
}

include(file="/etc/rsyslog.d/*.conf" mode="optional")

stop
13 changes: 12 additions & 1 deletion integration-test.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
#!/bin/sh
set -e
cd integration-tests
docker-compose up --build --abort-on-container-exit --exit-code-from tests
for i in `find -maxdepth 1 -type d`; do
i="$(basename "$i")"
if [ "$i" == "tester" ] || [ "$i" == "." ] || [ "$i" == ".." ]; then
continue
fi
(
echo "$i"
cd "$i"
docker-compose up --build --abort-on-container-exit --exit-code-from tests
)
done
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ services:
hostname: "postfix"
image: "boky/postfix"
build:
context: ..
context: ../..
restart: always
healthcheck:
test: [ "CMD", "sh", "-c", "netstat -an | fgrep 587 | fgrep -q LISTEN" ]
Expand All @@ -21,7 +21,7 @@ services:
image: "boky/postfix-integration-test"
restart: "no"
volumes:
- "..:/code"
- "../tester:/code"
build:
context: .
command: "/code/integration-tests/"
context: ../tester
command: "/"
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ if [ -z "$TO" ]; then
fi

# Wait for postfix to startup
echo "Waiting for startup..."
wait-for-service -q tcp://postfix_test_587:587

SMTP_DATA="-smtp postfix_test_587 -port 587"
Expand Down
57 changes: 56 additions & 1 deletion scripts/common-run.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/sh
#!/usr/bin/env bash

announce_startup() {
echo -e "******************************"
Expand All @@ -21,6 +21,15 @@ setup_timezone() {
fi
}

rsyslog_log_format() {
local log_format="${LOG_FORMAT}"
if [[ -z "${log_format}" ]]; then
log_format="plain"
fi
echo -e "$info Using ${emphasis}${log_format}${reset} log format for rsyslog."
sed -i -E "s/<log-format>/${log_format}/" /etc/rsyslog.conf
}

reown_folders() {
mkdir -p /var/spool/postfix/ && mkdir -p /var/spool/postfix/pid
chown root: /var/spool/postfix/
Expand Down Expand Up @@ -250,6 +259,52 @@ postfix_setup_dkim() {
fi
}

opendkim_custom_commands() {
local setting
local key
local padded_key
local value
for setting in ${!OPENDKIM_*}; do
key="${setting:9}"
value="${!setting}"
if [ -n "${value}" ]; then
if [ "${#key}" -gt 23 ]; then
padded_key="${key} "
else
padded_key="$(printf %-24s "${key}")"
fi
if cat /etc/opendkim/opendkim.conf | egrep -q "^[[:space:]]*#?[[:space:]]*${key}"; then
echo -e "$info Updating custom OpenDKIM setting: ${emphasis}${key}=${value}${reset}"
sed -i -E "s/^[ \t]*#?[ \t]*${key}[ \t]*.+$/${padded_key}${value}/" /etc/opendkim/opendkim.conf
else
echo -e "$info Adding custom OpenDKIM setting: ${emphasis}${key}=${value}${reset}"
echo "Adding ${padded_key}${value}"
echo "${padded_key}${value}" >> /etc/opendkim/opendkim.conf
fi
else
echo -e "$info Deleting custom OpenDKIM setting: ${emphasis}${key}${reset}"
sed -i -E "/^[ \t]*#?[ \t]*${key}[ \t]*.+$/d" /etc/opendkim/opendkim.conf
fi
done
}

postfix_custom_commands() {
local setting
local key
local value
for setting in ${!POSTFIX_*}; do
key="${setting:8}"
value="${!setting}"
if [ -n "${value}" ]; then
echo -e "$info Applying custom postfix setting: ${emphasis}${key}=${value}${reset}"
postconf -e "${key}=${value}"
else
echo -e "$info Deleting custom postfix setting: ${emphasis}${key}${reset}"
postconf -# "${key}"
fi
done
}

postfix_open_submission_port() {
# Use 587 (submission)
sed -i -r -e 's/^#submission/submission/' /etc/postfix/master.cf
Expand Down
2 changes: 1 addition & 1 deletion scripts/common.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/sh
#!/usr/bin/env bash

reset=""
yellow=""
Expand Down
5 changes: 4 additions & 1 deletion scripts/run.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#!/bin/sh
#!/usr/bin/env bash
set -e

. /common.sh
. /common-run.sh

announce_startup # Print startup banner
setup_timezone # Check if we need to configure the container timezone
rsyslog_log_format # Setup rsyslog output format
reown_folders # Make and reown postfix folders
postfix_disable_utf8 # Disable SMTPUTF8, because libraries (ICU) are missing in alpine
postfix_create_aliases # Update aliases database. It's not used, but postfix complains if the .db file is missing
Expand All @@ -23,6 +24,8 @@ postfix_setup_sender_domains # Configure allowed sender domains
postfix_setup_masquarading # Setup masquaraded domains
postfix_setup_header_checks # Enable SMTP header checks, if defined
postfix_setup_dkim # Configure DKIM, if enabled
postfix_custom_commands # Apply custom postfix settings
opendkim_custom_commands # Apply custom OpenDKIM settings
postfix_open_submission_port # Enable the submission port
execute_post_init_scripts # Execute any scripts found in /docker-init.db/

Expand Down
7 changes: 6 additions & 1 deletion unit-tests/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@

FROM alpine:latest

RUN apk add --no-cache bash bats
RUN true && \
apk add --no-cache bash bats && \
apk add --no-cache --upgrade cyrus-sasl cyrus-sasl-plain cyrus-sasl-login && \
apk add --no-cache postfix && \
apk add --no-cache opendkim && \
(rm "/tmp/"* 2>/dev/null || true) && (rm -rf /var/cache/apk/* 2>/dev/null || true)

WORKDIR /code
ENTRYPOINT ["/usr/bin/bats"]
Expand Down
60 changes: 60 additions & 0 deletions unit-tests/opendkim_custom_commands.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env bats

load /code/scripts/common.sh
load /code/scripts/common-run.sh

setup() {
mkdir -p /etc/opendkim/
cat > /etc/opendkim/opendkim.conf <<-EOF
AutoRestart Yes
AutoRestartRate 10/1h
UMask 002
Syslog Yes
SyslogSuccess Yes
LogWhy No
Canonicalization relaxed/simple
RequireSafeKeys no
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
InternalHosts refile:/etc/opendkim/TrustedHosts
KeyTable refile:/etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
Mode sv
PidFile /var/run/opendkim/opendkim.pid
SignatureAlgorithm rsa-sha256
UserID opendkim:opendkim
Socket inet:8891@localhost
SignHeaders From,Sender,To,CC,Subject,Message-Id,Date,MIME-Version,Content-Type,Reply-To
OversignHeaders From,Sender,To,CC,Subject,Message-Id,Date,MIME-Version,Content-Type,Reply-To
EOF
}

teardown() {
rm -f /etc/opendkim/opendkim.conf
}

@test "Make sure that opendkim_custom_commands changes lines" {
local OPENDKIM_RequireSafeKeys=yes
opendkim_custom_commands
cat /etc/opendkim/opendkim.conf | fgrep -qx "RequireSafeKeys yes"
}

@test "Make sure that opendkim_custom_commands adds lines" {
local OPENDKIM_CaptureUnknownErrors=yes
opendkim_custom_commands
cat /etc/opendkim/opendkim.conf | fgrep -qx "CaptureUnknownErrors yes"
}

@test "Make sure that opendkim_custom_commands removes lines" {
local OPENDKIM_SignHeaders=
opendkim_custom_commands
if cat /etc/opendkim/opendkim.conf | egrep -q "^SignHeaders"; then
return 1
fi
}


17 changes: 17 additions & 0 deletions unit-tests/postfix_custom_commands.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bats

load /code/scripts/common.sh
load /code/scripts/common-run.sh


@test "Make sure that postfix_custom_commands adds lines" {
local POSTFIX_alias_database=hash:/etc/mail/aliases
postfix_custom_commands
cat /etc/postfix/main.cf | fgrep -qx "alias_database = hash:/etc/mail/aliases"
}

@test "Make sure that postfix_custom_commands removes lines" {
local POSTFIX_readme_directory=
postfix_custom_commands
cat /etc/postfix/main.cf | egrep -q "^#readme_directory"
}

0 comments on commit ff2d080

Please sign in to comment.