From 6fc12d011953e92ead28e42abfbfe0fa364104b4 Mon Sep 17 00:00:00 2001 From: il Date: Sun, 15 Mar 2026 05:28:43 +0900 Subject: [PATCH] 1.2.0 Release vaultwarden --- ansible/inventory/group_vars/all.yaml | 2 + ansible/playbooks/app/site.yaml | 8 +++ ansible/roles/app/handlers/main.yaml | 11 ++++ .../app/tasks/services/set_vaultwarden.yaml | 56 +++++++++++++++++++ .../infra/tasks/services/set_postgresql.yaml | 1 + config/secrets/secrets.yaml | 17 +++++- .../app/vaultwarden/vaultwarden.container.j2 | 34 +++++++++++ .../common/caddy/etc/app/Caddyfile.j2 | 14 +++-- .../common/caddy/etc/auth/Caddyfile.j2 | 39 ++++++++----- docs/services/app/vaultwarden.md | 50 +++++++++++++++++ 10 files changed, 211 insertions(+), 21 deletions(-) create mode 100644 ansible/roles/app/handlers/main.yaml create mode 100644 ansible/roles/app/tasks/services/set_vaultwarden.yaml create mode 100644 config/services/containers/app/vaultwarden/vaultwarden.container.j2 create mode 100644 docs/services/app/vaultwarden.md diff --git a/ansible/inventory/group_vars/all.yaml b/ansible/inventory/group_vars/all.yaml index b09553c..89d14a1 100644 --- a/ansible/inventory/group_vars/all.yaml +++ b/ansible/inventory/group_vars/all.yaml @@ -72,3 +72,5 @@ version: vectorchord: "0.5.3" # Auth authelia: "4.39.15" + # App + vaultwarden: "1.35.4" diff --git a/ansible/playbooks/app/site.yaml b/ansible/playbooks/app/site.yaml index 9586cf7..325a7bc 100644 --- a/ansible/playbooks/app/site.yaml +++ b/ansible/playbooks/app/site.yaml @@ -161,6 +161,14 @@ tags: ["site", "caddy"] tags: ["site", "caddy"] + - name: Set vaultwarden + ansible.builtin.include_role: + name: "app" + tasks_from: "services/set_vaultwarden" + apply: + tags: ["site", "vaultwarden"] + tags: ["site", "vaultwarden"] + - name: Flush handlers right now ansible.builtin.meta: "flush_handlers" diff --git a/ansible/roles/app/handlers/main.yaml b/ansible/roles/app/handlers/main.yaml new file mode 100644 index 0000000..9daaa0d --- /dev/null +++ b/ansible/roles/app/handlers/main.yaml @@ -0,0 +1,11 @@ +--- +- name: Restart vaultwarden + ansible.builtin.systemd: + name: "vaultwarden.service" + state: "restarted" + enabled: true + scope: "user" + daemon_reload: true + changed_when: false + listen: "notification_restart_vaultwarden" + ignore_errors: true # noqa: ignore-errors diff --git a/ansible/roles/app/tasks/services/set_vaultwarden.yaml b/ansible/roles/app/tasks/services/set_vaultwarden.yaml new file mode 100644 index 0000000..b0054ab --- /dev/null +++ b/ansible/roles/app/tasks/services/set_vaultwarden.yaml @@ -0,0 +1,56 @@ +--- +- name: Create vaultwarden directory + ansible.builtin.file: + path: "{{ node['home_path'] }}/{{ item }}" + state: "directory" + owner: "{{ ansible_user }}" + group: "svadmins" + mode: "0770" + loop: + - "data/containers/vaultwarden" + - "containers/vaultwarden" + - "containers/vaultwarden/ssl" + +- name: Deploy root certificate + ansible.builtin.copy: + content: | + {{ hostvars['console']['ca']['root']['crt'] }} + dest: "{{ node['home_path'] }}/containers/vaultwarden/ssl/ilnmors_root_ca.crt" + owner: "{{ ansible_user }}" + group: "svadmins" + mode: "0440" + become: true + notify: "notification_restart_vaultwarden" + no_log: true + +- name: Register secret value to podman secret + containers.podman.podman_secret: + name: "{{ item.name }}" + data: "{{ item.value }}" + state: "present" + force: true + loop: + - name: "VW_ADMIN_TOKEN" + value: "{{ hostvars['console']['vaultwarden']['admin']['hash'] }}" + - name: "VW_DATABASE_URL" + value: "postgresql://vaultwarden:{{ hostvars['console']['postgresql']['password']['vaultwarden'] | urlencode | replace('/', '%2F') }}\ + @{{ infra_uri['postgresql']['domain'] }}/vaultwarden_db?sslmode=verify-full&sslrootcert=/etc/ssl/vaultwarden/ilnmors_root_ca.crt" + notify: "notification_restart_vaultwarden" + no_log: true + +- name: Deploy container file + ansible.builtin.template: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/vaultwarden/vaultwarden.container.j2" + dest: "{{ node['home_path'] }}/.config/containers/systemd/vaultwarden.container" + owner: "{{ ansible_user }}" + group: "svadmins" + mode: "0644" + notify: "notification_restart_vaultwarden" + +- name: Enable vaultwarden.service + ansible.builtin.systemd: + name: "vaultwarden.service" + state: "started" + enabled: true + daemon_reload: true + scope: "user" diff --git a/ansible/roles/infra/tasks/services/set_postgresql.yaml b/ansible/roles/infra/tasks/services/set_postgresql.yaml index e68f90f..95f93f4 100644 --- a/ansible/roles/infra/tasks/services/set_postgresql.yaml +++ b/ansible/roles/infra/tasks/services/set_postgresql.yaml @@ -10,6 +10,7 @@ - "ldap" - "authelia" - "grafana" + - "vaultwarden" - name: Create postgresql directory ansible.builtin.file: diff --git a/config/secrets/secrets.yaml b/config/secrets/secrets.yaml index c5eaca9..20d9cda 100644 --- a/config/secrets/secrets.yaml +++ b/config/secrets/secrets.yaml @@ -113,6 +113,7 @@ postgresql: ldap: ENC[AES256_GCM,data:mJrxIhXynHxJhncw3upHpOkXIw+Ka9bmDBJwkDjYl+D9Pg4RDvL6WzBjthw=,iv:y8MUYo6VhgTzbWh/+n7/hf1Jw+L2KcdxKvulPJ67xn8=,tag:4ZFpj1UdOwXmaZjYvC/s3A==,type:str] grafana: ENC[AES256_GCM,data:P9okJ7bcsqmeGstkSwbDq/RgnG+lFrgAOvcj8A5lOTpmHaSlXGiKG+ybXa0=,iv:Di1ghnxIbAb/u7uo/mJCC3QYVjdweTHaQDZmXTx8OG4=,tag:DT3a1zgU9sTr0BXpyoZ/SQ==,type:str] authelia: ENC[AES256_GCM,data:OqyloAADO6KKEaBjGLsJc9GTe77wn6IvA1VCD2dfCWxx+zgzUYh87fK1XX8=,iv:QIOHNTdNnzcY/f3Co8dPdNHykhBnYRhm43nt35hbALM=,tag:DLQq58GrZd+Ul7MSn6s9uQ==,type:str] + vaultwarden: ENC[AES256_GCM,data:BPj5eFo54DTZ82n3yTIqEbm7kb/jWT0n2kZY//oV5q48eRch3C2RBuxn/Ko=,iv:DGC4ipHMyVs25gc4sNMt8LN1RsHjiR/b303vgiFoxMY=,tag:k1eb4DoRPLKvvMstSI1faQ==,type:str] #ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment] # # @@ -170,6 +171,18 @@ authelia: #ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment] # # +#ENC[AES256_GCM,data:MiobmVqj3dPargzws5q7Cpggnvw=,iv:VzBh1ZWqyByyTDn90pl0//TsjAYO5QLpfbrxnMF5OU4=,tag:7Y16L9i/homdtEpWynzsdg==,type:comment] +vaultwarden: + admin: + password: ENC[AES256_GCM,data:FzD80H5lPNyjTRGd/IcIGir35KLF8gN3qmTzEQrlfutHh08mk0Vh9X8irHA=,iv:cfn5432g2MvkuBJLB2zDJmU0sLgMefVvpy0bP/4/oPY=,tag:7MHEv8XswdQZNTARvxs55g==,type:str] + hash: ENC[AES256_GCM,data:Ae7bhnL5pxfNV3+r/PCDIpxx0tHcNiB/s4sm7OoxVpp+tcEpXZ9vsv38sZjx+Z8t2vlmiYHc72PEdapcAihleuLMBPM1dAVn8LqzzWtVhfmCOoUZI8Z5L3VL8eyyz6npiw==,iv:c2MEIbrnVsR+bUx3zLLTnKSElFTH7JRl23HmmTlWEBg=,tag:mnMrER9pQEKAdloYMeRa3g==,type:str] + il: + password: ENC[AES256_GCM,data:mhLb55ENatpE59Rbzk2Uq6iBKc0Jj/9x4fOANJOHR0WvSxTQRKJ94cCq9Ykp/chWbdgYYPrwU5oD0Yo17zqb,iv:6bzKq5WaKhuOsQ8zSSH9ZrQYbDPB1nv/bFoQou1ycL4=,tag:bSmuUodl+/9nzxZ7YBoezA==,type:str] + morsalin: + password: ENC[AES256_GCM,data:ibg5/MfLH7pSY2pEmjM=,iv:+aV5muP/9BYoKwTGQxKEL+IGY9P+O3GVKGgSuTzT+U0=,tag:rgqcmJvd1RtvWJ91PCxYIg==,type:str] +#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment] +# +# #ENC[AES256_GCM,data:T4Wtn49AAxPd2QUFTR+q,iv:bH5goGWBDqumAat9dUv2OwfCUJUpuVqncTMqMBZUXhI=,tag:G+W6hHA+yftQ+4RJpXrxHg==,type:comment] switch: password: ENC[AES256_GCM,data:qu0f9L7A0eFq/UCpaRs=,iv:W8LLOp3MSfd/+EfNEZNf91K8GgI5eUfVPoWTRES2C0Y=,tag:Q5FlAOfwqwJwPvd7k6i+0g==,type:str] @@ -199,7 +212,7 @@ sops: UmliaFNxVTBqRkI1QWJpWGpTRWxETW8KEY/8AfU73UOzCGhny1cNnd5dCNv7bHXt k+uyWPPi+enFkVaceSwMFrA66uaWWrwAj11sXEB7yzvGFPrnAGezjQ== -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-03-14T19:40:47Z" - mac: ENC[AES256_GCM,data:EUVSxs6FPhKMSSmHe8P/d0IyBZsNb3q7AYj06j98bklAMYYVOludVePdh45MSvn92lDn712Muy6pqcJzDpsPWyxgXngywTu2SGV1yRCyA7U7RloRxlNROuDiugMkJWOtHcKArytVChUHT2PnzagAJR2kBSApbjUsC/xUTMBpsNM=,iv:SsJW2fMNEJHT2M+gjW5TKu6AYoxsf9jKf5T9KgJoF40=,tag:ItVweaSxts2Cm1VKkLp0/w==,type:str] + lastmodified: "2026-03-14T19:42:34Z" + mac: ENC[AES256_GCM,data:xnsUauduZtPvf7MHkCWkzxpD8tCKDZZ97jskvi8jOeQdRM+L9U0bs3PTfrSeXjRvt1FCEDLyLxh6+NUBmGdI24/9mPU8wVN2sLVLKQ4HkXneCiKErRLoVKdliJGD9YxpkSNuYkwNyiM442akJUG0sVJOGcOGIrxn2msqCZlwSuk=,iv:AUqsg6B6CFVKcnBX+g126Xva7+xeNpTQ3FBKVVWoGFw=,tag:YMMZXfXON0MAahtjKWWWzw==,type:str] unencrypted_suffix: _unencrypted version: 3.12.1 diff --git a/config/services/containers/app/vaultwarden/vaultwarden.container.j2 b/config/services/containers/app/vaultwarden/vaultwarden.container.j2 new file mode 100644 index 0000000..ea68470 --- /dev/null +++ b/config/services/containers/app/vaultwarden/vaultwarden.container.j2 @@ -0,0 +1,34 @@ +[Quadlet] +DefaultDependencies=false + +[Unit] +Description=Vaultwarden + +After=network-online.target +Wants=network-online.target + +[Container] +Image=docker.io/vaultwarden/server:{{ version['containers']['vaultwarden'] }} + +ContainerName=vaultwarden +HostName=vaultwarden + +PublishPort=8000:80/tcp + +Volume=%h/data/containers/vaultwarden:/data:rw +Volume=%h/containers/vaultwarden/ssl:/etc/ssl/vaultwarden:ro + +Environment="TZ=Asia/Seoul" +Environment="DOMAIN=https://vault.ilnmors.com" +Environment="SIGNUPS_ALLOWED=false" +Secret=VW_ADMIN_TOKEN,type=env,target=ADMIN_TOKEN +Secret=VW_DATABASE_URL,type=env,target=DATABASE_URL + +[Service] +ExecStartPre=/usr/bin/nc -zv {{ infra_uri['postgresql']['domain'] }} {{ infra_uri['postgresql']['ports']['tcp'] }} +Restart=always +RestartSec=10s +TimeoutStopSec=120 + +[Install] +WantedBy=default.target diff --git a/config/services/containers/common/caddy/etc/app/Caddyfile.j2 b/config/services/containers/common/caddy/etc/app/Caddyfile.j2 index 5d90279..d37c55e 100644 --- a/config/services/containers/common/caddy/etc/app/Caddyfile.j2 +++ b/config/services/containers/common/caddy/etc/app/Caddyfile.j2 @@ -23,8 +23,14 @@ app.ilnmors.internal { import private_tls metrics } -test.app.ilnmors.internal { - import private_tls - root * /usr/share/caddy - file_server +# test.app.ilnmors.internal { +# import private_tls +# root * /usr/share/caddy +# file_server +# } +vault.app.ilnmors.internal { + import private_tls + reverse_proxy host.containers.internal:8000 { + header_up Host {http.request.header.X-Forwarded-Host} + } } diff --git a/config/services/containers/common/caddy/etc/auth/Caddyfile.j2 b/config/services/containers/common/caddy/etc/auth/Caddyfile.j2 index cef72bb..c6b61a8 100644 --- a/config/services/containers/common/caddy/etc/auth/Caddyfile.j2 +++ b/config/services/containers/common/caddy/etc/auth/Caddyfile.j2 @@ -41,24 +41,33 @@ authelia.ilnmors.com { reverse_proxy host.containers.internal:9091 } } -test.ilnmors.com { +# test.ilnmors.com { +# import crowdsec_log +# route { +# crowdsec +# forward_auth host.containers.internal:9091 { +# # Authelia Forward Auth endpoint URI +# uri /api/authz/forward-auth +# copy_headers Remote-User Remote-Groups Remote-Email Remote-Name +# } +# root * /usr/share/caddy +# file_server +# } +# } +# test.app.ilnmors.com { +# import crowdsec_log +# route { +# crowdsec +# reverse_proxy https://test.app.ilnmors.internal { +# header_up Host {http.reverse_proxy.upstream.host} +# } +# } +# } +vault.ilnmors.com { import crowdsec_log route { crowdsec - forward_auth host.containers.internal:9091 { - # Authelia Forward Auth endpoint URI - uri /api/authz/forward-auth - copy_headers Remote-User Remote-Groups Remote-Email Remote-Name - } - root * /usr/share/caddy - file_server - } -} -test.app.ilnmors.com { - import crowdsec_log - route { - crowdsec - reverse_proxy https://test.app.ilnmors.internal { + reverse_proxy https://vault.app.ilnmors.internal { header_up Host {http.reverse_proxy.upstream.host} } } diff --git a/docs/services/app/vaultwarden.md b/docs/services/app/vaultwarden.md new file mode 100644 index 0000000..6563b33 --- /dev/null +++ b/docs/services/app/vaultwarden.md @@ -0,0 +1,50 @@ +# vaultwarden + +## Prerequisite + +### Create database + +- Create the password with `openssl rand -base64 32` + - Save this value in secrets.yaml in `postgres.password.vaultwarden` + - Access infra server to create vaultwarden_db with `podman exec -it postgresql psql -U postgres` + +```SQL +CREATE USER vaultwarden WITH PASSWORD 'postgres.password.vaultwarden'; +CREATE DATABASE vaultwarden_db; +ALTER DATABASE vaultwarden_db OWNER TO vaultwarden; +``` + +### Create admin hash + +- Create the password with `openssl rand -base64 32` + - https://argon2.online/ + - salt: auto generate + - parallelism: factor 4 + - memory cost: 65536 + - iterations: 3 + - hash length: 32 + - type: argon2id +- Save this value in secrets.yaml in `vaultwarden.admin.password` and `vaultwarden.admin.hash` + +### Add postgresql dump backup list + +- [set_postgresql.yaml](../../../ansible/roles/infra/tasks/services/set_postgresql.yaml) + +```yaml +- name: Set connected services list + ansible.builtin.set_fact: + # telegraf has no database + connected_services: + - ... + - "vaultwarden" + +``` + +## Configuration + +- https://vault.ilnmors.com/admin + - token value: vaultwarden.admin.password +- Users:Invite User:Email + - add +- https://vault.ilnmors.com + - Create account and input the Email which added in admin page