A proxmox install script for Vaultwarden

shodan

Active Member
Sep 1, 2022
184
56
33
Hello,

I don't know where else to post this.

But I just created a proxmox install script to create a Vaultwarden server in an LXC container, inside of a debian LXC template

Code:
#----------------------------------------
# stop in case of errors
#set -e # this breaks on or soon after pct create 
# shell function to create --net variable
addnet() { CT_net_count=${CT_net_count:-0}; local net_name=$1; shift; local _CT_new_config="--net$CT_net_count name=$net_name"; local valid_params=("bridge" "firewall" "gw" "gw6" "hwaddr" "ip" "ip6" "link_down" "mtu" "rate" "tag" "trunks" "type"); while [ $# -gt 0 ]; do key=$1; value=$2; if [[ " ${valid_params[*]} " =~ " $key " ]]; then _CT_new_config="$_CT_new_config,$key=$value"; shift 2; else shift 1; fi; done; _CT_net_config="$_CT_net_config $_CT_new_config"; echo "Interface added: $_CT_new_config"; CT_net_count=$((CT_net_count + 1)); }
# shell function to create user variables
adduser() { user_ubound=$((user_ubound + 1)); local user_and_group=$1; IFS=':' read -r username group <<< "$user_and_group"; eval "user_name_${user_ubound}=\"$username\""; [ -n "$group" ] && eval "user_group_${user_ubound}=\"$group\""; shift 1; while [ $# -gt 0 ]; do case $1 in nologin) eval "user_shell_${user_ubound}='/usr/sbin/nologin'" ;; shell) shift 1; eval "user_shell_${user_ubound}=\"$1\"" ;; groups) shift 1; eval "user_groups_${user_ubound}=\"$1\"" ;; nopassword) eval "user_password_${user_ubound}=$(printf '%s' '!')" ;; password) shift 1; eval "user_password_${user_ubound}=\"$1\"" ;; lock) eval "user_lock_${user_ubound}=true" ;; *) echo "Warning: Unrecognized option '$1'" ;; esac; shift 1; done; }
createuser() { local i=$1; local username=$(eval echo "\$user_name_${i}"); local group=$(eval echo "\$user_group_${i}"); local shell=$(eval echo "\$user_shell_${i}"); local password=$(eval echo "\$user_password_${i}"); local groups=$(eval echo "\$user_groups_${i}"); local lock=$(eval echo "\$user_lock_${i}"); [ -n "$shell" ] && shell_option="-s $shell" || shell_option="-s /bin/bash"; [ -n "$group" ] && group_option="-g $group" || group_option=""; pct_exec useradd $shell_option $group_option "$username"; [ -n "$password" ] && pct_set_password "$username:$password"; [ "$password" = "!" ] && pct_exec usermod -L "$username"; [ "$lock" = "true" ] && pct_exec usermod -L "$username"; pct_exec mkdir -p "/home/$username"; [ -n "$group" ] && pct_exec chown "$username:$group" "/home/$username" || pct_exec chown "$username" "/home/$username"; [ -n "$groups" ] && pct_exec usermod -aG "$groups" "$username"; unset username group shell password groups lock; }
# shell function for create each file line variables
addline() { file_line_ubound=$((file_line_ubound + 1)); eval "file_line_${file_line_ubound}_${file_ubound}=\"$1\""; eval "file_line_count_${file_ubound}=$file_line_ubound"; }
addfile() { file_ubound=$((file_ubound + 1)); eval "file_name_${file_ubound}=\"$1\""; [ -n "$2" ] && [[ "$2" =~ ^[0-9]+$ ]] && eval "filepermission_${file_ubound}=\"$2\""; [ -n "$3" ] && eval "fileowner_${file_ubound}=\"$3\""; unset file_line_ubound; }
pct_append_text() { local file=$1; local text_or_var=$2; if [ -n "${!text_or_var}" ]; then local text=${!text_or_var}; elif [[ "$text_or_var" == file_line* ]]; then local text=$(eval echo \${$text_or_var}); else local text="$text_or_var"; fi; local command="echo \"$text\" >> \"$file\""; [ "$VERBOSE" -gt 0 ] && echo "pct exec $CT_ID -- /bin/sh -c \"$command\""; pct exec $CT_ID -- /bin/sh -c "$command"; }
writefile() { local file_index=$1; local file_name=$(eval echo \${file_name_${file_index}}); local file_permission=$(eval echo \${filepermission_${file_index}}); local file_owner=$(eval echo \${fileowner_${file_index}}); local file_line_count=$(eval echo \${file_line_count_${file_index}}); pct_exec mkdir -p "$(dirname "$file_name")"; for i in $(seq 1 $file_line_count); do local file_line_var="file_line_${i}_${file_index}"; pct_append_text "$file_name" "$file_line_var"; done; [ -n "$file_permission" ] && pct_exec "chmod $file_permission $file_name"; [ -n "$file_owner" ] && pct_exec "chown $file_owner $file_name"; }
# shell functions for user port binding permissions using authbind
add_port_bind() { port_bind_ubound=$((port_bind_ubound + 1)); eval "port_bind_number_${port_bind_ubound}=\"${1%%:*}\""; eval "port_bind_username_${port_bind_ubound}=\"${1##*:}\""; }
create_port_bind() { [ -z "$authbind_installed" ] && pct_install_package authbind && authbind_installed=1; local i=$1; local port_number=$(eval echo "\$port_bind_number_${i}"); local portbind_username=$(eval echo "\$port_bind_username_${i}"); [ -z "$portbind_username" ] && { echo "Error: Username is empty for port $port_number. Skipping."; return 1; }; pct_exec touch "/etc/authbind/byport/${port_number}"; pct_exec chmod 500 "/etc/authbind/byport/${port_number}"; pct_exec chown "$portbind_username" "/etc/authbind/byport/${port_number}"; unset portbind_username; }
# shell function to type pct exec commands
pct_exec() { [ "$VERBOSE" -gt 0 ] && echo "pct exec $CT_ID -- /bin/sh -c \"$*\""; pct exec $CT_ID -- /bin/sh -c "$*"; }
pct_install_package() { local packages="$*"; case "$CT_os_type" in centos|almalinux|amazonlinux|openeuler|oracle|rockylinux|springdalelinux) install_command="yum install -y" ;; debian|devuan|kali|ubuntu|mint) install_command="apt -qq install -y" ;; alpine) install_command="apk add --quiet" ;; archlinux) install_command="pacman --noconfirm -S" ;; fedora) install_command="dnf install -y" ;; gentoo|funtoo) install_command="emerge --quiet" ;; opensuse) install_command="zypper --quiet install -y" ;; nixos) install_command="nix-env -i" ;; openwrt|busybox) install_command="opkg install" ;; voidlinux) install_command="xbps-install -y" ;; slackware) install_command="slackpkg install" ;; plamo) install_command="pkginstall" ;; alt) install_command="apt-get install -y" ;; *) echo "Error: Unknown or unsupported OS type '$CT_os_type'."; return 1 ;; esac; [ -n "$install_command" ] && pct_exec "$install_command $packages"; }
pct_update_package_manager() { case "$CT_os_type" in centos|almalinux|amazonlinux|openeuler|oracle|rockylinux|springdalelinux) pct_exec "yum -q -y update" ;; debian|devuan|kali|ubuntu|mint) pct_exec "apt -qq update" ;; alpine) pct_exec "apk update" ;; archlinux) pct_exec "pacman -Sy --noconfirm" ;; fedora) pct_exec "dnf -q -y update" ;; gentoo|funtoo) pct_exec "emerge --sync" ;; opensuse) pct_exec "zypper --gpg-auto-import-keys refresh" ;; nixos) pct_exec "nix-channel --update && nix-env -u '*'" ;; openwrt|busybox) pct_exec "opkg update" ;; voidlinux) pct_exec "xbps-install -Sy" ;; slackware) pct_exec "slackpkg update" ;; plamo) pct_exec "pkginstall --update" ;; alt) pct_exec "apt-get update" ;; *) echo "Error: Unknown or unsupported OS type '$CT_os_type'. Cannot update package manager."; return 1 ;; esac; }
pct_set_password() { for user_pass in "$@"; do user=$(echo "$user_pass" | cut -d':' -f1); pass=$(echo "$user_pass" | cut -d':' -f2); pct_exec bash -c "'printf \"%s\n\" \"${user}:${pass}\" | chpasswd'"; done; }
addcommand() { pct_command_ubound=$((pct_command_ubound + 1)); eval "pct_command_${pct_command_ubound}=\"\$*\""; }
runcommand() { local i=$1; eval "pct_exec \${pct_command_${i}}"; }

VERBOSE=1  # Enable verbose mode
[ "$VERBOSE" -gt 0 ] && echo "------------------------------ Creation of debian LXC container ------------------------------"
CT_ID="900"  # Adjust the container ID, doesn't have to be set
CT_hostname="vaultwarden"
CT_network_suffix=".lan"
CT_root_password="qwerty"
CT_memory="1024"
CT_cores="4"
CT_rootfs_size="2"

CT_install_packages="sudo screen wget htop docker-compose"

CT_enable_root_login="true"
CT_template_download="http://download.proxmox.com/images/system/debian-12-standard_12.7-1_amd64.tar.zst"
CT_template_filename="debian-12-standard_12.7-1_amd64.tar.zst"
CT_template_file="local:vztmpl/$CT_template_filename"
CT_key="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8Eg5xSbsyLySMCH5K1eb8ZzLTLwPrXDmgyGh9OAi/kofhR6UrTtuVzViAxBV8i+52pgMkRFoX2q/wDKkX7bJk0HXGzs26Npz40BCOEO6hf8MlTc/Kdu288sxVKPhnMofJ1UGy4vZjy2AHoAEe0NbazoEiBNZNO+EpXAGnaxnSM2KQFDDidZydeFMaGKWPb0wYXnGeKbnjxA2rASbX2Rd515FC5ZkYVAK0KFjRV41q+xJebBDhGhgLpHynusmFINM/RdoUswD+c2lwRpdIL+yU+DIPt4J7pM6h0Tj8hTFlqIzemwKKFi4UkzL53oQFYpCK2qAHiBTHAfOdL8gcF5Kv rsa-key-20240131"
CT_key_file="/ssh_key.$CT_hostname.pub"

addnet eth0 hwaddr "DE:AD:BE:EF:99:55" ip dhcp ip6 manual firewall 0 bridge vmbr0 #link_down 1 # LAN interface (eth0)

adduser user nopassword nologin lock groups docker
add_port_bind "443:user"

addfile "/opt/vaultwarden/docker-compose.yml" "644" "root:root"
addline "version: '3.7'"
addline "services:"
addline "  vaultwarden:"
addline "    image: vaultwarden/server:latest"
addline "    container_name: vaultwarden"
addline "    environment:"
addline "      DOMAIN: 'https://$CT_hostname$CT_network_suffix'"
addline "      ROCKET_PORT: 443"
addline "      ENABLE_SSL: 'true'"
addline "      SIGNUPS_ALLOWED: 'true'"
addline "      ROCKET_TLS: '{certs=/certs/fullchain.pem,key=/certs/privkey.pem}'"
addline "    volumes:"
addline "      - /opt/vaultwarden-data/:/data/"
addline "      - /opt/vaultwarden-certs/:/certs/:ro"
addline "    ports:"
addline "      - 443:443"
addline "    restart: unless-stopped"

addcommand mkdir -p /opt/vaultwarden/ /opt/vaultwarden-certs/ /opt/vaultwarden-data/ 
addcommand openssl req -x509 -newkey rsa:4096 -keyout /opt/vaultwarden-certs/privkey.pem -out /opt/vaultwarden-certs/fullchain.pem -sha256 -days 36500 -nodes -subj '/CN=$CT_hostname$CT_network_suffix'
addcommand sudo -u user docker-compose -f /opt/vaultwarden/docker-compose.yml up -d

# Only download template file if it is not already present
[ ! -f /var/lib/vz/template/cache/$CT_template_filename ] && wget "$CT_template_download" -O /var/lib/vz/template/cache/$CT_template_filename
# Set the right OS Type
case "$CT_template_filename" in *almalinux*|*amazonlinux*|*centos*|*openeuler*|*oracle*|*rockylinux*|*springdalelinux*) CT_os_type="centos" ;; *alpine*) CT_os_type="alpine" ;; *alt*|*busybox*|*plamo*|*slackware*|*voidlinux*|*openwrt*) CT_os_type="unmanaged" ;; *archlinux*) CT_os_type="archlinux" ;; *debian*|*devuan*|*kali*) CT_os_type="debian" ;; *fedora*) CT_os_type="fedora" ;; *funtoo*|*gentoo*) CT_os_type="gentoo" ;; *mint*) CT_os_type="ubuntu" ;; *nixos*) CT_os_type="nixos" ;; *opensuse*) CT_os_type="opensuse" ;; *ubuntu*) CT_os_type="ubuntu" ;; *) CT_os_type="unmanaged" ;; esac
# Obtain the next CT_ID if not already set
: ${CT_ID:=100}; CT_ID=$((CT_ID < 100 ? 100 : CT_ID)); existing_ids=$(pct list | awk 'NR>1 {print $1}' | sort -n); while echo "$existing_ids" | grep -qw "$CT_ID"; do CT_ID=$((CT_ID + 1)); done
# Writing the public key to a file, as pct requires
echo "$CT_key" > $CT_key_file

# Create the container
echo ""; echo Creating LXC Container for $CT_template_filename
[ "$VERBOSE" -gt 0 ] && echo "pct create $CT_ID $CT_template_file --arch amd64 --cores $CT_cores --memory $CT_memory --hostname $CT_hostname $_CT_net_config --rootfs local-lvm:$CT_rootfs_size --features nesting=1 --unprivileged 1 --ostype $CT_os_type"
pct create $CT_ID $CT_template_file --arch amd64 --cores $CT_cores --memory $CT_memory --hostname $CT_hostname $_CT_net_config --rootfs local-lvm:$CT_rootfs_size --features nesting=1 --unprivileged 1 --ostype $CT_os_type

# delete key file
rm $CT_key_file

# Define the LXC configuration file path
LXC_CONF_FILE="/etc/pve/nodes/proxmox/lxc/$CT_ID.conf"

# to set console to shell mode
echo "cmode: shell" >> "$LXC_CONF_FILE"

# to add the mount points
#for i in $(seq 1 $mount_point_ubound); do create_mount_point $i; done

# Start the container
pct start $CT_ID

#Wait until container is finished booting
while [[ $(pct status $CT_ID) != *"running"* ]]; do echo "Waiting for container $CT_ID to start..."; sleep 2; done; echo "Container $CT_ID is running."

sleep 5

# Enable root login via ssh
[ "$CT_enable_root_login" = "true" ] && pct_exec "sed -i 's/^#PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config"
# Enable login with password
[ -n "$CT_root_password" ] && pct_exec "sed -i 's/^#PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config"
[ -n "$CT_root_password" ] && pct_set_password "root:$CT_root_password"
pct_exec systemctl restart sshd

pct_update_package_manager

# Install additional packages if specified
[ -n "$CT_install_packages" ] && pct_install_package $CT_install_packages

# create extra users
[ -n "$user_ubound" ] && for i in $(seq 1 $user_ubound); do createuser $i; done

# write all the files into the container
[ -n "$file_ubound" ] && for i in $(seq 1 $file_ubound); do writefile $i; done

# allow port binding by users if needed
[ -n "$port_bind_ubound" ] && for i in $(seq 1 $port_bind_ubound); do create_port_bind $i; done

# run the commands specified with addcommand
[ -n "$pct_command_ubound" ] && for i in $(seq 1 $pct_command_ubound); do runcommand $i; done
 
Last edited:
  • Like
Reactions: husskii
Unfortunately proxmox support forum doesn't have "copy text" button to copy the whole block

I have just tested it, copy and pasted from the above text

This is meant to be directly pasted into the root console of a proxmox server

It will create an LXC container, ID 900+, hostname vaultwarden.lan

This has self-signed SSL out of the box, but you could easily put your let's encrypt certs in /opt/vaultwarden-certs/ if you want to

You will want to change the following variables before using

CT_root_password
CT_key
and maybe
CT_hostname
CT_network_suffix

1734946131993.png

1734946146784.png

Beauty !
 
Why don't you just install deb file from vaultwarden?
https://github.com/greizgh/vaultwarden-debian

I don't know, the docker is the first thing I found

I agree installing it natively in the LXC would be lighter weight

However, I notice that

greizgh/vaultwarden-debian was last updated in 2021
while dani-garcia/vaultwarden was last updated 3 days ago

Also, in my version, I have setup SSL from rocket, so SSL works out of the box without a reverse proxy
This is important because vaultwarden refuses to work at all on plain HTTP
And I wanted it to work on my .lan
 
Yeah,scripts works okay, nevermind the date. It pulls from dani ,and builds .deb files. But yeah , you would need to install nginx as a reverse proxy.
 
Hello,

I don't know where else to post this.

But I just created a proxmox install script to create a Vaultwarden server in an LXC container, inside of a debian LXC template

Code:
#----------------------------------------
# stop in case of errors
#set -e # this breaks on or soon after pct create
# shell function to create --net variable
addnet() { CT_net_count=${CT_net_count:-0}; local net_name=$1; shift; local _CT_new_config="--net$CT_net_count name=$net_name"; local valid_params=("bridge" "firewall" "gw" "gw6" "hwaddr" "ip" "ip6" "link_down" "mtu" "rate" "tag" "trunks" "type"); while [ $# -gt 0 ]; do key=$1; value=$2; if [[ " ${valid_params[*]} " =~ " $key " ]]; then _CT_new_config="$_CT_new_config,$key=$value"; shift 2; else shift 1; fi; done; _CT_net_config="$_CT_net_config $_CT_new_config"; echo "Interface added: $_CT_new_config"; CT_net_count=$((CT_net_count + 1)); }
# shell function to create user variables
adduser() { user_ubound=$((user_ubound + 1)); local user_and_group=$1; IFS=':' read -r username group <<< "$user_and_group"; eval "user_name_${user_ubound}=\"$username\""; [ -n "$group" ] && eval "user_group_${user_ubound}=\"$group\""; shift 1; while [ $# -gt 0 ]; do case $1 in nologin) eval "user_shell_${user_ubound}='/usr/sbin/nologin'" ;; shell) shift 1; eval "user_shell_${user_ubound}=\"$1\"" ;; groups) shift 1; eval "user_groups_${user_ubound}=\"$1\"" ;; nopassword) eval "user_password_${user_ubound}=$(printf '%s' '!')" ;; password) shift 1; eval "user_password_${user_ubound}=\"$1\"" ;; lock) eval "user_lock_${user_ubound}=true" ;; *) echo "Warning: Unrecognized option '$1'" ;; esac; shift 1; done; }
createuser() { local i=$1; local username=$(eval echo "\$user_name_${i}"); local group=$(eval echo "\$user_group_${i}"); local shell=$(eval echo "\$user_shell_${i}"); local password=$(eval echo "\$user_password_${i}"); local groups=$(eval echo "\$user_groups_${i}"); local lock=$(eval echo "\$user_lock_${i}"); [ -n "$shell" ] && shell_option="-s $shell" || shell_option="-s /bin/bash"; [ -n "$group" ] && group_option="-g $group" || group_option=""; pct_exec useradd $shell_option $group_option "$username"; [ -n "$password" ] && pct_set_password "$username:$password"; [ "$password" = "!" ] && pct_exec usermod -L "$username"; [ "$lock" = "true" ] && pct_exec usermod -L "$username"; pct_exec mkdir -p "/home/$username"; [ -n "$group" ] && pct_exec chown "$username:$group" "/home/$username" || pct_exec chown "$username" "/home/$username"; [ -n "$groups" ] && pct_exec usermod -aG "$groups" "$username"; unset username group shell password groups lock; }
# shell function for create each file line variables
addline() { file_line_ubound=$((file_line_ubound + 1)); eval "file_line_${file_line_ubound}_${file_ubound}=\"$1\""; eval "file_line_count_${file_ubound}=$file_line_ubound"; }
addfile() { file_ubound=$((file_ubound + 1)); eval "file_name_${file_ubound}=\"$1\""; [ -n "$2" ] && [[ "$2" =~ ^[0-9]+$ ]] && eval "filepermission_${file_ubound}=\"$2\""; [ -n "$3" ] && eval "fileowner_${file_ubound}=\"$3\""; unset file_line_ubound; }
pct_append_text() { local file=$1; local text_or_var=$2; if [ -n "${!text_or_var}" ]; then local text=${!text_or_var}; elif [[ "$text_or_var" == file_line* ]]; then local text=$(eval echo \${$text_or_var}); else local text="$text_or_var"; fi; local command="echo \"$text\" >> \"$file\""; [ "$VERBOSE" -gt 0 ] && echo "pct exec $CT_ID -- /bin/sh -c \"$command\""; pct exec $CT_ID -- /bin/sh -c "$command"; }
writefile() { local file_index=$1; local file_name=$(eval echo \${file_name_${file_index}}); local file_permission=$(eval echo \${filepermission_${file_index}}); local file_owner=$(eval echo \${fileowner_${file_index}}); local file_line_count=$(eval echo \${file_line_count_${file_index}}); pct_exec mkdir -p "$(dirname "$file_name")"; for i in $(seq 1 $file_line_count); do local file_line_var="file_line_${i}_${file_index}"; pct_append_text "$file_name" "$file_line_var"; done; [ -n "$file_permission" ] && pct_exec "chmod $file_permission $file_name"; [ -n "$file_owner" ] && pct_exec "chown $file_owner $file_name"; }
# shell functions for user port binding permissions using authbind
add_port_bind() { port_bind_ubound=$((port_bind_ubound + 1)); eval "port_bind_number_${port_bind_ubound}=\"${1%%:*}\""; eval "port_bind_username_${port_bind_ubound}=\"${1##*:}\""; }
create_port_bind() { [ -z "$authbind_installed" ] && pct_install_package authbind && authbind_installed=1; local i=$1; local port_number=$(eval echo "\$port_bind_number_${i}"); local portbind_username=$(eval echo "\$port_bind_username_${i}"); [ -z "$portbind_username" ] && { echo "Error: Username is empty for port $port_number. Skipping."; return 1; }; pct_exec touch "/etc/authbind/byport/${port_number}"; pct_exec chmod 500 "/etc/authbind/byport/${port_number}"; pct_exec chown "$portbind_username" "/etc/authbind/byport/${port_number}"; unset portbind_username; }
# shell function to type pct exec commands
pct_exec() { [ "$VERBOSE" -gt 0 ] && echo "pct exec $CT_ID -- /bin/sh -c \"$*\""; pct exec $CT_ID -- /bin/sh -c "$*"; }
pct_install_package() { local packages="$*"; case "$CT_os_type" in centos|almalinux|amazonlinux|openeuler|oracle|rockylinux|springdalelinux) install_command="yum install -y" ;; debian|devuan|kali|ubuntu|mint) install_command="apt -qq install -y" ;; alpine) install_command="apk add --quiet" ;; archlinux) install_command="pacman --noconfirm -S" ;; fedora) install_command="dnf install -y" ;; gentoo|funtoo) install_command="emerge --quiet" ;; opensuse) install_command="zypper --quiet install -y" ;; nixos) install_command="nix-env -i" ;; openwrt|busybox) install_command="opkg install" ;; voidlinux) install_command="xbps-install -y" ;; slackware) install_command="slackpkg install" ;; plamo) install_command="pkginstall" ;; alt) install_command="apt-get install -y" ;; *) echo "Error: Unknown or unsupported OS type '$CT_os_type'."; return 1 ;; esac; [ -n "$install_command" ] && pct_exec "$install_command $packages"; }
pct_update_package_manager() { case "$CT_os_type" in centos|almalinux|amazonlinux|openeuler|oracle|rockylinux|springdalelinux) pct_exec "yum -q -y update" ;; debian|devuan|kali|ubuntu|mint) pct_exec "apt -qq update" ;; alpine) pct_exec "apk update" ;; archlinux) pct_exec "pacman -Sy --noconfirm" ;; fedora) pct_exec "dnf -q -y update" ;; gentoo|funtoo) pct_exec "emerge --sync" ;; opensuse) pct_exec "zypper --gpg-auto-import-keys refresh" ;; nixos) pct_exec "nix-channel --update && nix-env -u '*'" ;; openwrt|busybox) pct_exec "opkg update" ;; voidlinux) pct_exec "xbps-install -Sy" ;; slackware) pct_exec "slackpkg update" ;; plamo) pct_exec "pkginstall --update" ;; alt) pct_exec "apt-get update" ;; *) echo "Error: Unknown or unsupported OS type '$CT_os_type'. Cannot update package manager."; return 1 ;; esac; }
pct_set_password() { for user_pass in "$@"; do user=$(echo "$user_pass" | cut -d':' -f1); pass=$(echo "$user_pass" | cut -d':' -f2); pct_exec bash -c "'printf \"%s\n\" \"${user}:${pass}\" | chpasswd'"; done; }
addcommand() { pct_command_ubound=$((pct_command_ubound + 1)); eval "pct_command_${pct_command_ubound}=\"\$*\""; }
runcommand() { local i=$1; eval "pct_exec \${pct_command_${i}}"; }

VERBOSE=1  # Enable verbose mode
[ "$VERBOSE" -gt 0 ] && echo "------------------------------ Creation of debian LXC container ------------------------------"
CT_ID="900"  # Adjust the container ID, doesn't have to be set
CT_hostname="vaultwarden"
CT_network_suffix=".lan"
CT_root_password="qwerty"
CT_memory="1024"
CT_cores="4"
CT_rootfs_size="2"

CT_install_packages="sudo screen wget htop docker-compose"

CT_enable_root_login="true"
CT_template_download="http://download.proxmox.com/images/system/debian-12-standard_12.7-1_amd64.tar.zst"
CT_template_filename="debian-12-standard_12.7-1_amd64.tar.zst"
CT_template_file="local:vztmpl/$CT_template_filename"
CT_key="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8Eg5xSbsyLySMCH5K1eb8ZzLTLwPrXDmgyGh9OAi/kofhR6UrTtuVzViAxBV8i+52pgMkRFoX2q/wDKkX7bJk0HXGzs26Npz40BCOEO6hf8MlTc/Kdu288sxVKPhnMofJ1UGy4vZjy2AHoAEe0NbazoEiBNZNO+EpXAGnaxnSM2KQFDDidZydeFMaGKWPb0wYXnGeKbnjxA2rASbX2Rd515FC5ZkYVAK0KFjRV41q+xJebBDhGhgLpHynusmFINM/RdoUswD+c2lwRpdIL+yU+DIPt4J7pM6h0Tj8hTFlqIzemwKKFi4UkzL53oQFYpCK2qAHiBTHAfOdL8gcF5Kv rsa-key-20240131"
CT_key_file="/ssh_key.$CT_hostname.pub"

addnet eth0 hwaddr "DE:AD:BE:EF:99:55" ip dhcp ip6 manual firewall 0 bridge vmbr0 #link_down 1 # LAN interface (eth0)

adduser user nopassword nologin lock groups docker
add_port_bind "443:user"

addfile "/opt/vaultwarden/docker-compose.yml" "644" "root:root"
addline "version: '3.7'"
addline "services:"
addline "  vaultwarden:"
addline "    image: vaultwarden/server:latest"
addline "    container_name: vaultwarden"
addline "    environment:"
addline "      DOMAIN: 'https://$CT_hostname$CT_network_suffix'"
addline "      ROCKET_PORT: 443"
addline "      ENABLE_SSL: 'true'"
addline "      SIGNUPS_ALLOWED: 'true'"
addline "      ROCKET_TLS: '{certs=/certs/fullchain.pem,key=/certs/privkey.pem}'"
addline "    volumes:"
addline "      - /opt/vaultwarden-data/:/data/"
addline "      - /opt/vaultwarden-certs/:/certs/:ro"
addline "    ports:"
addline "      - 443:443"
addline "    restart: unless-stopped"

addcommand mkdir -p /opt/vaultwarden/ /opt/vaultwarden-certs/ /opt/vaultwarden-data/
addcommand openssl req -x509 -newkey rsa:4096 -keyout /opt/vaultwarden-certs/privkey.pem -out /opt/vaultwarden-certs/fullchain.pem -sha256 -days 36500 -nodes -subj '/CN=$CT_hostname$CT_network_suffix'
addcommand sudo -u user docker-compose -f /opt/vaultwarden/docker-compose.yml up -d

# Only download template file if it is not already present
[ ! -f /var/lib/vz/template/cache/$CT_template_filename ] && wget "$CT_template_download" -O /var/lib/vz/template/cache/$CT_template_filename
# Set the right OS Type
case "$CT_template_filename" in *almalinux*|*amazonlinux*|*centos*|*openeuler*|*oracle*|*rockylinux*|*springdalelinux*) CT_os_type="centos" ;; *alpine*) CT_os_type="alpine" ;; *alt*|*busybox*|*plamo*|*slackware*|*voidlinux*|*openwrt*) CT_os_type="unmanaged" ;; *archlinux*) CT_os_type="archlinux" ;; *debian*|*devuan*|*kali*) CT_os_type="debian" ;; *fedora*) CT_os_type="fedora" ;; *funtoo*|*gentoo*) CT_os_type="gentoo" ;; *mint*) CT_os_type="ubuntu" ;; *nixos*) CT_os_type="nixos" ;; *opensuse*) CT_os_type="opensuse" ;; *ubuntu*) CT_os_type="ubuntu" ;; *) CT_os_type="unmanaged" ;; esac
# Obtain the next CT_ID if not already set
: ${CT_ID:=100}; CT_ID=$((CT_ID < 100 ? 100 : CT_ID)); existing_ids=$(pct list | awk 'NR>1 {print $1}' | sort -n); while echo "$existing_ids" | grep -qw "$CT_ID"; do CT_ID=$((CT_ID + 1)); done
# Writing the public key to a file, as pct requires
echo "$CT_key" > $CT_key_file

# Create the container
echo ""; echo Creating LXC Container for $CT_template_filename
[ "$VERBOSE" -gt 0 ] && echo "pct create $CT_ID $CT_template_file --arch amd64 --cores $CT_cores --memory $CT_memory --hostname $CT_hostname $_CT_net_config --rootfs local-lvm:$CT_rootfs_size --features nesting=1 --unprivileged 1 --ostype $CT_os_type"
pct create $CT_ID $CT_template_file --arch amd64 --cores $CT_cores --memory $CT_memory --hostname $CT_hostname $_CT_net_config --rootfs local-lvm:$CT_rootfs_size --features nesting=1 --unprivileged 1 --ostype $CT_os_type

# delete key file
rm $CT_key_file

# Define the LXC configuration file path
LXC_CONF_FILE="/etc/pve/nodes/proxmox/lxc/$CT_ID.conf"

# to set console to shell mode
echo "cmode: shell" >> "$LXC_CONF_FILE"

# to add the mount points
#for i in $(seq 1 $mount_point_ubound); do create_mount_point $i; done

# Start the container
pct start $CT_ID

#Wait until container is finished booting
while [[ $(pct status $CT_ID) != *"running"* ]]; do echo "Waiting for container $CT_ID to start..."; sleep 2; done; echo "Container $CT_ID is running."

sleep 5

# Enable root login via ssh
[ "$CT_enable_root_login" = "true" ] && pct_exec "sed -i 's/^#PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config"
# Enable login with password
[ -n "$CT_root_password" ] && pct_exec "sed -i 's/^#PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config"
[ -n "$CT_root_password" ] && pct_set_password "root:$CT_root_password"
pct_exec systemctl restart sshd

pct_update_package_manager

# Install additional packages if specified
[ -n "$CT_install_packages" ] && pct_install_package $CT_install_packages

# create extra users
[ -n "$user_ubound" ] && for i in $(seq 1 $user_ubound); do createuser $i; done

# write all the files into the container
[ -n "$file_ubound" ] && for i in $(seq 1 $file_ubound); do writefile $i; done

# allow port binding by users if needed
[ -n "$port_bind_ubound" ] && for i in $(seq 1 $port_bind_ubound); do create_port_bind $i; done

# run the commands specified with addcommand
[ -n "$pct_command_ubound" ] && for i in $(seq 1 $pct_command_ubound); do runcommand $i; done
hi and thanks for the share. if i used this script, how would it update?
 
Hi,
I don't know, I don't allow my systems to access the internet.
Looking at the source of this docker container
https://github.com/dani-garcia/vaultwarden

Since that docker container is create with a docker-compose, it seems the way to update is simply to run the following command, assuming the container number is 900

Code:
pct exec 900 -- /bin/docker-compose -f /opt/vaultwarden/docker-compose.yml down ; pct exec 900 --   /bin/docker-compose -f /opt/vaultwarden/docker-compose.yml up -d
 
  • Like
Reactions: husskii
Yeah,scripts works okay, nevermind the date. It pulls from dani ,and builds .deb files. But yeah , you would need to install nginx as a reverse proxy.
Hello,
Just to clarify, this does not need an extra reverse proxy, I use it on my LAN as https://vaultwarden.lan and it works.

Vaultwarden normally refuses to work without https/ssl but this method works.

Of course to access it from vaultwarden.example.com then it does need a reverse proxy
Unless you put your real certificates in /opt/vaultwarden-certs/
and set the LXC hostname to the actual hostname
 
Hi,
I don't know, I don't allow my systems to access the internet.
Looking at the source of this docker container
https://github.com/dani-garcia/vaultwarden

Since that docker container is create with a docker-compose, it seems the way to update is simply to run the following command, assuming the container number is 900

Code:
pct exec 900 -- /bin/docker-compose -f /opt/vaultwarden/docker-compose.yml down ; pct exec 900 --   /bin/docker-compose -f /opt/vaultwarden/docker-compose.yml up -d
thanks
 
great script, appreciate your work

the command to update vaultwarden above works but you need to run this first

Code:
docker pull vaultwarden/server

then

Code:
pct exec 900 -- /bin/docker-compose -f /opt/vaultwarden/docker-compose.yml down; pct exec 900 -- /bin/docker-compose -f /opt/vaultwarden/docker-compose.yml up -d
 
Hello,

The part of my script with
Code:
addfile "/opt/vaultwarden/docker-compose.yml" "644" "root:root"
is meant to create the docker-compose.yml file which is supposed to take care of everything

Just to be sure, I gave this a try again

I only changed the hostname to vaultwarden2.lan
I otherwise simply copied the above script directly into my proxmox shell

And video screen captured the result as follows

https://youtu.be/F_Ix9omhAvg
 
Hi,
Is it possible to connect this Vaultwarden with an application on the phone, e.g. Tailscale?
 
Yes,
I have not used tailscale but I imagine you mean, zero tier ?
In this case you would install that on it and join the private network.

The traditionnal way to do this is to open port 443 on your router, port forward to a reverse proxy, then reverse proxy redirects to vaultwarden.lan internally.

There are multiple ways of achieving this
 
im having trouble getting the vaultwarden to spin up. i cant access the page to create the account. it just pops up vaultwarden in the top left corner and then has a bunch of dots circling in the middle of the screen. it will sit there all day. not sure what im doing wrong here. i used the proxmox default setting helper scripts for vaultwarden. i havent set the admin token yet. but i thought i should be able to access the main page

any help that would be nice

the link i used
https://community-scripts.github.io/ProxmoxVE/scripts?id=vaultwarden
 
Last edited:
Disclaimer: I've never used Vaultwarden or the above script.

any help that would be nice
From the info/description of the above script:
Vaultwarden needs to be behind a proxy (Nginx Proxy Manager) to obtain HTTPS and to allow clients to connect.
Have you set this up? I see numerous posts with the same symptoms as yours, that appear to be caused by trying to access Vaultwarden over http which will not work apparently as it requires https.