Publish Alpine musl distroless Postfix as the sole latest image
Build and Push Docker Image / build (push) Successful in 1m25s

This commit is contained in:
Ketan Patel committed 2026-10-03 00:17:09 -04:00
1 parent 2a9a7c6baa
commit 7ead00647b
12 files changed
+776 -1274

No files matched your search

+9 -14
View File
@@ -1,7 +1,7 @@
.PHONY: help build up down restart logs shell test clean status queue config
.PHONY: help build up down restart logs test clean status queue config
# Change to parent directory for docker-compose commands
COMPOSE=cd .. && docker-compose
COMPOSE=cd .. && docker compose
# Default target
help:
@@ -22,7 +22,6 @@ help:
@echo " make health - Check container health"
@echo ""
@echo "Maintenance:"
@echo " make shell - Open shell in container"
@echo " make config - Show Postfix configuration"
@echo " make test - Send test email (auto-detects container)"
@echo " Usage: make test TO=user@example.com [FROM=sender@domain.com]"
@@ -68,10 +67,6 @@ logs:
logs-tail:
@$(COMPOSE) logs --tail=100
# Open shell in container
shell:
@$(COMPOSE) exec postfix bash
# Show container and Postfix status
status:
@echo "Container Status:"
@@ -80,7 +75,7 @@ status:
@echo ""
@echo "Postfix Status:"
@echo "==============="
@$(COMPOSE) exec postfix postfix status || echo "Container not running"
@$(COMPOSE) exec postfix /usr/local/bin/postfix-entrypoint --healthcheck || echo "Container not running"
# View mail queue
queue:
@@ -103,7 +98,7 @@ config:
# Check Postfix configuration
check:
@echo "Checking Postfix configuration..."
@$(COMPOSE) exec postfix postfix check
@$(COMPOSE) exec postfix /usr/local/bin/postfix-entrypoint --check
@$(COMPOSE) exec postfix postconf -c /etc/postfix/
@echo "Configuration check complete"
@@ -117,14 +112,14 @@ test:
echo " make test TO=user@example.com FROM=noreply@mydomain.com"; \
echo " make test TO=user@example.com FROM=sender@domain.com SUBJECT='My Test'"; \
echo ""; \
echo "Note: Container is auto-detected from docker-postfix-postfix image"; \
echo "Note: Container defaults to postfix-relay; override with CONTAINER=name"; \
echo " FROM defaults to noreply@<DOMAIN> if not specified"; \
exit 1; \
fi
@if [ -n "$(CONTAINER)" ]; then \
./test-email.sh $(CONTAINER) $(TO) $(FROM) $(SUBJECT); \
./test-email.sh "$(CONTAINER)" "$(TO)" "$(FROM)" "$(SUBJECT)"; \
else \
./test-email.sh $(TO) $(FROM) $(SUBJECT); \
./test-email.sh "$(TO)" "$(FROM)" "$(SUBJECT)"; \
fi
# Validate environment file
@@ -159,7 +154,7 @@ clean-all: clean
queue-detail:
@echo "Detailed Queue Information:"
@echo "=========================="
@$(COMPOSE) exec postfix sh -c "postqueue -p | tail -n 1"
@$(COMPOSE) exec -T postfix postqueue -p | tail -n 1
# Delete all queued messages
queue-delete:
@@ -176,7 +171,7 @@ queue-delete:
debug:
@echo "Restarting with DEBUG mode enabled..."
@$(COMPOSE) down
@cd .. && DEBUG=yes docker-compose up
@cd .. && DEBUG=yes docker compose up
# Show real-time statistics
stats:
+29 -191
View File
@@ -1,200 +1,38 @@
#!/bin/bash
#!/usr/bin/env bash
# Host-side helper; the container only executes Postfix's native sendmail.
set -euo pipefail
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Parse arguments flexibly
# Supports: recipient [from] [subject]
# Or: container recipient [from] [subject]
CONTAINER_NAME=""
RECIPIENT=""
FROM_EMAIL=""
SUBJECT="Test Email from Postfix Relay"
# Check if first argument looks like an email
if [[ "$1" =~ @.*\. ]]; then
# First arg is email - it's the recipient
RECIPIENT="${1}"
FROM_EMAIL="${2}"
SUBJECT="${3:-Test Email from Postfix Relay}"
if [[ $# -lt 1 ]]; then
echo "Usage: $0 [container] recipient [sender] [subject]" >&2
exit 1
fi
if [[ $1 == *@* ]]; then
container=${CONTAINER:-postfix-relay}
else
# First arg is not email - assume it's container name
if [ -n "$1" ]; then
CONTAINER_NAME="${1}"
RECIPIENT="${2}"
FROM_EMAIL="${3}"
SUBJECT="${4:-Test Email from Postfix Relay}"
fi
container=$1
shift
fi
# Function to print colored messages
print_error() {
echo -e "${RED}ERROR: $1${NC}" >&2
}
print_success() {
echo -e "${GREEN}SUCCESS: $1${NC}"
}
print_info() {
echo -e "${YELLOW}INFO: $1${NC}"
}
# Show usage if recipient is not provided
if [ -z "$RECIPIENT" ]; then
echo "Usage: $0 RECIPIENT_EMAIL [FROM_EMAIL] [SUBJECT]"
echo " or: $0 CONTAINER_NAME RECIPIENT_EMAIL [FROM_EMAIL] [SUBJECT]"
echo ""
echo "Arguments:"
echo " RECIPIENT_EMAIL - Email address to send test email to (required)"
echo " FROM_EMAIL - From email address (optional, default: noreply@<domain>)"
echo " SUBJECT - Email subject (default: 'Test Email from Postfix Relay')"
echo " CONTAINER_NAME - Docker container name (optional, auto-detected)"
echo ""
echo "Examples:"
echo " $0 user@example.com"
echo " $0 user@example.com sender@mydomain.com"
echo " $0 user@example.com sender@mydomain.com 'My Test Email'"
echo " $0 my-postfix user@example.com sender@mydomain.com"
echo ""
recipient=${1:?Recipient required}
from=${2:-}
subject=${3:-Test Email from Postfix Relay}
if [[ ! $recipient =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+$ ]]; then
echo "Invalid recipient" >&2
exit 1
fi
# Auto-detect container name if not provided
if [ -z "$CONTAINER_NAME" ]; then
print_info "Auto-detecting container..."
# Try to find container by image name pattern
CONTAINER_NAME=$(docker ps --filter "ancestor=docker-postfix-postfix" --format '{{.Names}}' | head -n 1)
# Fallback: try common naming patterns
if [ -z "$CONTAINER_NAME" ]; then
CONTAINER_NAME=$(docker ps --filter "name=postfix" --format '{{.Names}}' | head -n 1)
fi
if [ -z "$CONTAINER_NAME" ]; then
print_error "Could not auto-detect container. Please specify container name."
echo ""
echo "Available containers:"
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
echo ""
echo "Usage: $0 CONTAINER_NAME RECIPIENT_EMAIL"
exit 1
fi
print_success "Detected container: ${CONTAINER_NAME}"
if [[ -z $from ]]; then
domain=$(docker exec "$container" postconf -h mydomain)
from="noreply@$domain"
fi
# Check if container exists
if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
print_error "Container '${CONTAINER_NAME}' is not running"
echo ""
echo "Available containers:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
if [[ ! $from =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+$ || $subject == *$'\r'* || $subject == *$'\n'* ]]; then
echo "Invalid sender or subject" >&2
exit 1
fi
docker exec -i "$container" sendmail -i -f "$from" "$recipient" <<MAIL
From: $from
To: $recipient
Subject: $subject
print_info "Container: ${CONTAINER_NAME}"
# Get the port mapping for the container
PORT_MAPPING=$(docker port "${CONTAINER_NAME}" 25 2>/dev/null | head -n 1)
if [ -z "$PORT_MAPPING" ]; then
print_error "Could not determine port mapping for container '${CONTAINER_NAME}'"
exit 1
fi
# Extract host and port from mapping (format: 0.0.0.0:25 or 127.0.0.1:25)
HOST=$(echo "$PORT_MAPPING" | cut -d: -f1)
PORT=$(echo "$PORT_MAPPING" | cut -d: -f2)
# Convert 0.0.0.0 to 127.0.0.1 for local communication
if [ "$HOST" = "0.0.0.0" ]; then
HOST="127.0.0.1"
fi
# Set default FROM email if not provided
if [ -z "$FROM_EMAIL" ]; then
# Try to get domain from container environment or use hostname
DOMAIN=$(docker exec "$CONTAINER_NAME" printenv DOMAIN 2>/dev/null || echo "relay.local")
FROM_EMAIL="noreply@${DOMAIN}"
fi
print_info "SMTP Server: ${HOST}:${PORT}"
print_info "From: ${FROM_EMAIL}"
print_info "Recipient: ${RECIPIENT}"
print_info "Subject: ${SUBJECT}"
echo ""
# Generate email body with timestamp
TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S %Z")
EMAIL_BODY="This is a test email sent from the Postfix SMTP relay.
Container: ${CONTAINER_NAME}
Sent at: ${TIMESTAMP}
SMTP Server: ${HOST}:${PORT}
If you received this email, your Postfix relay is working correctly!
---
Automated test email"
print_info "Sending test email..."
echo ""
# Method 1: Try using sendmail directly (most reliable for Postfix)
print_info "Attempting to send via sendmail..."
SENDMAIL_OUTPUT=$(docker exec -i "$CONTAINER_NAME" sendmail -v -f "$FROM_EMAIL" "$RECIPIENT" 2>&1 << EOFMAIL
From: ${FROM_EMAIL}
To: ${RECIPIENT}
Subject: ${SUBJECT}
${EMAIL_BODY}
EOFMAIL
)
SENDMAIL_EXIT=$?
if [ $SENDMAIL_EXIT -eq 0 ]; then
print_success "Email queued successfully via sendmail"
echo "$SENDMAIL_OUTPUT" | grep -i "queued\|sent" || true
else
print_error "Sendmail method failed"
echo "Output: $SENDMAIL_OUTPUT"
echo ""
# Method 2: Try using mailx/mail command
print_info "Trying alternative method using mail command..."
MAIL_OUTPUT=$(docker exec "$CONTAINER_NAME" sh -c "
echo '${EMAIL_BODY}' | mail -v -s '${SUBJECT}' -r '${FROM_EMAIL}' '${RECIPIENT}' 2>&1
")
MAIL_EXIT=$?
if [ $MAIL_EXIT -eq 0 ]; then
print_success "Email queued successfully via mail command"
else
print_error "Mail command failed"
echo "Output: $MAIL_OUTPUT"
echo ""
print_info "Check container logs for more details:"
echo " docker logs $CONTAINER_NAME"
exit 1
fi
fi
echo ""
print_info "Checking mail queue..."
docker exec "$CONTAINER_NAME" postqueue -p 2>/dev/null || echo "Queue check not available"
echo ""
print_success "Test complete! Check the recipient mailbox for the test email."
echo ""
print_info "You can monitor the logs with:"
echo " docker logs -f ${CONTAINER_NAME}"
echo ""
Test email from the Postfix relay, submitted at $(date -u).
MAIL
echo "Message submitted; inspect delivery status with docker logs $container."
docker exec "$container" postqueue -p
+19 -353
View File
@@ -1,353 +1,19 @@
#!/bin/bash
# Postfix SMTP Relay Test Script
# This script validates the Postfix relay configuration and sends test emails
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Functions
print_header() {
echo -e "\n${BLUE}========================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}========================================${NC}\n"
}
print_success() {
echo -e "${GREEN}✓ $1${NC}"
}
print_error() {
echo -e "${RED}✗ $1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠ $1${NC}"
}
print_info() {
echo -e "${BLUE}ℹ $1${NC}"
}
# Check if .env file exists
check_env() {
print_header "Checking Environment Configuration"
if [ ! -f .env ]; then
print_error ".env file not found"
print_info "Copy env.sample to .env and configure it"
exit 1
fi
print_success ".env file found"
# Source the .env file
source .env
# Check required variables
local required_vars=("SMTP_SERVER" "SMTP_PORT" "SMTP_USERNAME" "SMTP_PASSWORD" "DOMAIN")
local missing_vars=0
for var in "${required_vars[@]}"; do
if [ -z "${!var}" ]; then
print_error "$var is not set"
missing_vars=$((missing_vars + 1))
else
print_success "$var is set"
fi
done
if [ $missing_vars -gt 0 ]; then
print_error "$missing_vars required variable(s) missing"
exit 1
fi
print_info "Provider: $SMTP_SERVER:$SMTP_PORT"
print_info "Domain: $DOMAIN"
}
# Check if Docker is running
check_docker() {
print_header "Checking Docker"
if ! command -v docker &> /dev/null; then
print_error "Docker is not installed"
exit 1
fi
print_success "Docker is installed"
if ! docker info &> /dev/null; then
print_error "Docker daemon is not running"
exit 1
fi
print_success "Docker daemon is running"
if ! command -v docker-compose &> /dev/null; then
print_error "Docker Compose is not installed"
exit 1
fi
print_success "Docker Compose is installed"
}
# Check container status
check_container() {
print_header "Checking Container Status"
if ! docker ps --filter name=postfix-relay --format '{{.Names}}' | grep -q postfix-relay; then
print_warning "Container is not running"
print_info "Starting container..."
docker-compose up -d
sleep 10
fi
if docker ps --filter name=postfix-relay --format '{{.Names}}' | grep -q postfix-relay; then
print_success "Container is running"
else
print_error "Failed to start container"
exit 1
fi
# Check health status
health_status=$(docker inspect postfix-relay --format='{{.State.Health.Status}}' 2>/dev/null || echo "unknown")
if [ "$health_status" = "healthy" ]; then
print_success "Container is healthy"
elif [ "$health_status" = "starting" ]; then
print_warning "Container is starting..."
print_info "Waiting for health check..."
sleep 10
else
print_warning "Health status: $health_status"
fi
}
# Check Postfix status
check_postfix() {
print_header "Checking Postfix Status"
if docker exec postfix-relay postfix status &> /dev/null; then
print_success "Postfix is running"
else
print_error "Postfix is not running"
print_info "Checking logs..."
docker logs --tail 50 postfix-relay
exit 1
fi
}
# Check network connectivity
check_network() {
print_header "Checking Network Connectivity"
source .env
# Check if nc (netcat) is available in container
if docker exec postfix-relay which nc &> /dev/null; then
if docker exec postfix-relay nc -zv ${SMTP_SERVER} ${SMTP_PORT} 2>&1 | grep -q succeeded; then
print_success "Can connect to ${SMTP_SERVER}:${SMTP_PORT}"
else
print_error "Cannot connect to ${SMTP_SERVER}:${SMTP_PORT}"
print_info "Check firewall and network settings"
fi
else
print_warning "netcat not available, skipping connectivity test"
fi
}
# Check Postfix configuration
check_config() {
print_header "Validating Postfix Configuration"
if docker exec postfix-relay postfix check 2>&1 | grep -q error; then
print_error "Postfix configuration has errors"
docker exec postfix-relay postfix check
exit 1
else
print_success "Postfix configuration is valid"
fi
# Show key configuration
print_info "Key Configuration Values:"
docker exec postfix-relay postconf -n | grep -E "^(myhostname|mydomain|relayhost|mynetworks)" | while read line; do
echo " $line"
done
}
# Check mail queue
check_queue() {
print_header "Checking Mail Queue"
queue_output=$(docker exec postfix-relay postqueue -p)
if echo "$queue_output" | grep -q "Mail queue is empty"; then
print_success "Mail queue is empty"
else
print_warning "Mail queue has messages:"
echo "$queue_output"
fi
}
# Send test email
send_test_email() {
print_header "Sending Test Email"
if [ -z "$1" ]; then
read -p "Enter recipient email address: " recipient
else
recipient="$1"
fi
if [ -z "$recipient" ]; then
print_error "No recipient provided"
return 1
fi
# Validate email format
if ! echo "$recipient" | grep -qE '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'; then
print_error "Invalid email address format"
return 1
fi
print_info "Sending test email to: $recipient"
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
hostname=$(docker exec postfix-relay hostname)
test_message="Test email from Postfix SMTP Relay
Timestamp: $timestamp
Hostname: $hostname
Container: postfix-relay
If you receive this email, your Postfix relay is working correctly!
"
if docker exec postfix-relay sh -c "echo '$test_message' | mail -s 'Postfix Relay Test - $timestamp' $recipient" 2>&1; then
print_success "Test email sent to $recipient"
print_info "Check the mail queue and logs:"
echo ""
docker exec postfix-relay postqueue -p
else
print_error "Failed to send test email"
return 1
fi
}
# Show logs
show_logs() {
print_header "Recent Logs"
docker logs --tail 50 postfix-relay
}
# Full test suite
run_full_test() {
print_header "Running Full Test Suite"
check_env
check_docker
check_container
check_postfix
check_network
check_config
check_queue
print_header "Test Summary"
print_success "All checks passed!"
print_info "Container is ready to relay emails"
echo ""
read -p "Do you want to send a test email? (y/n): " send_test
if [ "$send_test" = "y" ] || [ "$send_test" = "Y" ]; then
send_test_email
fi
}
# Main menu
show_menu() {
echo ""
echo -e "${BLUE}Postfix SMTP Relay Test Menu${NC}"
echo "================================"
echo "1) Run full test suite"
echo "2) Check environment"
echo "3) Check container status"
echo "4) Check Postfix configuration"
echo "5) Check mail queue"
echo "6) Send test email"
echo "7) Show logs"
echo "8) Exit"
echo ""
read -p "Select option: " option
case $option in
1) run_full_test ;;
2) check_env ;;
3) check_container && check_postfix ;;
4) check_config ;;
5) check_queue ;;
6) send_test_email ;;
7) show_logs ;;
8) exit 0 ;;
*) print_error "Invalid option"; show_menu ;;
esac
}
# Parse command line arguments
if [ $# -eq 0 ]; then
# No arguments, show menu
show_menu
else
case "$1" in
--full|-f)
run_full_test
;;
--email|-e)
if [ -n "$2" ]; then
send_test_email "$2"
else
send_test_email
fi
;;
--check|-c)
check_env
check_docker
check_container
check_postfix
check_config
;;
--logs|-l)
show_logs
;;
--queue|-q)
check_queue
;;
--help|-h)
echo "Postfix SMTP Relay Test Script"
echo ""
echo "Usage: $0 [option] [arguments]"
echo ""
echo "Options:"
echo " -f, --full Run full test suite"
echo " -c, --check Run configuration checks"
echo " -e, --email [address] Send test email"
echo " -q, --queue Check mail queue"
echo " -l, --logs Show recent logs"
echo " -h, --help Show this help message"
echo ""
echo "Examples:"
echo " $0 # Interactive menu"
echo " $0 --full # Run all tests"
echo " $0 --email user@example.com # Send test email"
echo " $0 --check # Check configuration"
echo ""
;;
*)
print_error "Unknown option: $1"
echo "Use --help for usage information"
exit 1
;;
esac
fi
#!/usr/bin/env bash
# Host-side checks for the distroless runtime. Email is sent only with --email.
set -euo pipefail
container=${CONTAINER:-postfix-relay}
case ${1:---check} in
--check|-c|--full|-f)
docker exec "$container" /usr/local/bin/postfix-entrypoint --healthcheck
docker exec "$container" /usr/local/bin/postfix-entrypoint --check
docker exec "$container" postqueue -p
;;
--email|-e)
shift
exec "$(dirname "$0")/test-email.sh" "$container" "$@"
;;
--logs|-l) docker logs --tail 50 "$container" ;;
--queue|-q) docker exec "$container" postqueue -p ;;
--help|-h) echo "Usage: $0 [--check|--full|--email recipient [sender] [subject]|--logs|--queue]" ;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
esac