From 2d40e424e8fcb1d12205802870f0affef90c4ab8 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Fri, 8 Aug 2025 00:56:19 +0200 Subject: [PATCH] Refactor s6 config so each service has its separate run and type files (#1098) --- dockerfiles/Dockerfile.server.allinone | 292 +----------------- .../s6-scripts/admin/dependencies.d/postgres | 0 dockerfiles/s6-scripts/admin/run | 28 ++ dockerfiles/s6-scripts/admin/type | 1 + .../s6-scripts/api/dependencies.d/postgres | 0 dockerfiles/s6-scripts/api/run | 30 ++ dockerfiles/s6-scripts/api/type | 1 + .../s6-scripts/client/dependencies.d/init | 0 dockerfiles/s6-scripts/client/run | 33 ++ dockerfiles/s6-scripts/client/type | 1 + .../s6-scripts/init/dependencies.d/base | 0 dockerfiles/s6-scripts/init/script | 139 +++++++++ dockerfiles/s6-scripts/init/type | 1 + dockerfiles/s6-scripts/init/up | 1 + .../s6-scripts/nginx/dependencies.d/admin | 0 .../s6-scripts/nginx/dependencies.d/api | 0 .../s6-scripts/nginx/dependencies.d/client | 0 dockerfiles/s6-scripts/nginx/run | 34 ++ dockerfiles/s6-scripts/nginx/type | 1 + .../s6-scripts/postgres/dependencies.d/init | 0 dockerfiles/s6-scripts/postgres/run | 10 + dockerfiles/s6-scripts/postgres/type | 1 + .../s6-scripts/smtp/dependencies.d/postgres | 0 dockerfiles/s6-scripts/smtp/run | 27 ++ dockerfiles/s6-scripts/smtp/type | 1 + .../taskrunner/dependencies.d/postgres | 0 dockerfiles/s6-scripts/taskrunner/run | 25 ++ dockerfiles/s6-scripts/taskrunner/type | 1 + 28 files changed, 343 insertions(+), 284 deletions(-) create mode 100644 dockerfiles/s6-scripts/admin/dependencies.d/postgres create mode 100644 dockerfiles/s6-scripts/admin/run create mode 100644 dockerfiles/s6-scripts/admin/type create mode 100644 dockerfiles/s6-scripts/api/dependencies.d/postgres create mode 100644 dockerfiles/s6-scripts/api/run create mode 100644 dockerfiles/s6-scripts/api/type create mode 100644 dockerfiles/s6-scripts/client/dependencies.d/init create mode 100644 dockerfiles/s6-scripts/client/run create mode 100644 dockerfiles/s6-scripts/client/type create mode 100644 dockerfiles/s6-scripts/init/dependencies.d/base create mode 100644 dockerfiles/s6-scripts/init/script create mode 100644 dockerfiles/s6-scripts/init/type create mode 100644 dockerfiles/s6-scripts/init/up create mode 100644 dockerfiles/s6-scripts/nginx/dependencies.d/admin create mode 100644 dockerfiles/s6-scripts/nginx/dependencies.d/api create mode 100644 dockerfiles/s6-scripts/nginx/dependencies.d/client create mode 100644 dockerfiles/s6-scripts/nginx/run create mode 100644 dockerfiles/s6-scripts/nginx/type create mode 100644 dockerfiles/s6-scripts/postgres/dependencies.d/init create mode 100644 dockerfiles/s6-scripts/postgres/run create mode 100644 dockerfiles/s6-scripts/postgres/type create mode 100644 dockerfiles/s6-scripts/smtp/dependencies.d/postgres create mode 100644 dockerfiles/s6-scripts/smtp/run create mode 100644 dockerfiles/s6-scripts/smtp/type create mode 100644 dockerfiles/s6-scripts/taskrunner/dependencies.d/postgres create mode 100644 dockerfiles/s6-scripts/taskrunner/run create mode 100644 dockerfiles/s6-scripts/taskrunner/type diff --git a/dockerfiles/Dockerfile.server.allinone b/dockerfiles/Dockerfile.server.allinone index 3ad8c3b9a..a4fa01a2b 100644 --- a/dockerfiles/Dockerfile.server.allinone +++ b/dockerfiles/Dockerfile.server.allinone @@ -112,296 +112,20 @@ RUN sed -i 's/client:3000/localhost:3000/g' /etc/nginx/nginx.conf && \ sed -i 's/admin:3002/localhost:3002/g' /etc/nginx/nginx.conf # ============================================ -# S6 Service Definitions +# S6 Service Configuration # ============================================ -# Container initialization with secrets management -RUN mkdir -p /etc/s6-overlay/s6-rc.d/init-container && \ - echo '#!/bin/sh' > /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo 'echo "[init-container] Initializing AliasVault single container..." >&2' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo 'echo "[init-container] Creating data directories..." >&2' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo 'mkdir -p /database/postgres /logs/postgres /certificates /secrets' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo '' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo '# Initialize secrets files if they dont exist' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo 'echo "[init-container] Initializing secrets..." >&2' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo '' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo '# Generate random 32 character password for PostgreSQL if not exists' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo 'if [ ! -f /secrets/postgres_password ]; then' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo ' echo "[init-container] Generating PostgreSQL password..." >&2' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo ' openssl rand -base64 32 | tr -d "\\n" > /secrets/postgres_password' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo ' chmod 600 /secrets/postgres_password' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo 'fi' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo '' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo '# Generate random 32 character password for Data Protection Certificate if not exists' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo 'if [ ! -f /secrets/data_protection_cert_pass ]; then' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo ' echo "[init-container] Generating Data Protection Certificate password..." >&2' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo ' openssl rand -base64 32 | tr -d "\\n" > /secrets/data_protection_cert_pass' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo ' chmod 600 /secrets/data_protection_cert_pass' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo 'fi' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo '' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo '# Generate JWT key if not exists' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo 'if [ ! -f /secrets/jwt_key ]; then' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo ' echo "[init-container] Generating JWT key..." >&2' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo ' openssl rand -base64 32 | tr -d "\\n" > /secrets/jwt_key' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo ' chmod 600 /secrets/jwt_key' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo 'fi' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo '' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo 'echo "[init-container] Setting database permissions..." >&2' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo 'chown -R postgres:postgres /database/postgres' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo 'chmod 700 /database/postgres' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo '' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo 'echo "[init-container] Container initialization complete" >&2' >> /etc/s6-overlay/s6-rc.d/init-container/up && \ - chmod +x /etc/s6-overlay/s6-rc.d/init-container/up && \ - echo "oneshot" > /etc/s6-overlay/s6-rc.d/init-container/type +# Copy s6 service definitions +COPY dockerfiles/s6-scripts /etc/s6-overlay/s6-rc.d/ -# PostgreSQL service -RUN mkdir -p /etc/s6-overlay/s6-rc.d/postgres && \ - { echo '#!/bin/sh'; \ - echo ''; \ - echo '# Set PostgreSQL paths'; \ - echo 'export PATH="/usr/lib/postgresql/16/bin:$PATH"'; \ - echo 'export PGDATA="/database/postgres"'; \ - echo ''; \ - echo '# Read PostgreSQL password from file'; \ - echo 'if [ -f /secrets/postgres_password ]; then'; \ - echo ' POSTGRES_PASSWORD=$(cat /secrets/postgres_password)'; \ - echo 'else'; \ - echo ' # Fallback to environment variable or default'; \ - echo ' POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-defaultpassword}"'; \ - echo 'fi'; \ - echo ''; \ - echo '# Initialize PostgreSQL if needed'; \ - echo 'if [ ! -d "$PGDATA/base" ]; then'; \ - echo ' echo "Initializing PostgreSQL database..."'; \ - echo ' mkdir -p "$PGDATA" /logs/postgres'; \ - echo ' chown -R postgres:postgres "$PGDATA" /logs/postgres'; \ - echo ' chmod 700 "$PGDATA"'; \ - echo ' su - postgres -c "/usr/lib/postgresql/16/bin/initdb -D $PGDATA --locale=en_US.UTF-8 --encoding=UTF8"'; \ - echo ' '; \ - echo ' # Configure PostgreSQL'; \ - echo ' echo "host all all 127.0.0.1/32 md5" >> "$PGDATA/pg_hba.conf"'; \ - echo ' echo "listen_addresses = '\''127.0.0.1'\''" >> "$PGDATA/postgresql.conf"'; \ - echo ' '; \ - echo ' # Start PostgreSQL temporarily to create database and user'; \ - echo ' su - postgres -c "/usr/lib/postgresql/16/bin/pg_ctl -D $PGDATA -l /logs/postgres/postgres.log start"'; \ - echo ' sleep 5'; \ - echo ' '; \ - echo ' # Create database and user'; \ - echo ' su - postgres -c "/usr/lib/postgresql/16/bin/psql -c \\"CREATE USER aliasvault WITH PASSWORD '\''$POSTGRES_PASSWORD'\''\\""'; \ - echo ' su - postgres -c "/usr/lib/postgresql/16/bin/psql -c \\"CREATE DATABASE aliasvault OWNER aliasvault;\\""'; \ - echo ' su - postgres -c "/usr/lib/postgresql/16/bin/psql -c \\"GRANT ALL PRIVILEGES ON DATABASE aliasvault TO aliasvault;\\""'; \ - echo ' '; \ - echo ' # Stop PostgreSQL'; \ - echo ' su - postgres -c "/usr/lib/postgresql/16/bin/pg_ctl -D $PGDATA stop"'; \ - echo ' sleep 2'; \ - echo 'fi'; \ - echo ''; \ - echo '# Run PostgreSQL'; \ - echo 'exec s6-setuidgid postgres /usr/lib/postgresql/16/bin/postgres -D "$PGDATA"'; \ - } > /etc/s6-overlay/s6-rc.d/postgres/run && \ - chmod +x /etc/s6-overlay/s6-rc.d/postgres/run && \ - echo "longrun" > /etc/s6-overlay/s6-rc.d/postgres/type && \ - mkdir -p /etc/s6-overlay/s6-rc.d/postgres/dependencies.d && \ - touch /etc/s6-overlay/s6-rc.d/postgres/dependencies.d/init-container +# Make all scripts executable (run for longrun services, up for oneshot services, and script files) +RUN find /etc/s6-overlay/s6-rc.d -type f \( -name "run" -o -name "up" -o -name "script" \) -exec chmod +x {} \; - -# API service -RUN mkdir -p /etc/s6-overlay/s6-rc.d/api && \ - { echo '#!/command/with-contenv bash'; \ - echo 'cd /app/api'; \ - echo '# Read PostgreSQL password from file'; \ - echo 'if [ -f /secrets/postgres_password ]; then'; \ - echo ' POSTGRES_PASSWORD=$(cat /secrets/postgres_password)'; \ - echo 'else'; \ - echo ' POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-defaultpassword}"'; \ - echo 'fi'; \ - echo '# Wait for PostgreSQL to be ready'; \ - echo 'i=1; while [ $i -le 30 ]; do'; \ - echo ' if PGPASSWORD="$POSTGRES_PASSWORD" /usr/lib/postgresql/16/bin/psql -h localhost -U aliasvault -d aliasvault -c "SELECT 1;" >/dev/null 2>&1; then'; \ - echo ' echo "[api] PostgreSQL ready, starting API..." >&2; break'; \ - echo ' fi'; \ - echo ' echo "[api] Waiting for PostgreSQL ($i/30)..." >&2; sleep 2; i=$((i+1))'; \ - echo 'done'; \ - echo 'export ConnectionStrings__AliasServerDbContext="Host=localhost;Database=aliasvault;Username=aliasvault;Password=$POSTGRES_PASSWORD"'; \ - echo 'export ASPNETCORE_URLS="http://0.0.0.0:3001"'; \ - echo 'export ASPNETCORE_PATHBASE="/api"'; \ - echo 'export PRIVATE_EMAIL_DOMAINS="${PRIVATE_EMAIL_DOMAINS:-}"'; \ - echo 'export PUBLIC_REGISTRATION_ENABLED="${PUBLIC_REGISTRATION_ENABLED:-true}"'; \ - echo 'export IP_LOGGING_ENABLED="${IP_LOGGING_ENABLED:-true}"'; \ - echo 'exec dotnet AliasVault.Api.dll'; \ - } > /etc/s6-overlay/s6-rc.d/api/run && \ - chmod +x /etc/s6-overlay/s6-rc.d/api/run && \ - echo "longrun" > /etc/s6-overlay/s6-rc.d/api/type - -# Client service (nginx for WASM app) - using echo approach -RUN mkdir -p /etc/s6-overlay/s6-rc.d/client && \ - { echo '#!/command/with-contenv bash'; \ - echo '# Client service entrypoint'; \ - echo 'DEFAULT_PRIVATE_EMAIL_DOMAINS=""'; \ - echo 'DEFAULT_SUPPORT_EMAIL=""'; \ - echo 'PRIVATE_EMAIL_DOMAINS=${PRIVATE_EMAIL_DOMAINS:-$DEFAULT_PRIVATE_EMAIL_DOMAINS}'; \ - echo 'SUPPORT_EMAIL=${SUPPORT_EMAIL:-$DEFAULT_SUPPORT_EMAIL}'; \ - echo 'PUBLIC_REGISTRATION_ENABLED=${PUBLIC_REGISTRATION_ENABLED:-true}'; \ - echo ''; \ - echo 'mkdir -p /etc/nginx/ssl'; \ - echo ''; \ - echo 'if [ ! -f /etc/nginx/ssl/nginx.crt ] || [ ! -f /etc/nginx/ssl/nginx.key ]; then'; \ - echo ' echo "Generating SSL certificate (10 years validity)..."'; \ - echo ' openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt -subj "/C=US/ST=State/L=City/O=Organization/CN=localhost"'; \ - echo ' chmod 644 /etc/nginx/ssl/nginx.crt'; \ - echo ' chmod 600 /etc/nginx/ssl/nginx.key'; \ - echo 'fi'; \ - echo ''; \ - echo '# Create simple JSON with environment variables'; \ - echo 'cat > /app/client/wwwroot/appsettings.json << EOF'; \ - echo '{'; \ - echo ' "PrivateEmailDomains": ["$PRIVATE_EMAIL_DOMAINS"],'; \ - echo ' "SupportEmail": "$SUPPORT_EMAIL",'; \ - echo ' "PublicRegistrationEnabled": "$PUBLIC_REGISTRATION_ENABLED"'; \ - echo '}'; \ - echo 'EOF'; \ - echo ''; \ - echo 'sed -i "s|/usr/share/nginx/html|/app/client/wwwroot|g" /app/client/nginx.conf'; \ - echo ''; \ - echo 'exec nginx -c /app/client/nginx.conf -g "daemon off;"'; \ - } > /etc/s6-overlay/s6-rc.d/client/run && \ - chmod +x /etc/s6-overlay/s6-rc.d/client/run && \ - echo "longrun" > /etc/s6-overlay/s6-rc.d/client/type - -# Admin service -RUN mkdir -p /etc/s6-overlay/s6-rc.d/admin && \ - { echo '#!/command/with-contenv bash'; \ - echo 'cd /app/admin'; \ - echo '# Read PostgreSQL password from file'; \ - echo 'if [ -f /secrets/postgres_password ]; then'; \ - echo ' POSTGRES_PASSWORD=$(cat /secrets/postgres_password)'; \ - echo 'else'; \ - echo ' POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-defaultpassword}"'; \ - echo 'fi'; \ - echo '# Wait for PostgreSQL to be ready'; \ - echo 'i=1; while [ $i -le 30 ]; do'; \ - echo ' if PGPASSWORD="$POSTGRES_PASSWORD" /usr/lib/postgresql/16/bin/psql -h localhost -U aliasvault -d aliasvault -c "SELECT 1;" >/dev/null 2>&1; then'; \ - echo ' echo "[admin] PostgreSQL ready, starting Admin..." >&2; break'; \ - echo ' fi'; \ - echo ' echo "[admin] Waiting for PostgreSQL ($i/30)..." >&2; sleep 2; i=$((i+1))'; \ - echo 'done'; \ - echo 'export ConnectionStrings__AliasServerDbContext="Host=localhost;Database=aliasvault;Username=aliasvault;Password=$POSTGRES_PASSWORD"'; \ - echo 'export ASPNETCORE_URLS="http://0.0.0.0:3002"'; \ - echo 'export ASPNETCORE_PATHBASE="/admin"'; \ - echo 'export IP_LOGGING_ENABLED="${IP_LOGGING_ENABLED:-true}"'; \ - echo 'exec dotnet AliasVault.Admin.dll'; \ - } > /etc/s6-overlay/s6-rc.d/admin/run && \ - chmod +x /etc/s6-overlay/s6-rc.d/admin/run && \ - echo "longrun" > /etc/s6-overlay/s6-rc.d/admin/type - -# SMTP service -RUN mkdir -p /etc/s6-overlay/s6-rc.d/smtp && \ - { echo '#!/command/with-contenv bash'; \ - echo 'cd /app/smtp'; \ - echo '# Read PostgreSQL password from file'; \ - echo 'if [ -f /secrets/postgres_password ]; then'; \ - echo ' POSTGRES_PASSWORD=$(cat /secrets/postgres_password)'; \ - echo 'else'; \ - echo ' POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-defaultpassword}"'; \ - echo 'fi'; \ - echo '# Wait for PostgreSQL to be ready'; \ - echo 'i=1; while [ $i -le 30 ]; do'; \ - echo ' if PGPASSWORD="$POSTGRES_PASSWORD" /usr/lib/postgresql/16/bin/psql -h localhost -U aliasvault -d aliasvault -c "SELECT 1;" >/dev/null 2>&1; then'; \ - echo ' echo "[smtp] PostgreSQL ready, starting SMTP..." >&2; break'; \ - echo ' fi'; \ - echo ' echo "[smtp] Waiting for PostgreSQL ($i/30)..." >&2; sleep 2; i=$((i+1))'; \ - echo 'done'; \ - echo 'export ConnectionStrings__AliasServerDbContext="Host=localhost;Database=aliasvault;Username=aliasvault;Password=$POSTGRES_PASSWORD"'; \ - echo 'export PRIVATE_EMAIL_DOMAINS="${PRIVATE_EMAIL_DOMAINS:-}"'; \ - echo 'export SMTP_TLS_ENABLED="${SMTP_TLS_ENABLED:-false}"'; \ - echo 'exec dotnet AliasVault.SmtpService.dll'; \ - } > /etc/s6-overlay/s6-rc.d/smtp/run && \ - chmod +x /etc/s6-overlay/s6-rc.d/smtp/run && \ - echo "longrun" > /etc/s6-overlay/s6-rc.d/smtp/type - -# Task Runner service -RUN mkdir -p /etc/s6-overlay/s6-rc.d/taskrunner && \ - { echo '#!/command/with-contenv bash'; \ - echo 'cd /app/taskrunner'; \ - echo '# Read PostgreSQL password from file'; \ - echo 'if [ -f /secrets/postgres_password ]; then'; \ - echo ' POSTGRES_PASSWORD=$(cat /secrets/postgres_password)'; \ - echo 'else'; \ - echo ' POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-defaultpassword}"'; \ - echo 'fi'; \ - echo '# Wait for PostgreSQL to be ready'; \ - echo 'i=1; while [ $i -le 30 ]; do'; \ - echo ' if PGPASSWORD="$POSTGRES_PASSWORD" /usr/lib/postgresql/16/bin/psql -h localhost -U aliasvault -d aliasvault -c "SELECT 1;" >/dev/null 2>&1; then'; \ - echo ' echo "[taskrunner] PostgreSQL ready, starting TaskRunner..." >&2; break'; \ - echo ' fi'; \ - echo ' echo "[taskrunner] Waiting for PostgreSQL ($i/30)..." >&2; sleep 2; i=$((i+1))'; \ - echo 'done'; \ - echo 'export ConnectionStrings__AliasServerDbContext="Host=localhost;Database=aliasvault;Username=aliasvault;Password=$POSTGRES_PASSWORD"'; \ - echo 'exec dotnet AliasVault.TaskRunner.dll'; \ - } > /etc/s6-overlay/s6-rc.d/taskrunner/run && \ - chmod +x /etc/s6-overlay/s6-rc.d/taskrunner/run && \ - echo "longrun" > /etc/s6-overlay/s6-rc.d/taskrunner/type - -# Nginx service -RUN mkdir -p /etc/s6-overlay/s6-rc.d/nginx && \ - { echo '#!/command/with-contenv bash'; \ - echo '# Generate SSL certificate if not exists'; \ - echo 'if [ ! -f /certificates/ssl/cert.pem ]; then'; \ - echo ' echo "Generating self-signed SSL certificate (10 years validity)..."'; \ - echo ' mkdir -p /certificates/ssl'; \ - echo ' openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \\'; \ - echo ' -keyout /certificates/ssl/key.pem \\'; \ - echo ' -out /certificates/ssl/cert.pem \\'; \ - echo ' -subj "/C=US/ST=State/L=City/O=Organization/CN=${HOSTNAME:-localhost}"'; \ - echo 'fi'; \ - echo ''; \ - echo '# Copy certificates to nginx directory'; \ - echo 'cp /certificates/ssl/* /etc/nginx/ssl/ 2>/dev/null || true'; \ - echo ''; \ - echo '# Create SSL configuration file'; \ - echo 'cat > /etc/nginx/ssl.conf << "SSLEOF"'; \ - echo 'ssl_certificate /etc/nginx/ssl/cert.pem;'; \ - echo 'ssl_certificate_key /etc/nginx/ssl/key.pem;'; \ - echo 'ssl_session_timeout 1d;'; \ - echo 'ssl_session_cache shared:MozTLS:10m;'; \ - echo 'ssl_session_tickets off;'; \ - echo 'ssl_protocols TLSv1.2 TLSv1.3;'; \ - echo 'ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;'; \ - echo 'ssl_prefer_server_ciphers off;'; \ - echo 'SSLEOF'; \ - echo ''; \ - echo '# Use container nginx configuration'; \ - echo 'cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf'; \ - echo ''; \ - echo '# Wait for all services to be ready'; \ - echo 'echo "[nginx] Waiting for services to be ready..."'; \ - echo 'i=1'; \ - echo 'while [ $i -le 60 ]; do'; \ - echo ' if nc -z localhost 3000 && nc -z localhost 3001 && nc -z localhost 3002; then'; \ - echo ' echo "[nginx] All services ready, starting nginx..."'; \ - echo ' break'; \ - echo ' fi'; \ - echo ' if [ $i -eq 60 ]; then'; \ - echo ' echo "[nginx] Timeout waiting for services"'; \ - echo ' exit 1'; \ - echo ' fi'; \ - echo ' echo "[nginx] Waiting for services ($i/60)..."'; \ - echo ' sleep 1'; \ - echo ' i=$(($i + 1))'; \ - echo 'done'; \ - echo ''; \ - echo 'exec nginx -g "daemon off;"'; \ - } > /etc/s6-overlay/s6-rc.d/nginx/run && \ - chmod +x /etc/s6-overlay/s6-rc.d/nginx/run && \ - echo "longrun" > /etc/s6-overlay/s6-rc.d/nginx/type && \ - mkdir -p /etc/s6-overlay/s6-rc.d/nginx/dependencies.d && \ - touch /etc/s6-overlay/s6-rc.d/nginx/dependencies.d/api && \ - touch /etc/s6-overlay/s6-rc.d/nginx/dependencies.d/client && \ - touch /etc/s6-overlay/s6-rc.d/nginx/dependencies.d/admin +# Service dependencies are now defined in the s6-scripts directory structure # Enable all services in the default bundle RUN mkdir -p /etc/s6-overlay/s6-rc.d/user/contents.d && \ - touch /etc/s6-overlay/s6-rc.d/user/contents.d/init-container && \ + touch /etc/s6-overlay/s6-rc.d/user/contents.d/init && \ touch /etc/s6-overlay/s6-rc.d/user/contents.d/postgres && \ touch /etc/s6-overlay/s6-rc.d/user/contents.d/api && \ touch /etc/s6-overlay/s6-rc.d/user/contents.d/client && \ @@ -412,7 +136,7 @@ RUN mkdir -p /etc/s6-overlay/s6-rc.d/user/contents.d && \ # Set environment variables for s6-overlay ENV S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \ - S6_VERBOSITY=1 \ + S6_VERBOSITY=0 \ S6_BEHAVIOUR_IF_STAGE2_FAILS=2 \ S6_KILL_GRACETIME=30000 diff --git a/dockerfiles/s6-scripts/admin/dependencies.d/postgres b/dockerfiles/s6-scripts/admin/dependencies.d/postgres new file mode 100644 index 000000000..e69de29bb diff --git a/dockerfiles/s6-scripts/admin/run b/dockerfiles/s6-scripts/admin/run new file mode 100644 index 000000000..1d932f568 --- /dev/null +++ b/dockerfiles/s6-scripts/admin/run @@ -0,0 +1,28 @@ +#!/command/with-contenv bash + +cd /app/admin + +# Read PostgreSQL password from file +POSTGRES_PASSWORD=$(cat /secrets/postgres_password) + +# Wait for PostgreSQL to be ready +echo "[admin] Waiting for PostgreSQL to be ready..." +for i in {1..30}; do + if PGPASSWORD="$POSTGRES_PASSWORD" /usr/lib/postgresql/16/bin/psql -h localhost -U aliasvault -d aliasvault -c "SELECT 1;" >/dev/null 2>&1; then + echo "[admin] PostgreSQL ready, starting Admin..." + break + fi + if [ $i -eq 30 ]; then + echo "[admin] Timeout waiting for PostgreSQL" + exit 1 + fi + sleep 2 +done + +export ConnectionStrings__AliasServerDbContext="Host=localhost;Database=aliasvault;Username=aliasvault;Password=$POSTGRES_PASSWORD" +export ASPNETCORE_URLS="http://0.0.0.0:3002" +export ASPNETCORE_PATHBASE="/admin" +export IP_LOGGING_ENABLED="${IP_LOGGING_ENABLED:-true}" + +echo "[admin] Starting Admin service..." +exec dotnet AliasVault.Admin.dll \ No newline at end of file diff --git a/dockerfiles/s6-scripts/admin/type b/dockerfiles/s6-scripts/admin/type new file mode 100644 index 000000000..1780f9f44 --- /dev/null +++ b/dockerfiles/s6-scripts/admin/type @@ -0,0 +1 @@ +longrun \ No newline at end of file diff --git a/dockerfiles/s6-scripts/api/dependencies.d/postgres b/dockerfiles/s6-scripts/api/dependencies.d/postgres new file mode 100644 index 000000000..e69de29bb diff --git a/dockerfiles/s6-scripts/api/run b/dockerfiles/s6-scripts/api/run new file mode 100644 index 000000000..398b050f3 --- /dev/null +++ b/dockerfiles/s6-scripts/api/run @@ -0,0 +1,30 @@ +#!/command/with-contenv bash + +cd /app/api + +# Read PostgreSQL password from file +POSTGRES_PASSWORD=$(cat /secrets/postgres_password) + +# Wait for PostgreSQL to be ready +echo "[api] Waiting for PostgreSQL to be ready..." +for i in {1..30}; do + if PGPASSWORD="$POSTGRES_PASSWORD" /usr/lib/postgresql/16/bin/psql -h localhost -U aliasvault -d aliasvault -c "SELECT 1;" >/dev/null 2>&1; then + echo "[api] PostgreSQL ready, starting API..." + break + fi + if [ $i -eq 30 ]; then + echo "[api] Timeout waiting for PostgreSQL" + exit 1 + fi + sleep 2 +done + +export ConnectionStrings__AliasServerDbContext="Host=localhost;Database=aliasvault;Username=aliasvault;Password=$POSTGRES_PASSWORD" +export ASPNETCORE_URLS="http://0.0.0.0:3001" +export ASPNETCORE_PATHBASE="/api" +export PRIVATE_EMAIL_DOMAINS="${PRIVATE_EMAIL_DOMAINS:-}" +export PUBLIC_REGISTRATION_ENABLED="${PUBLIC_REGISTRATION_ENABLED:-true}" +export IP_LOGGING_ENABLED="${IP_LOGGING_ENABLED:-true}" + +echo "[api] Starting API service..." +exec dotnet AliasVault.Api.dll \ No newline at end of file diff --git a/dockerfiles/s6-scripts/api/type b/dockerfiles/s6-scripts/api/type new file mode 100644 index 000000000..1780f9f44 --- /dev/null +++ b/dockerfiles/s6-scripts/api/type @@ -0,0 +1 @@ +longrun \ No newline at end of file diff --git a/dockerfiles/s6-scripts/client/dependencies.d/init b/dockerfiles/s6-scripts/client/dependencies.d/init new file mode 100644 index 000000000..e69de29bb diff --git a/dockerfiles/s6-scripts/client/run b/dockerfiles/s6-scripts/client/run new file mode 100644 index 000000000..2cdab1335 --- /dev/null +++ b/dockerfiles/s6-scripts/client/run @@ -0,0 +1,33 @@ +#!/command/with-contenv bash + +# Client service entrypoint +DEFAULT_PRIVATE_EMAIL_DOMAINS="" +DEFAULT_SUPPORT_EMAIL="" +PRIVATE_EMAIL_DOMAINS=${PRIVATE_EMAIL_DOMAINS:-$DEFAULT_PRIVATE_EMAIL_DOMAINS} +SUPPORT_EMAIL=${SUPPORT_EMAIL:-$DEFAULT_SUPPORT_EMAIL} +PUBLIC_REGISTRATION_ENABLED=${PUBLIC_REGISTRATION_ENABLED:-true} + +mkdir -p /etc/nginx/ssl + +if [ ! -f /etc/nginx/ssl/nginx.crt ] || [ ! -f /etc/nginx/ssl/nginx.key ]; then + openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ + -keyout /etc/nginx/ssl/nginx.key \ + -out /etc/nginx/ssl/nginx.crt \ + -subj "/C=US/ST=State/L=City/O=Organization/CN=localhost" >/dev/null 2>&1 + chmod 644 /etc/nginx/ssl/nginx.crt + chmod 600 /etc/nginx/ssl/nginx.key +fi + +# Create simple JSON with environment variables +cat > /app/client/wwwroot/appsettings.json << EOF +{ + "PrivateEmailDomains": ["$PRIVATE_EMAIL_DOMAINS"], + "SupportEmail": "$SUPPORT_EMAIL", + "PublicRegistrationEnabled": "$PUBLIC_REGISTRATION_ENABLED" +} +EOF + +sed -i "s|/usr/share/nginx/html|/app/client/wwwroot|g" /app/client/nginx.conf + +echo "[client] Starting Client service (nginx)..." +exec nginx -c /app/client/nginx.conf -g "daemon off;" \ No newline at end of file diff --git a/dockerfiles/s6-scripts/client/type b/dockerfiles/s6-scripts/client/type new file mode 100644 index 000000000..1780f9f44 --- /dev/null +++ b/dockerfiles/s6-scripts/client/type @@ -0,0 +1 @@ +longrun \ No newline at end of file diff --git a/dockerfiles/s6-scripts/init/dependencies.d/base b/dockerfiles/s6-scripts/init/dependencies.d/base new file mode 100644 index 000000000..e69de29bb diff --git a/dockerfiles/s6-scripts/init/script b/dockerfiles/s6-scripts/init/script new file mode 100644 index 000000000..7f52846ba --- /dev/null +++ b/dockerfiles/s6-scripts/init/script @@ -0,0 +1,139 @@ +#!/bin/sh -e + +# AliasVault Container Initialization Script +# This script runs once at container startup and handles all initialization tasks + +# Print AliasVault header +echo "" +echo "==================================================" +echo " _ _ _ __ __ _ _ " +echo " / \\ | (_) __ _ ___ \\ \\ / /_ _ _ _| | |_" +echo " / _ \\ | | |/ _\` / __| \\ \\/\\/ / _\` | | | | | __|" +echo " / ___ \\| | | (_| \\__ \\ \\ / / (_| | |_| | | |_ " +echo "/_/ \\_\\_|_|\\__,_|___/ \\/ \\__,__|\\__,_|_|\\__|" +echo "" +echo "==================================================" +echo "" + +echo "[init] Starting AliasVault container initialization..." +echo "" + +# Create required directories +echo "[init] Creating required directories..." +mkdir -p /database/postgres /logs/postgres /certificates /secrets /var/run/postgresql + +# Initialize secrets if they don't exist +echo "[init] Checking and initializing secrets..." + +if [ ! -f /secrets/postgres_password ]; then + echo "[init] → Generating PostgreSQL password..." + openssl rand -base64 32 | tr -d "\n" > /secrets/postgres_password + chmod 600 /secrets/postgres_password +else + echo "[init] → PostgreSQL password already exists" +fi + +if [ ! -f /secrets/data_protection_cert_pass ]; then + echo "[init] → Generating Data Protection Certificate password..." + openssl rand -base64 32 | tr -d "\n" > /secrets/data_protection_cert_pass + chmod 600 /secrets/data_protection_cert_pass +else + echo "[init] → Data Protection Certificate password already exists" +fi + +if [ ! -f /secrets/jwt_key ]; then + echo "[init] → Generating JWT key..." + openssl rand -base64 32 | tr -d "\n" > /secrets/jwt_key + chmod 600 /secrets/jwt_key +else + echo "[init] → JWT key already exists" +fi + +# Read PostgreSQL password for database initialization +POSTGRES_PASSWORD=$(cat /secrets/postgres_password) +export PGDATA="/database/postgres" + +# Initialize PostgreSQL if needed +if [ ! -d "$PGDATA/base" ]; then + echo "" + echo "[init] PostgreSQL database not found, initializing..." + + # Set proper permissions + chown -R postgres:postgres /database/postgres /logs/postgres /var/run/postgresql + chmod 700 /database/postgres + + # Initialize database as postgres user + echo "[init] → Running initdb..." + su - postgres -c "/usr/lib/postgresql/16/bin/initdb -D $PGDATA --locale=en_US.UTF-8 --encoding=UTF8" > /logs/postgres/initdb.log 2>&1 + + # Configure PostgreSQL + echo "[init] → Configuring PostgreSQL..." + echo "host all all 127.0.0.1/32 md5" >> "$PGDATA/pg_hba.conf" + echo "listen_addresses = '127.0.0.1'" >> "$PGDATA/postgresql.conf" + + # Start PostgreSQL temporarily to create database and user + echo "[init] → Starting PostgreSQL temporarily for setup..." + su - postgres -c "/usr/lib/postgresql/16/bin/pg_ctl -D $PGDATA -l /logs/postgres/postgres.log start" + + # Wait for PostgreSQL to be ready + echo "[init] → Waiting for PostgreSQL to be ready..." + i=1 + while [ $i -le 30 ]; do + if su - postgres -c "/usr/lib/postgresql/16/bin/psql -c 'SELECT 1;'" >/dev/null 2>&1; then + break + fi + sleep 1 + i=$((i + 1)) + done + + # Create database and user + echo "[init] → Creating AliasVault database and user..." + su - postgres -c "/usr/lib/postgresql/16/bin/psql -c \"CREATE USER aliasvault WITH PASSWORD '$POSTGRES_PASSWORD'\"" + su - postgres -c "/usr/lib/postgresql/16/bin/psql -c \"CREATE DATABASE aliasvault OWNER aliasvault;\"" + su - postgres -c "/usr/lib/postgresql/16/bin/psql -c \"GRANT ALL PRIVILEGES ON DATABASE aliasvault TO aliasvault;\"" + + # Stop PostgreSQL + echo "[init] → Stopping PostgreSQL..." + su - postgres -c "/usr/lib/postgresql/16/bin/pg_ctl -D $PGDATA stop" + sleep 2 + + echo "[init] → PostgreSQL initialization complete" +else + echo "[init] PostgreSQL database already initialized" + + # Just ensure permissions are correct + chown -R postgres:postgres /database/postgres /logs/postgres /var/run/postgresql + chmod 700 /database/postgres +fi + +# Future: Database migrations could go here +# echo "[init] Checking for database migrations..." +# if [ -f /app/migrations/pending ]; then +# echo "[init] → Running database migrations..." +# # Run migration logic here +# fi + +# Generate SSL certificates if needed +if [ ! -f /certificates/ssl/cert.pem ] || [ ! -f /certificates/ssl/key.pem ]; then + echo "" + echo "[init] Generating SSL certificates..." + mkdir -p /certificates/ssl + openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ + -keyout /certificates/ssl/key.pem \ + -out /certificates/ssl/cert.pem \ + -subj "/C=US/ST=State/L=City/O=AliasVault/CN=${HOSTNAME:-localhost}" \ + >/dev/null 2>&1 + chmod 600 /certificates/ssl/key.pem + chmod 644 /certificates/ssl/cert.pem + echo "[init] → SSL certificates generated" +else + echo "[init] SSL certificates already exist" +fi + +echo "" +echo "[init] =========================================" +echo "[init] AliasVault initialization complete!" +echo "[init] =========================================" +echo "" + +# Oneshot service exits successfully, dependencies can now start \ No newline at end of file diff --git a/dockerfiles/s6-scripts/init/type b/dockerfiles/s6-scripts/init/type new file mode 100644 index 000000000..3d92b15f2 --- /dev/null +++ b/dockerfiles/s6-scripts/init/type @@ -0,0 +1 @@ +oneshot \ No newline at end of file diff --git a/dockerfiles/s6-scripts/init/up b/dockerfiles/s6-scripts/init/up new file mode 100644 index 000000000..ef706057c --- /dev/null +++ b/dockerfiles/s6-scripts/init/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init/script \ No newline at end of file diff --git a/dockerfiles/s6-scripts/nginx/dependencies.d/admin b/dockerfiles/s6-scripts/nginx/dependencies.d/admin new file mode 100644 index 000000000..e69de29bb diff --git a/dockerfiles/s6-scripts/nginx/dependencies.d/api b/dockerfiles/s6-scripts/nginx/dependencies.d/api new file mode 100644 index 000000000..e69de29bb diff --git a/dockerfiles/s6-scripts/nginx/dependencies.d/client b/dockerfiles/s6-scripts/nginx/dependencies.d/client new file mode 100644 index 000000000..e69de29bb diff --git a/dockerfiles/s6-scripts/nginx/run b/dockerfiles/s6-scripts/nginx/run new file mode 100644 index 000000000..a6ddd2044 --- /dev/null +++ b/dockerfiles/s6-scripts/nginx/run @@ -0,0 +1,34 @@ +#!/command/with-contenv bash + +# Copy certificates to nginx directory (they were created during init) +mkdir -p /etc/nginx/ssl +cp /certificates/ssl/* /etc/nginx/ssl/ 2>/dev/null || true + +# Create SSL configuration file +cat > /etc/nginx/ssl.conf << "SSLEOF" +ssl_certificate /etc/nginx/ssl/cert.pem; +ssl_certificate_key /etc/nginx/ssl/key.pem; +ssl_session_timeout 1d; +ssl_session_cache shared:MozTLS:10m; +ssl_session_tickets off; +ssl_protocols TLSv1.2 TLSv1.3; +ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; +ssl_prefer_server_ciphers off; +SSLEOF + +# Wait for all services to be ready +echo "[nginx] Waiting for services to be ready..." +for i in {1..60}; do + if nc -z localhost 3000 && nc -z localhost 3001 && nc -z localhost 3002; then + echo "[nginx] All services ready, starting reverse proxy..." + break + fi + if [ $i -eq 60 ]; then + echo "[nginx] Timeout waiting for services" + exit 1 + fi + sleep 1 +done + +echo "[nginx] Starting reverse proxy..." +exec nginx -g "daemon off;" \ No newline at end of file diff --git a/dockerfiles/s6-scripts/nginx/type b/dockerfiles/s6-scripts/nginx/type new file mode 100644 index 000000000..1780f9f44 --- /dev/null +++ b/dockerfiles/s6-scripts/nginx/type @@ -0,0 +1 @@ +longrun \ No newline at end of file diff --git a/dockerfiles/s6-scripts/postgres/dependencies.d/init b/dockerfiles/s6-scripts/postgres/dependencies.d/init new file mode 100644 index 000000000..e69de29bb diff --git a/dockerfiles/s6-scripts/postgres/run b/dockerfiles/s6-scripts/postgres/run new file mode 100644 index 000000000..6f81470a0 --- /dev/null +++ b/dockerfiles/s6-scripts/postgres/run @@ -0,0 +1,10 @@ +#!/bin/sh + +# Set PostgreSQL paths +export PATH="/usr/lib/postgresql/16/bin:$PATH" +export PGDATA="/database/postgres" + +echo "[postgres] Starting PostgreSQL server..." + +# Run PostgreSQL as postgres user +exec s6-setuidgid postgres /usr/lib/postgresql/16/bin/postgres -D "$PGDATA" \ No newline at end of file diff --git a/dockerfiles/s6-scripts/postgres/type b/dockerfiles/s6-scripts/postgres/type new file mode 100644 index 000000000..1780f9f44 --- /dev/null +++ b/dockerfiles/s6-scripts/postgres/type @@ -0,0 +1 @@ +longrun \ No newline at end of file diff --git a/dockerfiles/s6-scripts/smtp/dependencies.d/postgres b/dockerfiles/s6-scripts/smtp/dependencies.d/postgres new file mode 100644 index 000000000..e69de29bb diff --git a/dockerfiles/s6-scripts/smtp/run b/dockerfiles/s6-scripts/smtp/run new file mode 100644 index 000000000..8e38822c8 --- /dev/null +++ b/dockerfiles/s6-scripts/smtp/run @@ -0,0 +1,27 @@ +#!/command/with-contenv bash + +cd /app/smtp + +# Read PostgreSQL password from file +POSTGRES_PASSWORD=$(cat /secrets/postgres_password) + +# Wait for PostgreSQL to be ready +echo "[smtp] Waiting for PostgreSQL to be ready..." +for i in {1..30}; do + if PGPASSWORD="$POSTGRES_PASSWORD" /usr/lib/postgresql/16/bin/psql -h localhost -U aliasvault -d aliasvault -c "SELECT 1;" >/dev/null 2>&1; then + echo "[smtp] PostgreSQL ready, starting SMTP..." + break + fi + if [ $i -eq 30 ]; then + echo "[smtp] Timeout waiting for PostgreSQL" + exit 1 + fi + sleep 2 +done + +export ConnectionStrings__AliasServerDbContext="Host=localhost;Database=aliasvault;Username=aliasvault;Password=$POSTGRES_PASSWORD" +export PRIVATE_EMAIL_DOMAINS="${PRIVATE_EMAIL_DOMAINS:-}" +export SMTP_TLS_ENABLED="${SMTP_TLS_ENABLED:-false}" + +echo "[smtp] Starting SMTP service..." +exec dotnet AliasVault.SmtpService.dll \ No newline at end of file diff --git a/dockerfiles/s6-scripts/smtp/type b/dockerfiles/s6-scripts/smtp/type new file mode 100644 index 000000000..1780f9f44 --- /dev/null +++ b/dockerfiles/s6-scripts/smtp/type @@ -0,0 +1 @@ +longrun \ No newline at end of file diff --git a/dockerfiles/s6-scripts/taskrunner/dependencies.d/postgres b/dockerfiles/s6-scripts/taskrunner/dependencies.d/postgres new file mode 100644 index 000000000..e69de29bb diff --git a/dockerfiles/s6-scripts/taskrunner/run b/dockerfiles/s6-scripts/taskrunner/run new file mode 100644 index 000000000..d0eb72137 --- /dev/null +++ b/dockerfiles/s6-scripts/taskrunner/run @@ -0,0 +1,25 @@ +#!/command/with-contenv bash + +cd /app/taskrunner + +# Read PostgreSQL password from file +POSTGRES_PASSWORD=$(cat /secrets/postgres_password) + +# Wait for PostgreSQL to be ready +echo "[taskrunner] Waiting for PostgreSQL to be ready..." +for i in {1..30}; do + if PGPASSWORD="$POSTGRES_PASSWORD" /usr/lib/postgresql/16/bin/psql -h localhost -U aliasvault -d aliasvault -c "SELECT 1;" >/dev/null 2>&1; then + echo "[taskrunner] PostgreSQL ready, starting TaskRunner..." + break + fi + if [ $i -eq 30 ]; then + echo "[taskrunner] Timeout waiting for PostgreSQL" + exit 1 + fi + sleep 2 +done + +export ConnectionStrings__AliasServerDbContext="Host=localhost;Database=aliasvault;Username=aliasvault;Password=$POSTGRES_PASSWORD" + +echo "[taskrunner] Starting TaskRunner service..." +exec dotnet AliasVault.TaskRunner.dll \ No newline at end of file diff --git a/dockerfiles/s6-scripts/taskrunner/type b/dockerfiles/s6-scripts/taskrunner/type new file mode 100644 index 000000000..1780f9f44 --- /dev/null +++ b/dockerfiles/s6-scripts/taskrunner/type @@ -0,0 +1 @@ +longrun \ No newline at end of file