From df6de32a4a80427d627f408147fb84abe66d1bb3 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Fri, 15 Nov 2024 11:55:26 +0100 Subject: [PATCH 01/12] 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 Date: Fri, 15 Nov 2024 12:38:09 +0100 Subject: [PATCH 02/12] Fix AliasVault.InstallCli dockerfile names (#364) --- .gitignore | 6 +++--- .../AliasVault.InstallCli.csproj | 4 ++-- src/Utilities/AliasVault.InstallCli/Dockerfile | 14 +++++++------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 015874678..a3b152ea8 100644 --- a/.gitignore +++ b/.gitignore @@ -394,6 +394,6 @@ src/Tests/AliasVault.E2ETests/appsettings.Development.json *.drawio.* # Certificates -certificates/*.crt -certificates/*.key -certificates/*.pfx +certificates/**/*.crt +certificates/**/*.key +certificates/**/*.pfx diff --git a/src/Utilities/AliasVault.InstallCli/AliasVault.InstallCli.csproj b/src/Utilities/AliasVault.InstallCli/AliasVault.InstallCli.csproj index 23968baf9..727100b73 100644 --- a/src/Utilities/AliasVault.InstallCli/AliasVault.InstallCli.csproj +++ b/src/Utilities/AliasVault.InstallCli/AliasVault.InstallCli.csproj @@ -9,12 +9,12 @@ - bin\Debug\net9.0\InitializationCLI.xml + bin\Debug\net9.0\AliasVault.InstallCli.xml true - bin\Release\net9.0\InitializationCLI.xml + bin\Release\net9.0\AliasVault.InstallCli.xml true diff --git a/src/Utilities/AliasVault.InstallCli/Dockerfile b/src/Utilities/AliasVault.InstallCli/Dockerfile index 864eb1f56..1212444d0 100644 --- a/src/Utilities/AliasVault.InstallCli/Dockerfile +++ b/src/Utilities/AliasVault.InstallCli/Dockerfile @@ -1,28 +1,28 @@ -FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base +FROM mcr.microsoft.com/dotnet/runtime:9.0 AS base WORKDIR /app -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build ARG BUILD_CONFIGURATION=Release WORKDIR /src # Copy csproj files and restore as distinct layers -COPY ["src/Utilities/InitializationCLI/InitializationCLI.csproj", "src/Utilities/InitializationCLI/"] +COPY ["src/Utilities/AliasVault.InstallCli/AliasVault.InstallCli.csproj", "src/Utilities/AliasVault.InstallCli/"] COPY ["src/Databases/AliasServerDb/AliasServerDb.csproj", "src/Databases/AliasServerDb/"] -RUN dotnet restore "src/Utilities/InitializationCLI/InitializationCLI.csproj" +RUN dotnet restore "src/Utilities/AliasVault.InstallCli/AliasVault.InstallCli.csproj" # Copy the entire source code COPY . . # Build the project -RUN dotnet build "src/Utilities/InitializationCLI/InitializationCLI.csproj" \ +RUN dotnet build "src/Utilities/AliasVault.InstallCli/AliasVault.InstallCli.csproj" \ -c "$BUILD_CONFIGURATION" -o /app/build FROM build AS publish ARG BUILD_CONFIGURATION=Release -RUN dotnet publish "src/Utilities/InitializationCLI/InitializationCLI.csproj" \ +RUN dotnet publish "src/Utilities/AliasVault.InstallCli/AliasVault.InstallCli.csproj" \ -c "$BUILD_CONFIGURATION" -o /app/publish /p:UseAppHost=false FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "InitializationCLI.dll"] +ENTRYPOINT ["dotnet", "AliasVault.InstallCli.dll"] From 0dcc77eb0d89033341b27de85f023b3ae3c00bc4 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Fri, 15 Nov 2024 12:50:53 +0100 Subject: [PATCH 03/12] Update docker-compose-build.yml (#364) --- .github/workflows/docker-compose-build.yml | 45 ++++++++++++++-------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/.github/workflows/docker-compose-build.yml b/.github/workflows/docker-compose-build.yml index 85656da2f..c35985177 100644 --- a/.github/workflows/docker-compose-build.yml +++ b/.github/workflows/docker-compose-build.yml @@ -32,13 +32,13 @@ jobs: run: | # Wait for a few seconds sleep 10 - - name: Test if localhost:80 (WASM app) responds + - name: Test if localhost:443 (WASM app) responds uses: nick-fields/retry@v3 with: timeout_minutes: 2 max_attempts: 3 command: | - http_code=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:80) + http_code=$(curl -s -o /dev/null -w "%{http_code}" https://localhost:443) if [ "$http_code" -ne 200 ]; then echo "Service did not respond with 200 OK. Check if client app is configured correctly." exit 1 @@ -46,13 +46,13 @@ jobs: echo "Service responded with 200 OK" fi - - name: Test if localhost:81 (WebApi) responds + - name: Test if localhost:4430 (WebApi) responds uses: nick-fields/retry@v3 with: timeout_minutes: 2 max_attempts: 3 command: | - http_code=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:81) + http_code=$(curl -s -o /dev/null -w "%{http_code}" https://localhost:4430) if [ "$http_code" -ne 200 ]; then echo "Service did not respond with expected 200 OK. Check if WebApi is configured correctly." exit 1 @@ -60,6 +60,20 @@ jobs: echo "Service responded with $http_code" fi + - name: Test if localhost:4431 (Admin) responds + uses: nick-fields/retry@v3 + with: + timeout_minutes: 2 + max_attempts: 3 + command: | + http_code=$(curl -s -o /dev/null -w "%{http_code}" https://localhost:4431/user/login) + if [ "$http_code" -ne 200 ]; then + echo "Service did not respond with expected 200 OK. Check if admin app is configured correctly." + exit 1 + else + echo "Service responded with $http_code" + fi + - name: Test if localhost:2525 (SmtpService) responds uses: nick-fields/retry@v3 with: @@ -73,16 +87,13 @@ jobs: echo "SmtpService responded on port 2525" fi - - name: Test if localhost:8080 (Admin) responds - uses: nick-fields/retry@v3 - with: - timeout_minutes: 2 - max_attempts: 3 - command: | - http_code=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/user/login) - if [ "$http_code" -ne 200 ]; then - echo "Service did not respond with expected 200 OK. Check if admin app is configured correctly." - exit 1 - else - echo "Service responded with $http_code" - fi + - name: Test install.sh --reset-password output + run: | + output=$(./install.sh --reset-password) + if ! echo "$output" | grep -E '^Password: [a-zA-Z0-9]{8,}$'; then + echo "Password reset output format is incorrect. Expected format: 'Password: '" + echo "Actual output: $output" + exit 1 + else + echo "Password reset output format is correct" + fi From 50a18dc461327ed922a691a7b141043fda611377 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Fri, 15 Nov 2024 13:19:50 +0100 Subject: [PATCH 04/12] Add -k flag to ignore self-signed certs, refactor (#364) --- .github/workflows/docker-compose-build.yml | 6 +++--- src/AliasVault.Admin/Dockerfile | 4 ++-- src/AliasVault.Api/Dockerfile | 4 ++-- src/AliasVault.Client/Dockerfile | 5 ++--- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/docker-compose-build.yml b/.github/workflows/docker-compose-build.yml index c35985177..5c92d0d32 100644 --- a/.github/workflows/docker-compose-build.yml +++ b/.github/workflows/docker-compose-build.yml @@ -38,7 +38,7 @@ jobs: timeout_minutes: 2 max_attempts: 3 command: | - http_code=$(curl -s -o /dev/null -w "%{http_code}" https://localhost:443) + http_code=$(curl -k -s -o /dev/null -w "%{http_code}" https://localhost:443) if [ "$http_code" -ne 200 ]; then echo "Service did not respond with 200 OK. Check if client app is configured correctly." exit 1 @@ -52,7 +52,7 @@ jobs: timeout_minutes: 2 max_attempts: 3 command: | - http_code=$(curl -s -o /dev/null -w "%{http_code}" https://localhost:4430) + http_code=$(curl -k -s -o /dev/null -w "%{http_code}" https://localhost:4430) if [ "$http_code" -ne 200 ]; then echo "Service did not respond with expected 200 OK. Check if WebApi is configured correctly." exit 1 @@ -66,7 +66,7 @@ jobs: timeout_minutes: 2 max_attempts: 3 command: | - http_code=$(curl -s -o /dev/null -w "%{http_code}" https://localhost:4431/user/login) + http_code=$(curl -k -s -o /dev/null -w "%{http_code}" https://localhost:4431/user/login) if [ "$http_code" -ne 200 ]; then echo "Service did not respond with expected 200 OK. Check if admin app is configured correctly." exit 1 diff --git a/src/AliasVault.Admin/Dockerfile b/src/AliasVault.Admin/Dockerfile index d49be3ca3..3c0574358 100644 --- a/src/AliasVault.Admin/Dockerfile +++ b/src/AliasVault.Admin/Dockerfile @@ -27,9 +27,9 @@ COPY --from=publish /app/publish . COPY /src/AliasVault.Admin/entrypoint.sh /app # Create SSL directory -RUN mkdir -p /app/ssl && chmod 755 /app/ssl +RUN mkdir -p /app/ssl && chmod 755 /app/ssl \ +&& chmod +x /app/entrypoint.sh -RUN chmod +x /app/entrypoint.sh EXPOSE 4431 ENV ASPNETCORE_URLS=https://+:4431 ENTRYPOINT ["/app/entrypoint.sh"] diff --git a/src/AliasVault.Api/Dockerfile b/src/AliasVault.Api/Dockerfile index 31f3aeeb1..3ffdfd806 100644 --- a/src/AliasVault.Api/Dockerfile +++ b/src/AliasVault.Api/Dockerfile @@ -26,9 +26,9 @@ 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 mkdir -p /app/ssl && chmod 755 /app/ssl \ +&& chmod +x /app/entrypoint.sh -RUN chmod +x /app/entrypoint.sh EXPOSE 4430 ENV ASPNETCORE_URLS=https://+:4430 ENTRYPOINT ["/app/entrypoint.sh"] diff --git a/src/AliasVault.Client/Dockerfile b/src/AliasVault.Client/Dockerfile index ba5617745..35ac3bdf1 100644 --- a/src/AliasVault.Client/Dockerfile +++ b/src/AliasVault.Client/Dockerfile @@ -40,9 +40,8 @@ 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 +RUN mkdir -p /etc/nginx/ssl && chmod 755 /etc/nginx/ssl \ +&& chmod +x /app/entrypoint.sh EXPOSE 80 443 ENV ASPNETCORE_URLS=http://+:80;https://+:443 From 970d334b59f9dbc8d20d08c53e8159f7e6be1e8b Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Fri, 15 Nov 2024 15:33:23 +0100 Subject: [PATCH 05/12] Make all apps available through single container and HTTPS port (#364) --- .env.example | 2 +- .gitignore | 1 + Dockerfile | 15 ++++ docker-compose.yml | 49 +++++++---- entrypoint.sh | 20 +++++ install.sh | 28 +++--- nginx.conf | 87 +++++++++++++++++++ .../Auth/Components/Logo.razor | 9 +- src/AliasVault.Admin/Dockerfile | 17 +--- src/AliasVault.Admin/Main/App.razor | 2 +- .../Main/Layout/TopMenu.razor | 12 +-- src/AliasVault.Admin/Main/Pages/Home.razor | 2 +- src/AliasVault.Admin/Program.cs | 18 ++++ src/AliasVault.Api/Dockerfile | 15 +--- src/AliasVault.Client/Dockerfile | 23 ++--- src/AliasVault.Client/entrypoint.sh | 12 +-- src/AliasVault.Client/nginx.conf | 15 +--- .../Headings/Breadcrumb.razor | 6 +- 18 files changed, 226 insertions(+), 107 deletions(-) create mode 100644 Dockerfile create mode 100644 entrypoint.sh create mode 100644 nginx.conf diff --git a/.env.example b/.env.example index ca1cb3fbe..4984f71e3 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -API_URL= +HOSTNAME= JWT_KEY= DATA_PROTECTION_CERT_PASS= ADMIN_PASSWORD_HASH= diff --git a/.gitignore b/.gitignore index a3b152ea8..65dce725a 100644 --- a/.gitignore +++ b/.gitignore @@ -397,3 +397,4 @@ src/Tests/AliasVault.E2ETests/appsettings.Development.json certificates/**/*.crt certificates/**/*.key certificates/**/*.pfx +certificates/**/*.pem diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..1d37a84c6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM nginx:alpine + +# Install OpenSSL for certificate generation +RUN apk add --no-cache openssl + +# Copy configuration and entrypoint script +COPY nginx.conf /etc/nginx/nginx.conf +COPY entrypoint.sh /docker-entrypoint.sh + +# Create SSL directory +RUN mkdir -p /etc/nginx/ssl && chmod 755 /etc/nginx/ssl \ + && chmod +x /docker-entrypoint.sh + +EXPOSE 80 443 +ENTRYPOINT ["/docker-entrypoint.sh"] diff --git a/docker-compose.yml b/docker-compose.yml index 17e809381..bef727fb9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,30 +1,29 @@ services: - admin: - image: aliasvault-admin + nginx: build: context: . - dockerfile: src/AliasVault.Admin/Dockerfile + dockerfile: Dockerfile ports: - - "4431:4431" + - "80:80" + - "443:443" volumes: - - ./certificates/app:/certificates:rw - - ./certificates/ssl/admin:/app/ssl:rw - - ./database:/database:rw - - ./logs:/logs:rw + - ./certificates/ssl:/etc/nginx/ssl:rw + depends_on: + - admin + - client + - api + - smtp restart: always - env_file: - - .env client: image: aliasvault-client build: context: . dockerfile: src/AliasVault.Client/Dockerfile - ports: - - "80:80" - - "443:443" volumes: - - ./certificates/ssl/client:/etc/nginx/ssl:rw + - ./logs/msbuild:/app/msbuild-logs:rw + expose: + - "3000" restart: always env_file: - .env @@ -34,17 +33,31 @@ services: build: context: . dockerfile: src/AliasVault.Api/Dockerfile - ports: - - "4430:4430" + expose: + - "3001" volumes: - - ./certificates/app:/certificates:rw - - ./certificates/ssl/api:/app/ssl:rw - ./database:/database:rw + - ./certificates/app:/certificates:rw - ./logs:/logs:rw env_file: - .env restart: always + admin: + image: aliasvault-admin + build: + context: . + dockerfile: src/AliasVault.Admin/Dockerfile + expose: + - "3002" + volumes: + - ./database:/database:rw + - ./certificates/app:/certificates:rw + - ./logs:/logs:rw + restart: always + env_file: + - .env + smtp: image: aliasvault-smtp build: diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 000000000..4fb1e2875 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# 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/cert.pem ] || [ ! -f /etc/nginx/ssl/key.pem ]; then + echo "Generating new SSL certificate..." + openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout /etc/nginx/ssl/key.pem \ + -out /etc/nginx/ssl/cert.pem \ + -subj "/C=US/ST=State/L=City/O=Organization/CN=localhost" + + # Set proper permissions + chmod 644 /etc/nginx/ssl/cert.pem + chmod 600 /etc/nginx/ssl/key.pem +fi + +# Start nginx +nginx -g "daemon off;" \ No newline at end of file diff --git a/install.sh b/install.sh index 44776b8b5..05d19e542 100755 --- a/install.sh +++ b/install.sh @@ -140,22 +140,22 @@ create_env_file() { fi } -# 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="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 - awk -v url="$API_URL" '/^API_URL=/ {$0="API_URL="url} 1' "$ENV_FILE" > "$ENV_FILE.tmp" && mv "$ENV_FILE.tmp" "$ENV_FILE" +# Function to populate HOSTNAME +populate_hostname() { + printf "${CYAN}> Checking HOSTNAME...${NC}\n" + if ! grep -q "^HOSTNAME=" "$ENV_FILE" || [ -z "$(grep "^HOSTNAME=" "$ENV_FILE" | cut -d '=' -f2)" ]; then + DEFAULT_HOSTNAME="localhost" + read -p "Enter the hostname where AliasVault will be hosted (press Enter for default: $DEFAULT_HOSTNAME): " USER_HOSTNAME + HOSTNAME=${USER_HOSTNAME:-$DEFAULT_HOSTNAME} + if grep -q "^HOSTNAME=" "$ENV_FILE"; then + awk -v hostname="$HOSTNAME" '/^HOSTNAME=/ {$0="HOSTNAME="hostname} 1' "$ENV_FILE" > "$ENV_FILE.tmp" && mv "$ENV_FILE.tmp" "$ENV_FILE" else - echo "API_URL=${API_URL}" >> "$ENV_FILE" + echo "HOSTNAME=${HOSTNAME}" >> "$ENV_FILE" fi - printf "${GREEN}> API_URL has been set to $API_URL in $ENV_FILE.${NC}\n" + printf "${GREEN}> HOSTNAME has been set to $HOSTNAME in $ENV_FILE.${NC}\n" else - API_URL=$(grep "^API_URL=" "$ENV_FILE" | cut -d '=' -f2) - printf "${GREEN}> API_URL already exists in $ENV_FILE with value: $API_URL${NC}\n" + HOSTNAME=$(grep "^HOSTNAME=" "$ENV_FILE" | cut -d '=' -f2) + printf "${GREEN}> HOSTNAME already exists in $ENV_FILE with value: $HOSTNAME${NC}\n" fi } @@ -359,7 +359,7 @@ main() { printf "${YELLOW}+++ Initializing .env file +++${NC}\n" printf "\n" create_env_file || exit $? - populate_api_url || exit $? + populate_hostname || exit $? populate_jwt_key || exit $? populate_data_protection_cert_pass || exit $? set_private_email_domains || exit $? diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 000000000..5e798eec6 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,87 @@ +events { + worker_connections 1024; +} + +http { + upstream client { + server client:3000; + } + + upstream api { + server api:3001; + } + + upstream admin { + server admin:3002; + } + + # 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 _; + return 301 https://$host$request_uri; + } + + server { + listen 443 ssl; + server_name _; + + ssl_certificate /etc/nginx/ssl/cert.pem; + ssl_certificate_key /etc/nginx/ssl/key.pem; + + # Client app (root path) + location / { + proxy_pass http://client; + proxy_set_header Host $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; + } + + # Admin interface + location /admin { + proxy_pass http://admin; + proxy_set_header Host $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; + + # 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; + } + + # API endpoints + location /api { + rewrite ^/api(.*)$ $1 break; + proxy_pass http://api; + proxy_set_header Host $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 /api; + } + } +} \ No newline at end of file diff --git a/src/AliasVault.Admin/Auth/Components/Logo.razor b/src/AliasVault.Admin/Auth/Components/Logo.razor index af5733c11..c3eb1e7dd 100644 --- a/src/AliasVault.Admin/Auth/Components/Logo.razor +++ b/src/AliasVault.Admin/Auth/Components/Logo.razor @@ -1,9 +1,10 @@ - +@using AliasVault.Admin.Services +@inject NavigationService NavigationService + +
AliasVault AliasVault
-
- - + \ No newline at end of file diff --git a/src/AliasVault.Admin/Dockerfile b/src/AliasVault.Admin/Dockerfile index 3c0574358..65dcfbb13 100644 --- a/src/AliasVault.Admin/Dockerfile +++ b/src/AliasVault.Admin/Dockerfile @@ -1,22 +1,18 @@ FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base WORKDIR /app -EXPOSE 4431 +EXPOSE 3002 FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build ARG BUILD_CONFIGURATION=Release WORKDIR /src - -# Copy the project files and restore dependencies COPY ["src/AliasVault.Admin/AliasVault.Admin.csproj", "src/AliasVault.Admin/"] RUN dotnet restore "src/AliasVault.Admin/AliasVault.Admin.csproj" COPY . . -# Build the WebApi project WORKDIR "/src/src/AliasVault.Admin" RUN dotnet build "AliasVault.Admin.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.Admin.csproj" -c "$BUILD_CONFIGURATION" -o /app/publish /p:UseAppHost=false @@ -24,12 +20,7 @@ RUN dotnet publish "AliasVault.Admin.csproj" -c "$BUILD_CONFIGURATION" -o /app/p FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -COPY /src/AliasVault.Admin/entrypoint.sh /app -# Create SSL directory -RUN mkdir -p /app/ssl && chmod 755 /app/ssl \ -&& chmod +x /app/entrypoint.sh - -EXPOSE 4431 -ENV ASPNETCORE_URLS=https://+:4431 -ENTRYPOINT ["/app/entrypoint.sh"] +ENV ASPNETCORE_URLS=http://+:3002 +ENV ASPNETCORE_PATHBASE=/admin +ENTRYPOINT ["dotnet", "AliasVault.Admin.dll"] diff --git a/src/AliasVault.Admin/Main/App.razor b/src/AliasVault.Admin/Main/App.razor index 0ab53e4cd..bd46984a5 100644 --- a/src/AliasVault.Admin/Main/App.razor +++ b/src/AliasVault.Admin/Main/App.razor @@ -5,7 +5,7 @@ - + diff --git a/src/AliasVault.Admin/Main/Layout/TopMenu.razor b/src/AliasVault.Admin/Main/Layout/TopMenu.razor index 496b41fd5..e76379b53 100644 --- a/src/AliasVault.Admin/Main/Layout/TopMenu.razor +++ b/src/AliasVault.Admin/Main/Layout/TopMenu.razor @@ -5,7 +5,7 @@