mirror of
https://github.com/docker-mailserver/docker-mailserver.git
synced 2024-01-19 02:48:50 +00:00
Rootless Podman security update (#2393)
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com> Co-authored-by: Casper <casperklein@users.noreply.github.com>
This commit is contained in:
parent
ede2b2394a
commit
4c3af32692
|
@ -95,6 +95,8 @@ chmod a+x ./setup.sh
|
||||||
- [_only_ basic `VAR=VAL`](https://docs.docker.com/compose/env-file/) is supported (**do not** quote your values!)
|
- [_only_ basic `VAR=VAL`](https://docs.docker.com/compose/env-file/) is supported (**do not** quote your values!)
|
||||||
- variable substitution is **not** supported (e.g. :no_entry_sign: `OVERRIDE_HOSTNAME=$HOSTNAME.$DOMAINNAME` :no_entry_sign:)
|
- variable substitution is **not** supported (e.g. :no_entry_sign: `OVERRIDE_HOSTNAME=$HOSTNAME.$DOMAINNAME` :no_entry_sign:)
|
||||||
|
|
||||||
|
**Note:** If you're using podman, make sure to read the related [documentation](https://docker-mailserver.github.io/docker-mailserver/edge/config/advanced/podman/)
|
||||||
|
|
||||||
### Get up and running
|
### Get up and running
|
||||||
|
|
||||||
#### First Things First
|
#### First Things First
|
||||||
|
|
|
@ -14,6 +14,11 @@ Podman is a daemonless container engine for developing, managing, and running OC
|
||||||
|
|
||||||
This guide was tested with Fedora 34 using `systemd` and `firewalld`. Moreover, it requires Podman version >= 3.2. You may be able to substitute `dnf` - Fedora's package maneger - with others such as `apt`.
|
This guide was tested with Fedora 34 using `systemd` and `firewalld`. Moreover, it requires Podman version >= 3.2. You may be able to substitute `dnf` - Fedora's package maneger - with others such as `apt`.
|
||||||
|
|
||||||
|
!!! warning "About Security"
|
||||||
|
|
||||||
|
Running podman in rootless mode requires additional modifications in order to keep your mailserver secure.
|
||||||
|
Make sure to read the related documentation.
|
||||||
|
|
||||||
## Installation in Rootfull Mode
|
## Installation in Rootfull Mode
|
||||||
|
|
||||||
While using Podman, you can just manage docker-mailserver as what you did with Docker. Your best friend `setup.sh` includes the minimum code in order to support Podman since it's 100% compatible with the Docker CLI.
|
While using Podman, you can just manage docker-mailserver as what you did with Docker. Your best friend `setup.sh` includes the minimum code in order to support Podman since it's 100% compatible with the Docker CLI.
|
||||||
|
@ -56,6 +61,7 @@ Running rootless containers is one of Podman's major features. But due to some r
|
||||||
|
|
||||||
- a rootless container is running in a user namespace so you cannot bind ports lower than 1024
|
- a rootless container is running in a user namespace so you cannot bind ports lower than 1024
|
||||||
- a rootless container's systemd file can only be placed in folder under `~/.config`
|
- a rootless container's systemd file can only be placed in folder under `~/.config`
|
||||||
|
- a rootless container can result in an open relay, make sure to read the [security section](#security-in-rootless-mode).
|
||||||
|
|
||||||
Also notice that Podman's rootless mode is not about running as a non-root user inside the container, but about the mapping of (normal, non-root) host users to root inside the container.
|
Also notice that Podman's rootless mode is not about running as a non-root user inside the container, but about the mapping of (normal, non-root) host users to root inside the container.
|
||||||
|
|
||||||
|
@ -90,6 +96,40 @@ docker-compose up -d mailserver
|
||||||
docker-compose ps
|
docker-compose ps
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Security in Rootless Mode
|
||||||
|
|
||||||
|
In rootless mode, podman resolves all incoming IPs as localhost, which results in an open gateway in the default configuration. There are two workarounds to fix this problem, both of which have their own drawbacks.
|
||||||
|
|
||||||
|
#### Enforce authentication from localhost
|
||||||
|
|
||||||
|
The `PERMIT_DOCKER` variable in the `mailserver.env` file allows to specify trusted networks that do not need to authenticate. If the variable is left empty, only requests from localhost and the container IP are allowed, but in the case of rootless podman any IP will be resolved as localhost. Setting `PERMIT_DOCKER=none` enforces authentication also from localhost, which prevents sending unauthenticated emails.
|
||||||
|
|
||||||
|
#### Use the slip4netns network driver
|
||||||
|
|
||||||
|
The second workaround is slightly more complicated because the `docker-compose.yml` has to be modified.
|
||||||
|
As shown in the [fail2ban section](https://docker-mailserver.github.io/docker-mailserver/edge/config/security/fail2ban/#podman-with-slirp4netns-port-driver) the `slirp4netns` network driver has to be enabled.
|
||||||
|
This network driver enables podman to correctly resolve IP addresses but it is not compatible with
|
||||||
|
user defined networks which might be a problem depending on your setup.
|
||||||
|
|
||||||
|
[Rootless Podman][rootless::podman] requires adding the value `slirp4netns:port_handler=slirp4netns` to the `--network` CLI option, or `network_mode` setting in your `docker-compose.yml`.
|
||||||
|
|
||||||
|
You must also add the ENV `NETWORK_INTERFACE=tap0`, because Podman uses a [hard-coded interface name][rootless::podman::interface] for `slirp4netns`.
|
||||||
|
|
||||||
|
!!! example
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
mailserver:
|
||||||
|
network_mode: "slirp4netns:port_handler=slirp4netns"
|
||||||
|
environment:
|
||||||
|
- NETWORK_INTERFACE=tap0
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
`podman-compose` is not compatible with configuration.
|
||||||
|
|
||||||
### Self-start in Rootless Mode
|
### Self-start in Rootless Mode
|
||||||
|
|
||||||
Generate a systemd file with the Podman CLI.
|
Generate a systemd file with the Podman CLI.
|
||||||
|
|
|
@ -46,9 +46,12 @@ UPDATE_CHECK_INTERVAL=1d
|
||||||
# **WARNING**: Adding the docker network's gateway to the list of trusted hosts, e.g. using the `network` or
|
# **WARNING**: Adding the docker network's gateway to the list of trusted hosts, e.g. using the `network` or
|
||||||
# `connected-networks` option, can create an open relay
|
# `connected-networks` option, can create an open relay
|
||||||
# https://github.com/docker-mailserver/docker-mailserver/issues/1405#issuecomment-590106498
|
# https://github.com/docker-mailserver/docker-mailserver/issues/1405#issuecomment-590106498
|
||||||
# empty => localhost only
|
# The same can happen for rootless podman. To prevent this, set the value to "none" or configure slirp4netns
|
||||||
# host => Add docker host (ipv4 only)
|
# https://github.com/docker-mailserver/docker-mailserver/issues/2377
|
||||||
# network => Add all docker containers (ipv4 only)
|
# <empty> => container ip only
|
||||||
|
# none => Explicitly force authentication
|
||||||
|
# host => Add docker container network (ipv4 only)
|
||||||
|
# network => Add all docker container networks (ipv4 only)
|
||||||
# connected-networks => Add all connected docker networks (ipv4 only)
|
# connected-networks => Add all connected docker networks (ipv4 only)
|
||||||
PERMIT_DOCKER=
|
PERMIT_DOCKER=
|
||||||
|
|
||||||
|
|
|
@ -1154,6 +1154,11 @@ function _setup_docker_permit
|
||||||
done < <(ip -o -4 addr show type veth | grep -E -o '[0-9\.]+/[0-9]+')
|
done < <(ip -o -4 addr show type veth | grep -E -o '[0-9\.]+/[0-9]+')
|
||||||
|
|
||||||
case "${PERMIT_DOCKER}" in
|
case "${PERMIT_DOCKER}" in
|
||||||
|
"none" )
|
||||||
|
_notify 'inf' "Clearing Postfix's 'mynetworks'"
|
||||||
|
postconf -e "mynetworks ="
|
||||||
|
;;
|
||||||
|
|
||||||
"host" )
|
"host" )
|
||||||
_notify 'inf' "Adding ${CONTAINER_NETWORK}/16 to my networks"
|
_notify 'inf' "Adding ${CONTAINER_NETWORK}/16 to my networks"
|
||||||
postconf -e "$(postconf | grep '^mynetworks =') ${CONTAINER_NETWORK}/16"
|
postconf -e "$(postconf | grep '^mynetworks =') ${CONTAINER_NETWORK}/16"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
load 'test_helper/common'
|
load 'test_helper/common'
|
||||||
|
|
||||||
NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME=non-default-docker-mail-network
|
NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME=non-default-docker-mail-network
|
||||||
setup() {
|
setup_file() {
|
||||||
docker network create --driver bridge "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}"
|
docker network create --driver bridge "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}"
|
||||||
docker network create --driver bridge "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}2"
|
docker network create --driver bridge "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}2"
|
||||||
# use two networks (default ("bridge") and our custom network) to recreate problematic test case where PERMIT_DOCKER=host would not help
|
# use two networks (default ("bridge") and our custom network) to recreate problematic test case where PERMIT_DOCKER=host would not help
|
||||||
|
@ -33,12 +33,26 @@ setup() {
|
||||||
|
|
||||||
# wait until postfix is up
|
# wait until postfix is up
|
||||||
wait_for_smtp_port_in_container mail_smtponly_second_network
|
wait_for_smtp_port_in_container mail_smtponly_second_network
|
||||||
|
|
||||||
|
# create another container that enforces authentication even on local connections
|
||||||
|
docker run -d --name mail_smtponly_force_authentication \
|
||||||
|
-v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \
|
||||||
|
-v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \
|
||||||
|
-e SMTP_ONLY=1 \
|
||||||
|
-e PERMIT_DOCKER=none \
|
||||||
|
-e DMS_DEBUG=0 \
|
||||||
|
-e OVERRIDE_HOSTNAME=mail.my-domain.com \
|
||||||
|
-t "${NAME}"
|
||||||
|
|
||||||
|
# wait until postfix is up
|
||||||
|
wait_for_smtp_port_in_container mail_smtponly_force_authentication
|
||||||
}
|
}
|
||||||
|
|
||||||
teardown() {
|
teardown_file() {
|
||||||
docker logs mail_smtponly_second_network
|
docker logs mail_smtponly_second_network
|
||||||
docker rm -f mail_smtponly_second_network \
|
docker rm -f mail_smtponly_second_network \
|
||||||
mail_smtponly_second_network_sender
|
mail_smtponly_second_network_sender \
|
||||||
|
mail_smtponly_force_authentication
|
||||||
docker network rm "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}" "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}2"
|
docker network rm "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}" "${NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME}2"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +70,17 @@ teardown() {
|
||||||
# we should be able to send from the other container on the second network!
|
# we should be able to send from the other container on the second network!
|
||||||
run docker exec mail_smtponly_second_network_sender /bin/sh -c "nc mail_smtponly_second_network 25 < /tmp/docker-mailserver-test/email-templates/smtp-only.txt"
|
run docker exec mail_smtponly_second_network_sender /bin/sh -c "nc mail_smtponly_second_network 25 < /tmp/docker-mailserver-test/email-templates/smtp-only.txt"
|
||||||
assert_output --partial "250 2.0.0 Ok: queued as "
|
assert_output --partial "250 2.0.0 Ok: queued as "
|
||||||
|
|
||||||
repeat_until_success_or_timeout 60 run docker exec mail_smtponly_second_network /bin/sh -c 'grep -cE "to=<user2\@external.tld>.*status\=sent" /var/log/mail/mail.log'
|
repeat_until_success_or_timeout 60 run docker exec mail_smtponly_second_network /bin/sh -c 'grep -cE "to=<user2\@external.tld>.*status\=sent" /var/log/mail/mail.log'
|
||||||
[[ ${status} -ge 0 ]]
|
[[ ${status} -ge 0 ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "checking PERMIT_DOCKER: none" {
|
||||||
|
run docker exec mail_smtponly_force_authentication /bin/sh -c "postconf -e smtp_host_lookup=no"
|
||||||
|
assert_success
|
||||||
|
run docker exec mail_smtponly_force_authentication /bin/sh -c "/etc/init.d/postfix reload"
|
||||||
|
assert_success
|
||||||
|
# the mailserver should require authentication and a protocol error should occur when using TLS
|
||||||
|
run docker exec mail_smtponly_force_authentication /bin/sh -c "nc localhost 25 < /tmp/docker-mailserver-test/email-templates/smtp-only.txt"
|
||||||
|
assert_output --partial "550 5.5.1 Protocol error"
|
||||||
|
[[ ${status} -ge 0 ]]
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue