Skip to content
Draft
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
36 changes: 28 additions & 8 deletions services/config-scripts/gen-postfix-conf.sh
100644 β†’ 100755
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@ MAIL_HOSTNAME=${MAIL_HOSTNAME:-mail.$MAIL_DOMAIN}

mkdir -p ${CONFIGS_PATH}

# Note: Using SQLite database instead of virtual files
# SQLite configuration files are in silver-config/postfix/sqlite-*.cf
# Note: Using socketmap service for all virtual maps
# - Virtual domains: validates which domains we accept mail for
# - Virtual users: validates which user mailboxes exist
# - Virtual aliases: resolves email aliases to destination addresses

echo -e "SMTP configuration will use SQLite database"
echo " - Database: /app/data/databses/shared.db"
echo " - SQLite configs: $CONFIGS_PATH/sqlite-*.cf"
echo -e "SMTP configuration will use:"
echo " - Socketmap for domains: socketmap-server:9100"
echo " - Socketmap for users: socketmap-server:9100"
echo " - Socketmap for aliases: socketmap-server:9100"

# --- Generate main.cf content ---
cat >"${CONFIGS_PATH}/main.cf" <<EOF
Expand Down Expand Up @@ -92,6 +95,16 @@ smtp_tls_loglevel = 1


smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination

# Recipient restrictions - ENFORCE USER VALIDATION
# This ensures virtual_mailbox_maps is actually queried for recipient validation
smtpd_recipient_restrictions =
permit_mynetworks
permit_sasl_authenticated
reject_unauth_destination
reject_unlisted_recipient
reject_non_fqdn_recipient

myhostname = ${MAIL_HOSTNAME}
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
Expand All @@ -112,9 +125,16 @@ smtpd_sasl_path = inet:raven:12345
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
broken_sasl_auth_clients = yes
virtual_mailbox_domains = sqlite:/etc/postfix/sqlite-virtual-domains.cf
virtual_mailbox_maps = sqlite:/etc/postfix/sqlite-virtual-users.cf
virtual_alias_maps = sqlite:/etc/postfix/sqlite-virtual-aliases.cf

# Virtual mailbox configuration - socketmap based
virtual_mailbox_domains = socketmap:inet:socketmap-server:9100:virtual-domains
virtual_mailbox_maps = socketmap:inet:socketmap-server:9100:user-exists
virtual_alias_maps = socketmap:inet:socketmap-server:9100:virtual-aliases

# Set mailbox base to tell Postfix we're managing mailboxes
# This ensures reject_unlisted_recipient properly checks virtual_mailbox_maps
virtual_mailbox_base = /var/mail/virtual

virtual_transport = lmtp:raven:24
milter_protocol = 6
milter_default_action = accept
Expand Down
40 changes: 36 additions & 4 deletions services/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,16 @@ services:
networks:
- mail-network
depends_on:
- opendkim-server
- rspamd-server
- certbot-server
- raven-server
opendkim-server:
condition: service_started
rspamd-server:
condition: service_started
certbot-server:
condition: service_started
raven-server:
condition: service_started
socketmap-server:
condition: service_healthy

raven-server:
image: ghcr.io/lsflk/raven:latest
Expand All @@ -43,6 +49,32 @@ services:
restart: unless-stopped
user: "0:0"

socketmap-server:
build:
context: ./socketmap
dockerfile: Dockerfile
container_name: socketmap-server
expose:
- "9100"
networks:
- mail-network
restart: unless-stopped
environment:
- SOCKETMAP_HOST=0.0.0.0
- SOCKETMAP_PORT=9100
healthcheck:
test: ["CMD", "nc", "-z", "localhost", "9100"]
interval: 10s
timeout: 5s
retries: 3
start_period: 5s
deploy:
resources:
limits:
memory: 128M
reservations:
memory: 64M

Comment on lines +52 to +77
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The socketmap-server service is missing a healthcheck. Adding one would improve the reliability of your stack, especially since smtp-server depends on it. Without a healthcheck, depends_on only waits for the container to start, not for the service to be ready. You could then change the dependency to condition: service_healthy.

Note that the healthcheck command needs to send a valid netstring request, which can be tricky with sh -c. You may want to consider adding a simpler healthcheck mechanism to the Go service itself.

opendkim-server:
image: ghcr.io/lsflk/silver-dkim:main
container_name: opendkim-server
Expand Down
29 changes: 29 additions & 0 deletions services/socketmap/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Binaries
socketmap
*.exe
*.dll
*.so
*.dylib

# Test binary
*.test

# Output of the go coverage tool
*.out

# Dependency directories
vendor/

# Go workspace file
go.work

# IDE
.idea/
.vscode/
*.swp
*.swo
*~

# OS
.DS_Store
Thumbs.db
40 changes: 40 additions & 0 deletions services/socketmap/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Socketmap Service Dockerfile
FROM golang:1.21-alpine AS builder

WORKDIR /build

# Copy go module files
COPY go.mod ./
RUN go mod download

# Copy source code
COPY main.go ./

# Build the binary
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o socketmap .

# Final stage
FROM alpine:3.19

# Install ca-certificates and netcat for healthcheck
RUN apk --no-cache add ca-certificates netcat-openbsd

# Create a non-root user and group
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app

# Copy binary from builder
COPY --from=builder /build/socketmap .

# Set ownership for the app directory and binary
RUN chown -R appuser:appgroup /app

# Switch to the non-root user
USER appuser

# Expose socketmap port
EXPOSE 9100

# Run the service
CMD ["./socketmap"]
Comment on lines +39 to +40
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better security, the container should not run as the root user. You should create a dedicated non-root user, change file ownership, and switch to that user before running the application.

# Create a non-root user and group
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# Set ownership for the app directory and binary
RUN chown -R appuser:appgroup /app

# Switch to the non-root user
USER appuser

# Run the service
CMD ["./socketmap"]

Loading
Loading