From 279a1f2ab281f3437a25c1ed4558ae72074212f3 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Fri, 5 Sep 2025 15:19:10 +0200 Subject: [PATCH 01/33] Update docker-compose.all-in-one.yml (#1181) --- dockerfiles/docker-compose.all-in-one.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/dockerfiles/docker-compose.all-in-one.yml b/dockerfiles/docker-compose.all-in-one.yml index 5266de441..4d5405d6a 100644 --- a/dockerfiles/docker-compose.all-in-one.yml +++ b/dockerfiles/docker-compose.all-in-one.yml @@ -1,5 +1,3 @@ -version: "3" - services: aliasvault: image: ghcr.io/aliasvault/aliasvault:latest From 21e0ad501763a19e1edabd12d85fb3f6c2dc2fcc Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Fri, 5 Sep 2025 16:35:02 +0200 Subject: [PATCH 02/33] Update all-in-one image to run in HTTP 80 mode (#1181) --- dockerfiles/all-in-one/Dockerfile | 14 +-- dockerfiles/all-in-one/README.md | 2 - dockerfiles/all-in-one/config/nginx.conf | 112 ++++++++++++++++++ dockerfiles/all-in-one/s6-scripts/init/script | 31 ----- dockerfiles/all-in-one/s6-scripts/nginx/run | 18 +-- dockerfiles/docker-compose.all-in-one.yml | 2 - 6 files changed, 117 insertions(+), 62 deletions(-) create mode 100644 dockerfiles/all-in-one/config/nginx.conf diff --git a/dockerfiles/all-in-one/Dockerfile b/dockerfiles/all-in-one/Dockerfile index fd5b28cca..473f394b2 100644 --- a/dockerfiles/all-in-one/Dockerfile +++ b/dockerfiles/all-in-one/Dockerfile @@ -109,11 +109,8 @@ RUN apt-get update && \ /app/taskrunner \ /app/installcli \ /database \ - /certificates/ssl \ /logs/postgres \ - /etc/nginx/ssl \ /var/run/postgresql \ - /var/www/certbot \ /etc/s6-overlay/s6-rc.d/user/contents.d # Copy built applications and all configuration files from builder stage @@ -122,7 +119,7 @@ COPY --from=dotnet-builder /app /app # Copy configuration files and scripts directly to their destinations COPY dockerfiles/all-in-one/reset-admin-password.sh /usr/local/bin/reset-admin-password.sh COPY apps/server/AliasVault.Client/nginx.conf /app/client/nginx.conf -COPY apps/server/nginx.conf /etc/nginx/nginx.conf +COPY dockerfiles/all-in-one/config/nginx.conf /etc/nginx/nginx.conf COPY apps/server/status.html /usr/share/nginx/html/status.html COPY dockerfiles/all-in-one/s6-scripts /etc/s6-overlay/s6-rc.d/ @@ -131,9 +128,6 @@ RUN cp -r /app/installcli /usr/local/bin/aliasvault-cli && \ chmod +x /usr/local/bin/aliasvault-cli/AliasVault.InstallCli \ /usr/local/bin/reset-admin-password.sh && \ ln -s /usr/local/bin/aliasvault-cli/AliasVault.InstallCli /usr/local/bin/aliasvault-cli.sh && \ - sed -i 's/server client:3000/server localhost:3000/g; \ - s/server api:3001/server localhost:3001/g; \ - s/server admin:3002/server localhost:3002/g' /etc/nginx/nginx.conf && \ find /etc/s6-overlay/s6-rc.d -type f \( -name "run" -o -name "up" -o -name "script" \) -exec chmod +x {} \; && \ cd /etc/s6-overlay/s6-rc.d/user/contents.d && \ touch init postgres api client admin smtp taskrunner nginx notification && \ @@ -153,8 +147,8 @@ ENV ALIASVAULT_VERBOSITY=0 \ S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \ S6_VERBOSITY=0 -EXPOSE 80 443 25 587 -VOLUME ["/database", "/certificates", "/logs", "/secrets"] +EXPOSE 80 25 587 +VOLUME ["/database", "/logs", "/secrets"] HEALTHCHECK --interval=30s --timeout=10s --start-period=90s --retries=3 \ - CMD curl -f -k https://localhost/api || exit 1 + CMD curl -f http://localhost/api || exit 1 ENTRYPOINT ["/init"] \ No newline at end of file diff --git a/dockerfiles/all-in-one/README.md b/dockerfiles/all-in-one/README.md index a22b570d0..778bf858e 100644 --- a/dockerfiles/all-in-one/README.md +++ b/dockerfiles/all-in-one/README.md @@ -18,9 +18,7 @@ docker build -f dockerfiles/all-in-one/Dockerfile -t aliasvault-allinone:local . docker run -d \ --name aliasvault \ -p 80:80 \ - -p 443:443 \ -v ./database:/database \ - -v ./certificates:/certificates \ -v ./logs:/logs \ -v ./secrets:/secrets \ aliasvault-allinone:local diff --git a/dockerfiles/all-in-one/config/nginx.conf b/dockerfiles/all-in-one/config/nginx.conf new file mode 100644 index 000000000..e469e51b3 --- /dev/null +++ b/dockerfiles/all-in-one/config/nginx.conf @@ -0,0 +1,112 @@ +# This is the main Nginx configuration file for the AliasVault all-in-one container. +# which exposes all AliasVault services via a single port 80. +events { + worker_connections 1024; +} + +http { + client_max_body_size 25M; + + upstream client { + server localhost:3000 max_fails=1 fail_timeout=5s; + } + + upstream api { + server localhost:3001 max_fails=1 fail_timeout=5s; + } + + upstream admin { + server localhost:3002 max_fails=1 fail_timeout=5s; + } + + # Preserve any existing X-Forwarded-* headers, this is relevant if AliasVault + # is running behind another reverse proxy. + set_real_ip_from 10.0.0.0/8; + set_real_ip_from 172.16.0.0/12; + set_real_ip_from 192.168.0.0/16; + real_ip_header X-Forwarded-For; + real_ip_recursive on; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Enable gzip compression, which reduces the amount of data that needs to be transferred + # to speed up WASM load times. + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + + server { + listen 80; + server_name _; + + # Security headers + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header Cross-Origin-Resource-Policy "same-origin" always; + add_header Content-Security-Policy "frame-ancestors 'self'" always; + + # Root for static files + root /usr/share/nginx/html; + + # Error page handler location (internal use only) + location = /status.html { + internal; + try_files /status.html =404; + add_header Cache-Control "no-cache, no-store, must-revalidate"; + add_header Pragma "no-cache"; + add_header Expires "0"; + } + + # Admin interface + location /admin { + proxy_pass http://admin; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Prefix /admin/; + + # Add WebSocket support for Blazor server + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_read_timeout 86400; + + # Show status page if admin is down + proxy_intercept_errors on; + error_page 502 503 504 =503 /status.html; + } + + # API endpoints + location /api { + proxy_pass http://api; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # Show status page if api is down + proxy_intercept_errors on; + error_page 502 503 504 =503 /status.html; + } + + # Client app (root path) + location / { + proxy_pass http://client; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # Show status page if client is down + proxy_intercept_errors on; + error_page 502 503 504 =503 /status.html; + } + } +} \ No newline at end of file diff --git a/dockerfiles/all-in-one/s6-scripts/init/script b/dockerfiles/all-in-one/s6-scripts/init/script index f4204bfac..3160295c1 100644 --- a/dockerfiles/all-in-one/s6-scripts/init/script +++ b/dockerfiles/all-in-one/s6-scripts/init/script @@ -154,37 +154,6 @@ else chmod 700 /database/postgres fi -# Future: Database migrations could go here -# log 1 "[init] Checking for database migrations..." -# if [ -f /app/migrations/pending ]; then -# log 1 "[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 - log 0 "" - log 0 "[init] Generating SSL certificates..." - mkdir -p /certificates/ssl - if [ "$VERBOSITY" -ge 2 ]; then - 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}" - else - 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 - fi - chmod 600 /certificates/ssl/key.pem - chmod 644 /certificates/ssl/cert.pem - log 0 "[init] Self-signed SSL certificates generated" -else - log 0 "[init] βœ… Self-signed SSL certificates already exist" -fi - echo "" echo "[init] Waiting for all services to be ready... this can take a short while..." echo "" diff --git a/dockerfiles/all-in-one/s6-scripts/nginx/run b/dockerfiles/all-in-one/s6-scripts/nginx/run index 761d645d2..5198bb4f7 100644 --- a/dockerfiles/all-in-one/s6-scripts/nginx/run +++ b/dockerfiles/all-in-one/s6-scripts/nginx/run @@ -3,23 +3,7 @@ # Get verbosity level (0=minimal, 1=normal, 2=verbose) VERBOSITY="${ALIASVAULT_VERBOSITY:-0}" -# 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 - -echo "Starting Nginx reverse proxy..." +echo "Starting Nginx reverse proxy (HTTP-only mode)..." # Set nginx error log level based on verbosity if [ "$VERBOSITY" -ge 2 ]; then diff --git a/dockerfiles/docker-compose.all-in-one.yml b/dockerfiles/docker-compose.all-in-one.yml index 4d5405d6a..dacda95c9 100644 --- a/dockerfiles/docker-compose.all-in-one.yml +++ b/dockerfiles/docker-compose.all-in-one.yml @@ -6,13 +6,11 @@ services: ports: - "80:80" - - "443:443" - "25:25" - "587:587" volumes: - ./database:/database - - ./certificates:/certificates - ./logs:/logs - ./secrets:/secrets From d629ffb6e54d9f2bbd598ea77b5823ea63cbc33e Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Fri, 5 Sep 2025 17:49:37 +0200 Subject: [PATCH 03/33] Update all-in-one build to prevent lock contention (#1181) --- dockerfiles/all-in-one/Dockerfile | 43 +++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/dockerfiles/all-in-one/Dockerfile b/dockerfiles/all-in-one/Dockerfile index 473f394b2..ff6845906 100644 --- a/dockerfiles/all-in-one/Dockerfile +++ b/dockerfiles/all-in-one/Dockerfile @@ -18,24 +18,39 @@ COPY shared/ ./shared/ # Install required .NET workloads and restore packages once for the entire solution WORKDIR /src/apps/server + +# Install workloads and restore packages once for the entire solution RUN dotnet workload install wasm-tools && \ dotnet restore aliasvault.sln -# Build all applications +# Build each application in separate layers to avoid lock contention + +# Build API RUN dotnet publish AliasVault.Api/AliasVault.Api.csproj \ - -c Release -o /app/api --no-restore && \ - dotnet publish AliasVault.Client/AliasVault.Client.csproj \ - -c Release -o /app/client --no-restore && \ - dotnet publish AliasVault.Admin/AliasVault.Admin.csproj \ - -c Release -o /app/admin --no-restore && \ - dotnet publish Services/AliasVault.SmtpService/AliasVault.SmtpService.csproj \ - -c Release -o /app/smtp --no-restore && \ - dotnet publish Services/AliasVault.TaskRunner/AliasVault.TaskRunner.csproj \ - -c Release -o /app/taskrunner --no-restore && \ - dotnet publish Utilities/AliasVault.InstallCli/AliasVault.InstallCli.csproj \ - -c Release -o /app/installcli --no-restore && \ - # Clean up .NET debug files and unnecessary files - find /app -name "*.pdb" -delete && \ + -c Release -o /app/api --no-restore + +# Build Client (contains WASM which can be slow) +RUN dotnet publish AliasVault.Client/AliasVault.Client.csproj \ + -c Release -o /app/client --no-restore + +# Build Admin +RUN dotnet publish AliasVault.Admin/AliasVault.Admin.csproj \ + -c Release -o /app/admin --no-restore + +# Build SMTP Service +RUN dotnet publish Services/AliasVault.SmtpService/AliasVault.SmtpService.csproj \ + -c Release -o /app/smtp --no-restore + +# Build Task Runner +RUN dotnet publish Services/AliasVault.TaskRunner/AliasVault.TaskRunner.csproj \ + -c Release -o /app/taskrunner --no-restore + +# Build Install CLI +RUN dotnet publish Utilities/AliasVault.InstallCli/AliasVault.InstallCli.csproj \ + -c Release -o /app/installcli --no-restore + +# Clean up .NET debug files and unnecessary files +RUN find /app -name "*.pdb" -delete && \ find /app -name "*.xml" -not -name "*.deps.json" -delete && \ find /app -name "*.Development.json" -delete && \ find /app -name "web.config" -delete From 8655f15731e52f626dda82117296f5751600a523 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Fri, 5 Sep 2025 18:37:46 +0200 Subject: [PATCH 04/33] Support both HTTP and HTTPS in all in one docker image (#1181) --- .../Main/Layout/Footer.razor | 53 +- .../Resources/Layout/Footer.en.resx | 8 + .../wwwroot/css/tailwind.css | 3665 ++++++++++++++++- dockerfiles/all-in-one/Dockerfile | 2 +- dockerfiles/all-in-one/config/nginx.conf | 75 + dockerfiles/all-in-one/s6-scripts/init/script | 25 + dockerfiles/all-in-one/s6-scripts/nginx/run | 18 +- 7 files changed, 3841 insertions(+), 5 deletions(-) diff --git a/apps/server/AliasVault.Client/Main/Layout/Footer.razor b/apps/server/AliasVault.Client/Main/Layout/Footer.razor index d9f925eb4..a26688ffc 100644 --- a/apps/server/AliasVault.Client/Main/Layout/Footer.razor +++ b/apps/server/AliasVault.Client/Main/Layout/Footer.razor @@ -4,11 +4,35 @@ @using Microsoft.Extensions.Localization @implements IDisposable -