#!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: ProxmoxVED Community # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://aliasvault.net source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nginx \ python3 \ build-essential \ gettext-base \ inotify-tools \ libkrb5-3 \ libgssapi-krb5-2 \ openssl # Ensure cc linker is available — update-alternatives may not run in minimal LXC [[ ! -e /usr/bin/cc ]] && ln -sf /usr/bin/gcc /usr/local/bin/cc msg_ok "Installed Dependencies" setup_rust fetch_and_deploy_gh_release "wasm-pack" "rustwasm/wasm-pack" "prebuild" "latest" "/usr/local/bin" "wasm-pack-v*-x86_64-unknown-linux-musl.tar.gz" NODE_VERSION="20" setup_nodejs msg_info "Installing .NET SDK 10.0" setup_deb822_repo "microsoft-prod" \ "https://packages.microsoft.com/keys/microsoft.asc" \ "https://packages.microsoft.com/debian/12/prod" \ "bookworm" \ "main" \ "amd64" $STD apt install -y dotnet-sdk-10.0 msg_ok "Installed .NET SDK 10.0" PG_VERSION="16" setup_postgresql PG_DB_NAME="aliasvault" PG_DB_USER="aliasvault" setup_postgresql_db fetch_and_deploy_gh_release "aliasvault" "aliasvault/aliasvault" "tarball" msg_info "Building Core Libraries (Patience)" source "$HOME/.cargo/env" $STD rustup target add wasm32-unknown-unknown cd /opt/aliasvault/core $STD bash build-and-distribute.sh --browser msg_ok "Built Core Libraries" msg_info "Copying Core Artifacts" mkdir -p /opt/aliasvault/apps/server/AliasVault.Client/wwwroot/wasm cp /opt/aliasvault/core/rust/dist/wasm/aliasvault_core_bg.wasm \ /opt/aliasvault/apps/server/AliasVault.Client/wwwroot/wasm/ cp /opt/aliasvault/core/rust/dist/wasm/aliasvault_core.js \ /opt/aliasvault/apps/server/AliasVault.Client/wwwroot/wasm/ mkdir -p /opt/aliasvault/apps/server/AliasVault.Client/wwwroot/js/dist/core/{identity-generator,password-generator,vault} cp -r /opt/aliasvault/core/typescript/identity-generator/dist/. \ /opt/aliasvault/apps/server/AliasVault.Client/wwwroot/js/dist/core/identity-generator/ cp -r /opt/aliasvault/core/typescript/password-generator/dist/. \ /opt/aliasvault/apps/server/AliasVault.Client/wwwroot/js/dist/core/password-generator/ cp -r /opt/aliasvault/core/vault/dist/. \ /opt/aliasvault/apps/server/AliasVault.Client/wwwroot/js/dist/core/vault/ msg_ok "Copied Core Artifacts" msg_info "Building AliasVault Applications (Patience)" cd /opt/aliasvault/apps/server $STD dotnet workload install wasm-tools $STD dotnet restore aliasvault.sln $STD dotnet publish AliasVault.Api/AliasVault.Api.csproj \ -c Release -o /opt/aliasvault/api --no-restore $STD dotnet build AliasVault.Client/AliasVault.Client.csproj \ -c Release --no-restore $STD dotnet publish AliasVault.Client/AliasVault.Client.csproj \ -c Release -o /opt/aliasvault/client --no-restore # Clear the hardcoded localhost:5092 API URL so the client uses its own origin + /api/ # Also remove pre-compressed copies so nginx (gzip_static on) serves the patched file python3 -c " import json, pathlib p = pathlib.Path('/opt/aliasvault/client/wwwroot/appsettings.json') c = json.loads(p.read_text()); c['ApiUrl'] = ''; p.write_text(json.dumps(c, indent=2)) for ext in ['.gz', '.br']: q = pathlib.Path(str(p) + ext) if q.exists(): q.unlink() " $STD dotnet publish AliasVault.Admin/AliasVault.Admin.csproj \ -c Release -o /opt/aliasvault/admin --no-restore $STD dotnet publish Services/AliasVault.SmtpService/AliasVault.SmtpService.csproj \ -c Release -o /opt/aliasvault/smtp --no-restore $STD dotnet publish Services/AliasVault.TaskRunner/AliasVault.TaskRunner.csproj \ -c Release -o /opt/aliasvault/taskrunner --no-restore $STD dotnet publish Utilities/AliasVault.InstallCli/AliasVault.InstallCli.csproj \ -c Release -o /opt/aliasvault/installcli --no-restore msg_ok "Built AliasVault Applications" msg_info "Generating Secrets and Configuration" ADMIN_PASS=$(openssl rand -base64 12 | tr -dc 'a-zA-Z0-9' | head -c 16) ADMIN_HASH=$(dotnet /opt/aliasvault/installcli/AliasVault.InstallCli.dll hash-password "$ADMIN_PASS") ADMIN_GENERATED=$(date -u +"%Y-%m-%dT%H:%M:%SZ") JWT_KEY=$(openssl rand -base64 32) DATA_PROTECTION_CERT_PASS=$(openssl rand -base64 32) DB_CONN="Host=localhost;Port=5432;Database=aliasvault;Username=aliasvault;Password=${PG_DB_PASS};Maximum Pool Size=80;Minimum Pool Size=5" cat </opt/aliasvault/.env ConnectionStrings__AliasServerDbContext=${DB_CONN} JWT_KEY=${JWT_KEY} DATA_PROTECTION_CERT_PASS=${DATA_PROTECTION_CERT_PASS} ADMIN_PASSWORD_HASH=${ADMIN_HASH} ADMIN_PASSWORD_GENERATED=${ADMIN_GENERATED} PUBLIC_REGISTRATION_ENABLED=true IP_LOGGING_ENABLED=true PRIVATE_EMAIL_DOMAINS= HIDDEN_PRIVATE_EMAIL_DOMAINS= MAX_UPLOAD_SIZE_MB=100 SMTP_TLS_ENABLED=false Logging__LogLevel__Default=Error Logging__LogLevel__Microsoft__Hosting__Lifetime=Error Logging__LogLevel__Microsoft=Error EOF chmod 600 /opt/aliasvault/.env msg_ok "Generated Secrets and Configuration" msg_info "Generating SSL Certificate" mkdir -p /opt/aliasvault/certificates/ssl openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ -keyout /opt/aliasvault/certificates/ssl/key.pem \ -out /opt/aliasvault/certificates/ssl/cert.pem \ -subj "/C=US/ST=State/L=City/O=AliasVault/CN=${LOCAL_IP}" \ -addext "subjectAltName=IP:${LOCAL_IP},DNS:localhost,IP:127.0.0.1" \ 2>/dev/null chmod 600 /opt/aliasvault/certificates/ssl/key.pem chmod 644 /opt/aliasvault/certificates/ssl/cert.pem msg_ok "Generated SSL Certificate" msg_info "Configuring Nginx" rm -f /etc/nginx/sites-enabled/default cat <<'NGINXEOF' >/etc/nginx/sites-available/aliasvault map $http_upgrade $connection_upgrade { default upgrade; '' close; } upstream aliasvault_api { server 127.0.0.1:3001 max_fails=1 fail_timeout=5s; } upstream aliasvault_admin { server 127.0.0.1:3002 max_fails=1 fail_timeout=5s; } server { listen 80; listen [::]:80; server_name _; return 301 https://$host$request_uri; } server { listen 443 ssl; listen [::]:443 ssl; server_name _; ssl_certificate /opt/aliasvault/certificates/ssl/cert.pem; ssl_certificate_key /opt/aliasvault/certificates/ssl/key.pem; ssl_protocols TLSv1.2 TLSv1.3; client_max_body_size 100M; 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; include /etc/nginx/mime.types; default_type application/octet-stream; # .mjs files must be served as text/javascript for dynamic import() to work types { application/javascript mjs; } gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/wasm; # API location /api { proxy_pass http://aliasvault_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; proxy_intercept_errors on; error_page 502 503 504 =503 @unavailable; } # Admin (Blazor Server — needs WebSocket) location /admin { proxy_pass http://aliasvault_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/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_read_timeout 86400; proxy_intercept_errors on; error_page 502 503 504 =503 @unavailable; } # Blazor WASM client (static files) root /opt/aliasvault/client/wwwroot; location / { gzip_static on; try_files $uri $uri/ /index.html =404; } location @unavailable { return 503 "Service temporarily unavailable"; } } NGINXEOF ln -sf /etc/nginx/sites-available/aliasvault /etc/nginx/sites-enabled/aliasvault $STD nginx -t systemctl enable -q --now nginx $STD nginx -s reload msg_ok "Configured Nginx" mkdir -p /opt/certificates/app msg_info "Creating Services" cat </etc/systemd/system/aliasvault-api.service [Unit] Description=AliasVault API After=network.target postgresql.service Requires=postgresql.service [Service] Type=simple User=root WorkingDirectory=/opt/aliasvault/api EnvironmentFile=/opt/aliasvault/.env Environment=ASPNETCORE_URLS=http://127.0.0.1:3001 Environment=ASPNETCORE_PATHBASE=/api ExecStart=/usr/bin/dotnet AliasVault.Api.dll Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/aliasvault-admin.service [Unit] Description=AliasVault Admin After=network.target aliasvault-api.service Wants=aliasvault-api.service [Service] Type=simple User=root WorkingDirectory=/opt/aliasvault/admin EnvironmentFile=/opt/aliasvault/.env Environment=ASPNETCORE_URLS=http://127.0.0.1:3002 Environment=ASPNETCORE_PATHBASE=/admin ExecStart=/usr/bin/dotnet AliasVault.Admin.dll Restart=on-failure RestartSec=5 StartLimitIntervalSec=0 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/aliasvault-smtp.service [Unit] Description=AliasVault SMTP Service After=network.target aliasvault-api.service Requires=aliasvault-api.service [Service] Type=simple User=root WorkingDirectory=/opt/aliasvault/smtp EnvironmentFile=/opt/aliasvault/.env ExecStart=/usr/bin/dotnet AliasVault.SmtpService.dll Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/aliasvault-taskrunner.service [Unit] Description=AliasVault Task Runner After=network.target aliasvault-api.service Requires=aliasvault-api.service [Service] Type=simple User=root WorkingDirectory=/opt/aliasvault/taskrunner EnvironmentFile=/opt/aliasvault/.env ExecStart=/usr/bin/dotnet AliasVault.TaskRunner.dll Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now aliasvault-api aliasvault-admin aliasvault-smtp aliasvault-taskrunner msg_ok "Created Services" { echo "" echo "AliasVault Admin Credentials:" echo " Username: admin" echo " Password: ${ADMIN_PASS}" } >>~/aliasvault.creds motd_ssh customize cleanup_lxc