From df6de32a4a80427d627f408147fb84abe66d1bb3 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Fri, 15 Nov 2024 11:55:26 +0100 Subject: [PATCH] Update docker setup to run under HTTPS by default (#364) --- .gitignore | 4 ++++ certificates/README.md | 8 +++++--- certificates/ssl/README.md | 7 +++++++ docker-compose.yml | 16 +++++++++++----- install.sh | 11 ++++++----- src/AliasVault.Admin/Dockerfile | 14 ++++++++++---- src/AliasVault.Admin/entrypoint.sh | 29 +++++++++++++++++++++++++++++ src/AliasVault.Api/Dockerfile | 11 +++++++---- src/AliasVault.Api/entrypoint.sh | 28 ++++++++++++++++++++++++++-- src/AliasVault.Client/Dockerfile | 14 +++++++++++--- src/AliasVault.Client/entrypoint.sh | 21 +++++++++++++++++---- src/AliasVault.Client/nginx.conf | 15 ++++++++++++++- 12 files changed, 147 insertions(+), 31 deletions(-) create mode 100644 certificates/ssl/README.md create mode 100644 src/AliasVault.Admin/entrypoint.sh diff --git a/.gitignore b/.gitignore index dc89dca9d..015874678 100644 --- a/.gitignore +++ b/.gitignore @@ -393,3 +393,7 @@ src/Tests/AliasVault.E2ETests/appsettings.Development.json # Draw.io diagram temp files *.drawio.* +# Certificates +certificates/*.crt +certificates/*.key +certificates/*.pfx diff --git a/certificates/README.md b/certificates/README.md index 007cb45fa..b15837c13 100644 --- a/certificates/README.md +++ b/certificates/README.md @@ -1,4 +1,6 @@ -This is the default location where (self-generated) certificates are stored. +# Certificates directory structure -For example, the API and Admin projects make use of the .NET DataProtection API that depends on -certificates for encrypting various types of application data such as authentication cookies, anti-forgery tokens etc. +This directory contains certificates for AliasVault. + +- `app`: Certificates that AliasVault uses to protect application data at rest (e.g. .NET DataProtection keys) +- `ssl`: SSL/TLS certificates for AliasVault hosted services diff --git a/certificates/ssl/README.md b/certificates/ssl/README.md new file mode 100644 index 000000000..d6b748e4e --- /dev/null +++ b/certificates/ssl/README.md @@ -0,0 +1,7 @@ +# SSL certificates directory structure + +This directory contains SSL/TLS certificates for various AliasVault services: + +- `admin`: Certificate for the Admin UI. +- `api`: Certificate for the API service. +- `client`: Certificate for the Client UI. diff --git a/docker-compose.yml b/docker-compose.yml index eb31509ac..17e809381 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,21 +5,26 @@ services: context: . dockerfile: src/AliasVault.Admin/Dockerfile ports: - - "8080:8082" + - "4431:4431" volumes: - - ./certificates:/certificates:rw + - ./certificates/app:/certificates:rw + - ./certificates/ssl/admin:/app/ssl:rw - ./database:/database:rw - ./logs:/logs:rw restart: always env_file: - .env + client: image: aliasvault-client build: context: . dockerfile: src/AliasVault.Client/Dockerfile ports: - - "80:8080" + - "80:80" + - "443:443" + volumes: + - ./certificates/ssl/client:/etc/nginx/ssl:rw restart: always env_file: - .env @@ -30,9 +35,10 @@ services: context: . dockerfile: src/AliasVault.Api/Dockerfile ports: - - "81:8081" + - "4430:4430" volumes: - - ./certificates:/certificates:rw + - ./certificates/app:/certificates:rw + - ./certificates/ssl/api:/app/ssl:rw - ./database:/database:rw - ./logs:/logs:rw env_file: diff --git a/install.sh b/install.sh index 955097506..44776b8b5 100755 --- a/install.sh +++ b/install.sh @@ -140,11 +140,11 @@ create_env_file() { fi } -# Function to check and populate the .env file with API_URL +# Function to populate API_URL populate_api_url() { printf "${CYAN}> Checking API_URL...${NC}\n" if ! grep -q "^API_URL=" "$ENV_FILE" || [ -z "$(grep "^API_URL=" "$ENV_FILE" | cut -d '=' -f2)" ]; then - DEFAULT_API_URL="http://localhost:81" + DEFAULT_API_URL="https://localhost:4430" read -p "Enter the base URL where the API will be hosted (press Enter for default: $DEFAULT_API_URL): " USER_API_URL API_URL=${USER_API_URL:-$DEFAULT_API_URL} if grep -q "^API_URL=" "$ENV_FILE"; then @@ -381,14 +381,14 @@ main() { printf "${CYAN}To configure the server, login to the admin panel:${NC}\n" printf "\n" if [ "$ADMIN_PASSWORD" != "" ]; then - printf "Admin Panel: http://localhost:8080/\n" + printf "Admin Panel: https://localhost:4431/\n" printf "Username: admin\n" printf "Password: $ADMIN_PASSWORD\n" printf "\n" printf "${YELLOW}(!) Caution: Make sure to backup the above credentials in a safe place, they won't be shown again!${NC}\n" printf "\n" else - printf "Admin Panel: http://localhost:8080/\n" + printf "Admin Panel: https://localhost:4431/\n" printf "Username: admin\n" printf "Password: (Previously set. Run this command with --reset-password to generate a new one.)\n" printf "\n" @@ -397,7 +397,8 @@ main() { printf "\n" printf "${CYAN}In order to start using AliasVault and create your own vault, log into the client website:${NC}\n" printf "\n" - printf "Client Website: http://localhost:80/\n" + printf "Client Website: https://localhost:443/\n" + printf "${YELLOW}Note: The client website will automatically redirect from HTTP (port 80) to HTTPS (port 443)${NC}\n" printf "You can create your own account from there.\n" printf "\n" printf "${MAGENTA}=========================================================${NC}\n" diff --git a/src/AliasVault.Admin/Dockerfile b/src/AliasVault.Admin/Dockerfile index cbbc3c3b1..d49be3ca3 100644 --- a/src/AliasVault.Admin/Dockerfile +++ b/src/AliasVault.Admin/Dockerfile @@ -1,6 +1,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base WORKDIR /app -EXPOSE 8082 +EXPOSE 4431 FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build ARG BUILD_CONFIGURATION=Release @@ -24,6 +24,12 @@ RUN dotnet publish "AliasVault.Admin.csproj" -c "$BUILD_CONFIGURATION" -o /app/p FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -EXPOSE 8082 -ENV ASPNETCORE_URLS=http://+:8082 -ENTRYPOINT ["dotnet", "AliasVault.Admin.dll"] +COPY /src/AliasVault.Admin/entrypoint.sh /app + +# Create SSL directory +RUN mkdir -p /app/ssl && chmod 755 /app/ssl + +RUN chmod +x /app/entrypoint.sh +EXPOSE 4431 +ENV ASPNETCORE_URLS=https://+:4431 +ENTRYPOINT ["/app/entrypoint.sh"] diff --git a/src/AliasVault.Admin/entrypoint.sh b/src/AliasVault.Admin/entrypoint.sh new file mode 100644 index 000000000..a282a8189 --- /dev/null +++ b/src/AliasVault.Admin/entrypoint.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +# Create SSL directory if it doesn't exist +mkdir -p /app/ssl + +# Generate self-signed SSL certificate if not exists +if [ ! -f /app/ssl/admin.crt ] || [ ! -f /app/ssl/admin.key ]; then + echo "Generating new SSL certificate..." + openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout /app/ssl/admin.key \ + -out /app/ssl/admin.crt \ + -subj "/C=US/ST=State/L=City/O=Organization/CN=localhost" + + # Set proper permissions + chmod 644 /app/ssl/admin.crt + chmod 600 /app/ssl/admin.key + + # Create PFX for ASP.NET Core + openssl pkcs12 -export -out /app/ssl/admin.pfx \ + -inkey /app/ssl/admin.key \ + -in /app/ssl/admin.crt \ + -password pass:YourSecurePassword +fi + +export ASPNETCORE_Kestrel__Certificates__Default__Path=/app/ssl/admin.pfx +export ASPNETCORE_Kestrel__Certificates__Default__Password=YourSecurePassword + +# Start the application +dotnet AliasVault.Admin.dll \ No newline at end of file diff --git a/src/AliasVault.Api/Dockerfile b/src/AliasVault.Api/Dockerfile index ef466df00..31f3aeeb1 100644 --- a/src/AliasVault.Api/Dockerfile +++ b/src/AliasVault.Api/Dockerfile @@ -1,6 +1,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base WORKDIR /app -EXPOSE 8081 +EXPOSE 4430 FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build ARG BUILD_CONFIGURATION=Release @@ -16,7 +16,6 @@ COPY . . WORKDIR "/src/src/AliasVault.Api" RUN dotnet build "AliasVault.Api.csproj" -c "$BUILD_CONFIGURATION" -o /app/build -# Publish the application to the /app/publish directory in the container FROM build AS publish ARG BUILD_CONFIGURATION=Release RUN dotnet publish "AliasVault.Api.csproj" -c "$BUILD_CONFIGURATION" -o /app/publish /p:UseAppHost=false @@ -25,7 +24,11 @@ FROM base AS final WORKDIR /app COPY --from=publish /app/publish . COPY /src/AliasVault.Api/entrypoint.sh /app + +# Create SSL directory +RUN mkdir -p /app/ssl && chmod 755 /app/ssl + RUN chmod +x /app/entrypoint.sh -EXPOSE 8081 -ENV ASPNETCORE_URLS=http://+:8081 +EXPOSE 4430 +ENV ASPNETCORE_URLS=https://+:4430 ENTRYPOINT ["/app/entrypoint.sh"] diff --git a/src/AliasVault.Api/entrypoint.sh b/src/AliasVault.Api/entrypoint.sh index bfd531883..ce198847b 100644 --- a/src/AliasVault.Api/entrypoint.sh +++ b/src/AliasVault.Api/entrypoint.sh @@ -1,5 +1,29 @@ #!/bin/sh +# Create SSL directory if it doesn't exist +mkdir -p /app/ssl + +# Generate self-signed SSL certificate if not exists +if [ ! -f /app/ssl/api.crt ] || [ ! -f /app/ssl/api.key ]; then + echo "Generating new SSL certificate..." + openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout /app/ssl/api.key \ + -out /app/ssl/api.crt \ + -subj "/C=US/ST=State/L=City/O=Organization/CN=localhost" + + # Set proper permissions + chmod 644 /app/ssl/api.crt + chmod 600 /app/ssl/api.key + + # Create PFX for ASP.NET Core + openssl pkcs12 -export -out /app/ssl/api.pfx \ + -inkey /app/ssl/api.key \ + -in /app/ssl/api.crt \ + -password pass:YourSecurePassword +fi + +export ASPNETCORE_Kestrel__Certificates__Default__Path=/app/ssl/api.pfx +export ASPNETCORE_Kestrel__Certificates__Default__Password=YourSecurePassword + # Start the application -echo "Starting application..." -dotnet /app/AliasVault.Api.dll +dotnet AliasVault.Api.dll diff --git a/src/AliasVault.Client/Dockerfile b/src/AliasVault.Client/Dockerfile index 1e59424c6..ba5617745 100644 --- a/src/AliasVault.Client/Dockerfile +++ b/src/AliasVault.Client/Dockerfile @@ -9,7 +9,7 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 WORKDIR /src # Install Python which is required by the WebAssembly tools -RUN apt-get update && apt-get install -y python3 && apt-get clean +RUN apt-get update && apt-get install -y python3 openssl && apt-get clean # Install the WebAssembly tools RUN dotnet workload install wasm-tools @@ -31,11 +31,19 @@ RUN dotnet publish "AliasVault.Client.csproj" -c "$BUILD_CONFIGURATION" -o /app/ # Final stage FROM nginx:1.24.0 AS final +# Install OpenSSL for certificate generation +RUN apt-get update && apt-get install -y openssl && apt-get clean + WORKDIR /usr/share/nginx/html COPY --from=publish /app/publish/wwwroot . COPY /src/AliasVault.Client/nginx.conf /etc/nginx/nginx.conf COPY /src/AliasVault.Client/entrypoint.sh /app/ + +# Create SSL directory +RUN mkdir -p /etc/nginx/ssl && chmod 755 /etc/nginx/ssl + RUN chmod +x /app/entrypoint.sh -EXPOSE 8080 -ENV ASPNETCORE_URLS=http://+:8080 + +EXPOSE 80 443 +ENV ASPNETCORE_URLS=http://+:80;https://+:443 ENTRYPOINT ["/app/entrypoint.sh"] diff --git a/src/AliasVault.Client/entrypoint.sh b/src/AliasVault.Client/entrypoint.sh index 6549e5ffb..33cf7983c 100755 --- a/src/AliasVault.Client/entrypoint.sh +++ b/src/AliasVault.Client/entrypoint.sh @@ -1,6 +1,6 @@ #!/bin/sh # Set the default API URL for localhost debugging -DEFAULT_API_URL="http://localhost:81" +DEFAULT_API_URL="https://localhost:4430" DEFAULT_PRIVATE_EMAIL_DOMAINS="localmail.tld" DEFAULT_SUPPORT_EMAIL="" @@ -9,11 +9,24 @@ API_URL=${API_URL:-$DEFAULT_API_URL} PRIVATE_EMAIL_DOMAINS=${PRIVATE_EMAIL_DOMAINS:-$DEFAULT_PRIVATE_EMAIL_DOMAINS} SUPPORT_EMAIL=${SUPPORT_EMAIL:-$DEFAULT_SUPPORT_EMAIL} +# Create SSL directory if it doesn't exist +mkdir -p /etc/nginx/ssl + +# Generate self-signed SSL certificate if not exists +if [ ! -f /etc/nginx/ssl/nginx.crt ] || [ ! -f /etc/nginx/ssl/nginx.key ]; then + echo "Generating new SSL certificate..." + openssl req -x509 -nodes -days 365 -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" + + # Set proper permissions + chmod 644 /etc/nginx/ssl/nginx.crt + chmod 600 /etc/nginx/ssl/nginx.key +fi + # Replace the default URL with the actual API URL sed -i "s|http://localhost:5092|${API_URL}|g" /usr/share/nginx/html/appsettings.json -# Replace the default SMTP allowed domains with the actual allowed SMTP domains -# Note: this is used so the client knows which email addresses should be registered with the AliasVault server -# in order to be able to receive emails. # Convert comma-separated list to JSON array json_array=$(echo $PRIVATE_EMAIL_DOMAINS | awk '{split($0,a,","); printf "["; for(i=1;i<=length(a);i++) {printf "\"%s\"", a[i]; if(i