help run mox with docker

in the Dockerfile, allow running on privileged ports and expose those ports.

add a docker-compose.yml with instructions for the quickstart.

fix running imaptest somewhat. after a short while it will hit the rate limiter.

in quickstart, recognize we are running under docker, and print slightly
different commands to set permissions, and skip generating the systemd service
file. als fix cleaning up the right paths during failure in quickstart.

for issue #3
This commit is contained in:
Mechiel Lukkien 2023-02-24 14:16:51 +01:00
parent 210fd34702
commit b1dcd73ebe
No known key found for this signature in database
8 changed files with 104 additions and 34 deletions

View file

@ -1,11 +1,33 @@
FROM golang:1-alpine AS build FROM golang:1-alpine AS build
WORKDIR /build WORKDIR /build
RUN apk add make
COPY . . COPY . .
env GOPROXY=off RUN GOPROXY=off CGO_ENABLED=0 go build -trimpath
RUN make build
FROM alpine:3.17 # Using latest may break at some point, but will hopefully be convenient most of the time.
FROM alpine:latest
WORKDIR /mox WORKDIR /mox
COPY --from=build /build/mox /mox/mox COPY --from=build /build/mox /bin/mox
CMD ["/mox/mox", "serve"]
RUN apk add --no-cache libcap-utils
# Allow binding to privileged ports, <1024.
RUN setcap 'cap_net_bind_service=+ep' /bin/mox
# SMTP for incoming message delivery.
EXPOSE 25/tcp
# SMTP/submission with TLS.
EXPOSE 465/tcp
# SMTP/submission without initial TLS.
EXPOSE 587/tcp
# HTTP for internal account and admin pages.
EXPOSE 80/tcp
# HTTPS for ACME (Let's Encrypt), MTA-STS and autoconfig.
EXPOSE 443/tcp
# IMAP with TLS.
EXPOSE 993/tcp
# IMAP without initial TLS.
EXPOSE 143/tcp
# Prometheus metrics.
EXPOSE 8010/tcp
CMD ["/bin/mox", "serve"]

View file

@ -1,6 +1,6 @@
FROM alpine:3.17 FROM alpine:latest
RUN apk update && apk add wget build-base RUN apk --no-cache add build-base
WORKDIR /src WORKDIR /src
RUN wget http://dovecot.org/nightly/dovecot-latest.tar.gz && tar -zxvf dovecot-latest.tar.gz && cd dovecot-0.0.0-* && ./configure && make install && cd .. RUN wget http://dovecot.org/nightly/dovecot-latest.tar.gz && tar -zxvf dovecot-latest.tar.gz && cd dovecot-0.0.0-* && ./configure && make install && cd ..
RUN wget http://dovecot.org/nightly/imaptest/imaptest-latest.tar.gz && tar -zxvf imaptest-latest.tar.gz && cd dovecot-0.0-imaptest-0.0.0-* && ./configure --with-dovecot=$(ls -d ../dovecot-0.0.0-*) && make install RUN wget http://dovecot.org/nightly/imaptest/imaptest-latest.tar.gz && tar -zxvf imaptest-latest.tar.gz && cd dovecot-0.0-imaptest-0.0.0-* && ./configure --with-dovecot=$(ls -d ../dovecot-0.0.0-*) && make install

11
Dockerfile.moximaptest Normal file
View file

@ -0,0 +1,11 @@
FROM golang:1-alpine AS build
WORKDIR /build
COPY . .
RUN GOPROXY=off CGO_ENABLED=0 go build -trimpath
# Using latest may break at some point, but will hopefully be convenient most of the time.
FROM alpine:latest
WORKDIR /mox
COPY --from=build /build/mox /bin/mox
CMD ["/bin/mox", "serve"]

View file

@ -50,7 +50,6 @@ integration-start:
# run from within "make integration-start" # run from within "make integration-start"
integration-test: integration-test:
CGO_ENABLED=0 go test -tags integration CGO_ENABLED=0 go test -tags integration
go tool cover -html=cover.out -o cover.html
imaptest-build: imaptest-build:
-MOX_UID=$$(id -u) MOX_GID=$$(id -g) docker-compose -f docker-compose-imaptest.yml build --no-cache mox -MOX_UID=$$(id -u) MOX_GID=$$(id -g) docker-compose -f docker-compose-imaptest.yml build --no-cache mox

View file

@ -85,6 +85,9 @@ Verify you have a working mox binary:
Note: Mox only compiles/works on unix systems, not on Plan 9 or Windows. Note: Mox only compiles/works on unix systems, not on Plan 9 or Windows.
You can also run mox with docker image "moxmail/mox" on hub.docker.com, with
tags like "latest", "0.0.1", etc. See docker-compose.yml in this repository.
# Quickstart # Quickstart

View file

@ -1,7 +1,9 @@
version: '3.7' version: '3.7'
services: services:
mox: mox:
build: . build:
context: .
dockerfile: Dockerfile.moximaptest
user: ${MOX_UID}:${MOX_GID} user: ${MOX_UID}:${MOX_GID}
volumes: volumes:
- ./testdata/imaptest/data:/mox/data - ./testdata/imaptest/data:/mox/data
@ -9,7 +11,8 @@ services:
- ./testdata/imaptest/domains.conf:/mox/domains.conf - ./testdata/imaptest/domains.conf:/mox/domains.conf
- ./testdata/imaptest/imaptest.mbox:/mox/imaptest.mbox - ./testdata/imaptest/imaptest.mbox:/mox/imaptest.mbox
working_dir: /mox working_dir: /mox
command: sh -c 'echo testtest | ./mox setaccountpassword mjl@mox.example && ./mox serve' tty: true # For job control
command: sh -c 'export MOXCONF=mox.conf; set -m; mox serve & sleep 1; echo testtest | mox setaccountpassword mjl@mox.example; fg'
healthcheck: healthcheck:
test: netstat -nlt | grep ':1143 ' test: netstat -nlt | grep ':1143 '
interval: 1s interval: 1s

30
docker-compose.yml Normal file
View file

@ -0,0 +1,30 @@
# Before launching mox, run the quickstart to create config files:
#
# MOX_UID=0 MOX_GID=0 docker-compose run mox mox quickstart you@yourdomain.example
#
# After following the instructions, start mox as the newly created mox user:
#
# MOX_UID=$(id -u mox) MOX_GID=$(id -g mox) docker-compose up
version: '3.7'
services:
mox:
# Replace latest with the version you want to run.
image: moxmail/mox:latest
user: ${MOX_UID}:${MOX_GID}
environment:
- MOX_DOCKER=... # Quickstart won't try to write systemd service file.
# Mox needs host networking because it needs access to the IPs of the
# machine, and the IPs of incoming connections for spam filtering.
network_mode: 'host'
command: sh -c "umask 007 && exec mox serve"
volumes:
- ./config:/mox/config
- ./data:/mox/data
working_dir: /mox
restart: on-failure
healthcheck:
test: netstat -nlt | grep ':25 '
interval: 1s
timeout: 1s
retries: 10

View file

@ -8,7 +8,6 @@ import (
"log" "log"
"net" "net"
"os" "os"
"os/user"
"path/filepath" "path/filepath"
"runtime" "runtime"
"sort" "sort"
@ -345,7 +344,8 @@ This likely means one of two things:
dc := config.Dynamic{} dc := config.Dynamic{}
sc := config.Static{DataDir: "../data"} sc := config.Static{DataDir: "../data"}
os.MkdirAll(sc.DataDir, 0770) dataDir := "data" // ../data is relative to config/
os.MkdirAll(dataDir, 0770)
sc.LogLevel = "info" sc.LogLevel = "info"
sc.Hostname = hostname.Name() sc.Hostname = hostname.Name()
sc.ACME = map[string]config.ACME{ sc.ACME = map[string]config.ACME{
@ -491,7 +491,7 @@ This likely means one of two things:
if err != nil { if err != nil {
fatalf("open account: %s", err) fatalf("open account: %s", err)
} }
cleanupPaths = append(cleanupPaths, sc.DataDir, filepath.Join(sc.DataDir, "accounts"), filepath.Join(sc.DataDir, "accounts", username), filepath.Join(sc.DataDir, "accounts", username, "index.db")) cleanupPaths = append(cleanupPaths, dataDir, filepath.Join(dataDir, "accounts"), filepath.Join(dataDir, "accounts", username), filepath.Join(dataDir, "accounts", username, "index.db"))
password := pwgen() password := pwgen()
if err := acc.SetPassword(password); err != nil { if err := acc.SetPassword(password); err != nil {
@ -534,34 +534,36 @@ and permissions.
`) `)
userName := "root" if os.Getenv("MOX_DOCKER") == "" {
groupName := "root"
if u, err := user.Current(); err != nil {
log.Printf("get current user: %v", err)
} else {
userName = u.Username
if g, err := user.LookupGroupId(u.Gid); err != nil {
log.Printf("get current group: %v", err)
} else {
groupName = g.Name
}
}
fmt.Printf(`Assuming the mox binary is in the current directory, and you will run mox under fmt.Printf(`Assuming the mox binary is in the current directory, and you will run mox under
user name "mox", and the admin user is the current user, the following command user name "mox", and the admin user is the current user, the following commands
sets the correct permissions: set the correct permissions:
sudo useradd -d $PWD mox sudo useradd -d $PWD mox
sudo chown %s:mox . mox sudo chown $(id -nu):mox . mox
sudo chown -R mox:%s config data sudo chown -R mox:$(id -ng) config data
sudo chmod 751 . sudo chmod 751 .
sudo chmod 750 mox sudo chmod 750 mox
sudo chmod -R u=rwX,g=rwX,o= config data sudo chmod -R u=rwX,g=rwX,o= config data
sudo chmod g+s $(find . -type d) sudo chmod g+s $(find . -type d)
`, userName, groupName) `)
} else {
fmt.Printf(`Assuming you will run mox under user name "mox", and the admin user is the
current user, the following commands set the correct permissions:
// For now, we only give service config instructions for linux. sudo useradd -d $PWD mox
if runtime.GOOS == "linux" { sudo chown $(id -nu):mox .
sudo chown -R mox:$(id -ng) config data
sudo chmod 751 .
sudo chmod -R u=rwX,g=rwX,o= config data
sudo chmod g+s $(find . -type d)
`)
}
// For now, we only give service config instructions for linux when not running in docker.
if runtime.GOOS == "linux" && os.Getenv("MOX_DOCKER") == "" {
pwd, err := os.Getwd() pwd, err := os.Getwd()
if err != nil { if err != nil {
log.Printf("current working directory: %v", err) log.Printf("current working directory: %v", err)