Installer: use helm secrets to encrypt secrets locally and avoid regenerating them on every upgrade
diff --git a/charts/pihole/templates/_helpers.tpl b/charts/pihole/templates/_helpers.tpl
deleted file mode 100644
index 063b2b4..0000000
--- a/charts/pihole/templates/_helpers.tpl
+++ /dev/null
@@ -1,7 +0,0 @@
-{{- define "clientSecret" -}}
-{{- if .Values.oauth2.clientSecret -}}
-{{- .Values.oauth2.clientSecret -}}
-{{- else -}}
-{{- randAlphaNum 32 -}}
-{{- end -}}
-{{- end -}}
diff --git a/charts/pihole/templates/oauth2-client-secret.yaml b/charts/pihole/templates/oauth2-client-secret.yaml
new file mode 100644
index 0000000..bff9df3
--- /dev/null
+++ b/charts/pihole/templates/oauth2-client-secret.yaml
@@ -0,0 +1,9 @@
+apiVersion: v1
+kind: Secret
+type: Opaque
+metadata:
+  name: {{ .Values.oauth2.secretName }}
+  namespace: {{ .Release.Namespace }}
+data:
+  client_id: {{ .Values.oauth2.clientId | b64enc  }}
+  client_secret: {{ .Values.oauth2.clientSecret | b64enc }}
diff --git a/charts/pihole/templates/oauth2-proxy-config.yaml b/charts/pihole/templates/oauth2-proxy-config.yaml
index 1ce0ffa..69a6448 100644
--- a/charts/pihole/templates/oauth2-proxy-config.yaml
+++ b/charts/pihole/templates/oauth2-proxy-config.yaml
@@ -1,15 +1,3 @@
-{{- $secret := include "clientSecret" . -}}
----
-apiVersion: v1
-kind: Secret
-type: Opaque
-metadata:
-  name: {{ .Values.oauth2.secretName }}
-  namespace: {{ .Release.Namespace }}
-data:
-  client_id: {{ .Values.oauth2.clientId | b64enc  }}
-  client_secret: {{ $secret | b64enc }}
----
 apiVersion: v1
 kind: ConfigMap
 metadata:
@@ -43,13 +31,13 @@
 
     ## The OAuth Client ID, Secret
     client_id = "{{ .Values.oauth2.clientId }}"
-    client_secret = "{{ $secret }}"
+    client_secret = "{{ .Values.oauth2.clientSecret }}"
 
     ## Pass OAuth Access token to upstream via "X-Forwarded-Access-Token"
     pass_access_token = false
 
     cookie_name = "_oauth2_proxy_pihole"
-    cookie_secret = "123456789012345678901234567890--"
+    cookie_secret = "{{ .Values.oauth2.cookieSecret }}"
     cookie_domains = "pihole.p.{{ .Values.domain }}"
     cookie_expire = "168h"
     cookie_refresh = "100h"
diff --git a/charts/pihole/values.yaml b/charts/pihole/values.yaml
index 9c038d6..a7ced0b 100644
--- a/charts/pihole/values.yaml
+++ b/charts/pihole/values.yaml
@@ -1,6 +1,8 @@
 pihole: {}
 oauth2:
   clientId: app-pihole
+  clientSecret: ""
+  cookieSecret: ""
   secretName: oauth2-secret
   configName: oauth2-proxy
 domain: example.com
diff --git a/helmfile/users/.sops.yaml b/helmfile/users/.sops.yaml
new file mode 100644
index 0000000..013cdf0
--- /dev/null
+++ b/helmfile/users/.sops.yaml
@@ -0,0 +1,2 @@
+creation_rules:
+- pgp: 60584680BB48B3CE3FECFFBE7D1302EE361D316A
diff --git a/helmfile/users/helmfile.yaml b/helmfile/users/helmfile.yaml
index 24a9082..8bfc2e4 100644
--- a/helmfile/users/helmfile.yaml
+++ b/helmfile/users/helmfile.yaml
@@ -9,476 +9,476 @@
   waitForJobs: false
 
 releases:
-# - name: vpn-mesh-config
-#   chart: ../../charts/vpn-mesh-config
-#   namespace: {{ .Values.id }}-ingress-private
-#   createNamespace: true
-#   values:
-#   - certificateAuthority:
-#       name: {{ .Values.id }}
-#       secretName: ca-{{ .Values.id }}-cert
-#   - lighthouse:
-#       internalIP: 111.0.0.1
-#       externalIP: 46.49.35.44
-#       port: "4243"
-# - name: ingress-private
-#   chart: ingress-nginx/ingress-nginx
-#   version: 4.0.3
-#   namespace: {{ .Values.id }}-ingress-private
-#   createNamespace: true
-#   values:
-#   - fullnameOverride: nginx
-#   - controller:
-#       service:
-#         type: ClusterIP
-#       ingressClassByName: true
-#       ingressClassResource:
-#         name: {{ .Values.id }}-ingress-private
-#         enabled: true
-#         default: false
-#         controllerValue: k8s.io/{{ .Values.id }}-ingress-private
-#       extraArgs:
-#         default-ssl-certificate: "{{ .Values.id }}-ingress-private/cert-wildcard.p.{{ .Values.domain }}"
-#       extraVolumes:
-#       - name: lighthouse-cert
-#         secret:
-#           secretName: node-lighthouse-cert
-#       - name: config
-#         configMap:
-#           name: lighthouse-config
-#       extraContainers:
-#       - name: lighthouse
-#         image: giolekva/nebula:latest
-#         imagePullPolicy: IfNotPresent
-#         securityContext:
-#           privileged: true
-#           capabilities:
-#             add:
-#             - NET_ADMIN
-#         ports:
-#         - name: nebula
-#           containerPort: 4243
-#           protocol: UDP
-#         command:
-#         - nebula
-#         - --config=/etc/nebula/config/lighthouse.yaml
-#         volumeMounts:
-#         - name: lighthouse-cert
-#           mountPath: /etc/nebula/lighthouse
-#         - name: config
-#           mountPath: /etc/nebula/config
-#       config:
-#         bind-address: 111.0.0.1
-#         proxy-body-size: 0
-#       udp:
-#       - 53: {{ .Values.id }}-app-pihole/pihole-dns-udp:53
-#       tcp:
-#       - 53: {{ .Values.id }}-app-pihole/pihole-dns-tcp:53
-# - name: certificate-issuer
-#   chart: ../../charts/certificate-issuer
-#   namespace: {{ .Values.certManagerNamespace }} # {{ .Values.id }}-ingress-private
-#   createNamespace: true
-#   values:
-#   - public:
-#       name: {{ .Values.id }}-public
-#       server: https://acme-v02.api.letsencrypt.org/directory
-#       stagingServer: https://acme-staging-v02.api.letsencrypt.org/directory
-#       contactEmail: {{ .Values.contactEmail }}
-#       ingressClass: nginx
-#   - private:
-#       name: {{ .Values.id }}-private
-#       domain: {{ .Values.id }}
-#       ingressClassName: {{ .Values.id }}-ingress-private
-# - name: core-auth-storage  # TODO(giolekva): merge with core-auth
-#   chart: bitnami/postgresql
-#   version: 10.13.5
-#   namespace: {{ .Values.id }}-core-auth
-#   createNamespace: true
-#   values:
-#   - fullnameOverride: postgres
-#   - image:
-#       repository: arm64v8/postgres
-#       tag: 13.4
-#   - service:
-#       type: ClusterIP
-#       port: 5432
-#   - postgresqlPassword: psswd
-#   - postgresqlDatabase: kratos
-#   - persistence:
-#       size: 1Gi
-#   - securityContext:
-#       enabled: true
-#       fsGroup: 0
-#   - containerSecurityContext:
-#       enabled: true
-#       runAsUser: 0
-#   - volumePermissions:
-#       securityContext:
-#         runAsUser: 0
-# - name: core-auth
-#   chart: ../../charts/auth
-#   namespace: {{ .Values.id }}-core-auth
-#   createNamespace: true
-#   values:
-#   - kratos:
-#       fullnameOverride: kratos
-#       image:
-#         repository: giolekva/ory-kratos
-#         tag: latest
-#         pullPolicy: Always
-#       service:
-#         admin:
-#           enabled: true
-#           type: ClusterIP
-#           port: 80
-#           name: http
-#         public:
-#           enabled: true
-#           type: ClusterIP
-#           port: 80
-#           name: http
-#       ingress:
-#         admin:
-#           enabled: true
-#           className: {{ .Values.id }}-ingress-private
-#           hosts:
-#           - host: kratos.{{ .Values.id }}
-#             paths:
-#             - path: /
-#               pathType: Prefix
-#           annotations:
-#             cert-manager.io/cluster-issuer: "{{ .Values.id }}-private"
-#             acme.cert-manager.io/http01-edit-in-place: "true"
-#           tls:
-#           - hosts:
-#             - kratos.{{ .Values.id }}
-#             secretName: cert-kratos.{{ .Values.id }}
-#         public:
-#           enabled: true
-#           className: nginx
-#           hosts:
-#           - host: accounts.{{ .Values.domain }}
-#             paths:
-#             - path: /
-#               pathType: Prefix
-#           annotations:
-#             cert-manager.io/cluster-issuer: "{{ .Values.id }}-public"
-#             acme.cert-manager.io/http01-edit-in-place: "true"
-#           tls:
-#           - hosts:
-#             - accounts.{{ .Values.domain }}
-#             # secretName: cert-accounts.{{ .Values.domain }}
-#             secretName: cert-wildcard.{{ .Values.domain }}
-#       secret:
-#         enabled: true
-#       kratos:
-#         autoMigrate: true
-#         development: false
-#         config:
-#           version: v0.7.1-alpha.1
-#           dsn: postgres://postgres:psswd@postgres:5432/kratos?sslmode=disable&max_conns=20&max_idle_conns=4
-#           serve:
-#             public:
-#               base_url: https://accounts.{{ .Values.domain }}
-#               cors:
-#                 enabled: true
-#                 debug: false
-#                 allow_credentials: true
-#                 allowed_origins:
-#                 - https://{{ .Values.domain }}
-#                 - https://*.{{ .Values.domain }}
-#             admin:
-#               base_url: https://kratos.{{ .Values.id }}/
-#           selfservice:
-#             default_browser_return_url: https://accounts-ui.{{ .Values.domain }}
-#             whitelisted_return_urls:
-#               - https://accounts-ui.{{ .Values.domain }}
-#             methods:
-#               password:
-#                 enabled: true
-#             flows:
-#               error:
-#                 ui_url: https://accounts-ui.{{ .Values.domain }}/error
-#               settings:
-#                 ui_url: https://accounts-ui.{{ .Values.domain }}/settings
-#                 privileged_session_max_age: 15m
-#               recovery:
-#                 enabled: false
-#               verification:
-#                 enabled: false
-#               logout:
-#                 after:
-#                   default_browser_return_url: https://accounts-ui.{{ .Values.domain }}/login
-#               login:
-#                 ui_url: https://accounts-ui.{{ .Values.domain }}/login
-#                 lifespan: 10m
-#                 after:
-#                   password:
-#                     default_browser_return_url: https://accounts-ui.{{ .Values.domain }}/
-#               registration:
-#                 lifespan: 10m
-#                 ui_url: https://accounts-ui.{{ .Values.domain }}/registration
-#                 after:
-#                   password:
-#                     hooks:
-#                       -
-#                         hook: session
-#                     default_browser_return_url: https://accounts-ui.{{ .Values.domain }}/
-#           log:
-#             level: debug
-#             format: text
-#             leak_sensitive_values: true
-#           cookies:
-#             path: /
-#             same_site: None
-#             domain: {{ .Values.domain }}
-#           secrets:
-#             cookie:
-#               - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE
-#             # cipher:
-#             #   - 32-LONG-SECRET-NOT-SECURE-AT-ALL
-#           # ciphers:
-#           #   algorithm: xchacha20-poly1305
-#           hashers:
-#             argon2:
-#               parallelism: 1
-#               memory: 128MB
-#               iterations: 2
-#               salt_length: 16
-#               key_length: 16
-#           identity:
-#             default_schema_url: file:///etc/config/identity.schema.json
-#           courier:
-#             smtp:
-#               connection_uri: smtps://test-z1VmkYfYPjgdPRgPFgmeZ31esT9rUgS%40{{ .Values.domain }}:iW%213Kk%5EPPLFrZa%24%21bbpTPN9Wv3b8mvwS6ZJvMLtce%23A2%2A4MotD@mx1.{{ .Values.domain }}
-#         identitySchemas:
-#           "identity.schema.json": |
-#             {
-#               "$id": "https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json",
-#               "$schema": "http://json-schema.org/draft-07/schema#",
-#               "title": "User",
-#               "type": "object",
-#               "properties": {
-#                 "traits": {
-#                   "type": "object",
-#                   "properties": {
-#                     "username": {
-#                       "type": "string",
-#                       "format": "username",
-#                       "title": "Username",
-#                       "minLength": 3,
-#                       "ory.sh/kratos": {
-#                         "credentials": {
-#                           "password": {
-#                             "identifier": true
-#                           }
-#                         }
-#                       }
-#                     }
-#                   },
-#                   "additionalProperties": false
-#                 }
-#               }
-#             }
-#   - hydra:
-#       fullnameOverride: hydra
-#       image:
-#         repository: giolekva/ory-hydra
-#         tag: latest
-#         pullPolicy: Always
-#       service:
-#         admin:
-#           enabled: true
-#           type: ClusterIP
-#           port: 80
-#           name: http
-#         public:
-#           enabled: true
-#           type: ClusterIP
-#           port: 80
-#           name: http
-#       ingress:
-#         admin:
-#           enabled: true
-#           className: {{ .Values.id }}-ingress-private
-#           hosts:
-#           - host: hydra.{{ .Values.id }}
-#             paths:
-#             - path: /
-#               pathType: Prefix
-#           annotations:
-#             cert-manager.io/cluster-issuer: "{{ .Values.id }}-private"
-#             acme.cert-manager.io/http01-edit-in-place: "true"
-#           tls:
-#           - hosts:
-#             - hydra.{{ .Values.id }}
-#             secretName: cert-hydra.{{ .Values.id }}
-#         public:
-#           enabled: true
-#           className: nginx
-#           hosts:
-#           - host: hydra.{{ .Values.domain }}
-#             paths:
-#             - path: /
-#               pathType: Prefix
-#           annotations:
-#             cert-manager.io/cluster-issuer: "{{ .Values.id }}-public"
-#             acme.cert-manager.io/http01-edit-in-place: "true"
-#           tls:
-#           - hosts:
-#             - hydra.{{ .Values.domain }}
-#             # secretName: cert-hydra.{{ .Values.domain }}
-#             secretName: cert-wildcard.{{ .Values.domain }}
-#       secret:
-#         enabled: true
-#       maester:
-#         enabled: true
-#         hydraFullnameOverride: hydra
-#       hydra-maester:
-#         image:
-#           repository: giolekva/ory-hydra-maester
-#           tag: latest
-#           pullPolicy: IfNotPresent
-#         adminService:
-#           name: hydra
-#           port: 80
-#       hydra:
-#         autoMigrate: true
-#         config:
-#           version: v1.10.6
-#           dsn: postgres://postgres:psswd@postgres:5432/kratos?sslmode=disable&max_conns=20&max_idle_conns=4
-#           serve:
-#             cookies:
-#               same_site_mode: None
-#             public:
-#               cors:
-#                 enabled: true
-#                 debug: false
-#                 allow_credentials: true
-#                 allowed_origins:
-#                   - https://{{ .Values.domain }}
-#                   - https://*.{{ .Values.domain }}
-#             admin:
-#               # host: localhost
-#               cors:
-#                 allowed_origins:
-#                   - https://hydra.{{ .Values.id }}
-#               tls:
-#                 allow_termination_from:
-#                   - 0.0.0.0/0
-#                   - 10.42.0.0/16
-#                   - 10.43.0.0/16
-#                   - 111.0.0.1/32
-#             tls:
-#               allow_termination_from:
-#                 - 0.0.0.0/0
-#                 - 10.42.0.0/16
-#                 - 10.43.0.0/16
-#                 - 111.0.0.1/32
-#           urls:
-#             self:
-#               public: https://hydra.{{ .Values.domain }}
-#               issuer: https://hydra.{{ .Values.domain }}
-#             consent: https://accounts-ui.{{ .Values.domain }}/consent
-#             login: https://accounts-ui.{{ .Values.domain }}/login
-#             logout: https://accounts-ui.{{ .Values.domain }}/logout
-#           secrets:
-#             system:
-#               - youReallyNeedToChangeThis
-#           oidc:
-#             subject_identifiers:
-#               supported_types:
-#                 - pairwise
-#                 - public
-#               pairwise:
-#                 salt: youReallyNeedToChangeThis
-#           log:
-#             level: trace
-#             leak_sensitive_values: false
-#   - ui:
-#       certificateIssuer: {{ .Values.id }}-public
-#       ingressClassName: nginx
-#       domain: {{ .Values.domain }}
-#       internalDomain: {{ .Values.id }}
-#       nebula:
-#         lighthouse:
-#           name: ui-lighthouse
-#           internalIP: 111.0.0.1
-#           externalIP: 46.49.35.44
-#           port: "4243"
-#         node:
-#           name: ui
-#           ipCidr: 111.0.0.2/24
-#           secretName: node-ui-cert
-#         certificateAuthority:
-#           name: {{ .Values.id }}
-#           namespace: {{ .Values.id }}-ingress-private
-# - name: vaultwarden
-#   chart: ../../charts/vaultwarden
-#   namespace: {{ .Values.id }}-app-vaultwarden
-#   createNamespace: true
-#   values:
-#   - image:
-#       repository: vaultwarden/server
-#       tag: 1.22.2
-#       pullPolicy: IfNotPresent
-#   - storage:
-#       size: 1Gi
-#   - domain: bitwarden.{{ .Values.id }}
-#   - certificateIssuer: {{ .Values.id }}-private
-#   - ingressClassName: {{ .Values.id }}-ingress-private
-# - name: matrix-storage  # TODO(giolekva): merge with core-auth
-#   chart: bitnami/postgresql
-#   version: 10.13.5
-#   namespace: {{ .Values.id }}-app-matrix
-#   createNamespace: true
-#   values:
-#   - fullnameOverride: postgres
-#   - image:
-#       repository: arm64v8/postgres
-#       tag: 13.4
-#   - service:
-#       type: ClusterIP
-#       port: 5432
-#   - postgresqlPassword: psswd
-#   - initdbScripts:
-#       createdb.sh: |
-#         #!/bin/sh
-#         createdb -U postgres --encoding=UTF8 --locale=C --template=template0 --owner=postgres matrix
-#   - persistence:
-#       size: 1Gi
-#   - securityContext:
-#       enabled: true
-#       fsGroup: 0
-#   - containerSecurityContext:
-#       enabled: true
-#       runAsUser: 0
-#   - volumePermissions:
-#       securityContext:
-#         runAsUser: 0
-# - name: matrix
-#   chart: ../../charts/matrix
-#   namespace: {{ .Values.id }}-app-matrix
-#   createNamespace: true
-#   values:
-#   - domain: {{ .Values.domain }}
-#   - oauth2:
-#       hydraAdmin: http://hydra-admin
-#       hydraPublic: https://hydra.{{ .Values.domain }}
-#       clientId: matrix
-#       clientSecret: ""
-#       secretName: oauth2-client
-#   - postgresql:
-#       host: postgres
-#       port: 5432
-#       database: matrix
-#       user: postgres
-#       password: psswd
-#   - certificateIssuer: {{ .Values.id }}-public
-#   - ingressClassName: nginx
-#   - configMerge:
-#       configName: config-to-merge
-#       fileName: to-merge.yaml
+- name: vpn-mesh-config
+  chart: ../../charts/vpn-mesh-config
+  namespace: {{ .Values.id }}-ingress-private
+  createNamespace: true
+  values:
+  - certificateAuthority:
+      name: {{ .Values.id }}
+      secretName: ca-{{ .Values.id }}-cert
+  - lighthouse:
+      internalIP: 111.0.0.1
+      externalIP: 46.49.35.44
+      port: "4243"
+- name: ingress-private
+  chart: ingress-nginx/ingress-nginx
+  version: 4.0.3
+  namespace: {{ .Values.id }}-ingress-private
+  createNamespace: true
+  values:
+  - fullnameOverride: nginx
+  - controller:
+      service:
+        type: ClusterIP
+      ingressClassByName: true
+      ingressClassResource:
+        name: {{ .Values.id }}-ingress-private
+        enabled: true
+        default: false
+        controllerValue: k8s.io/{{ .Values.id }}-ingress-private
+      extraArgs:
+        default-ssl-certificate: "{{ .Values.id }}-ingress-private/cert-wildcard.p.{{ .Values.domain }}"
+      extraVolumes:
+      - name: lighthouse-cert
+        secret:
+          secretName: node-lighthouse-cert
+      - name: config
+        configMap:
+          name: lighthouse-config
+      extraContainers:
+      - name: lighthouse
+        image: giolekva/nebula:latest
+        imagePullPolicy: IfNotPresent
+        securityContext:
+          privileged: true
+          capabilities:
+            add:
+            - NET_ADMIN
+        ports:
+        - name: nebula
+          containerPort: 4243
+          protocol: UDP
+        command:
+        - nebula
+        - --config=/etc/nebula/config/lighthouse.yaml
+        volumeMounts:
+        - name: lighthouse-cert
+          mountPath: /etc/nebula/lighthouse
+        - name: config
+          mountPath: /etc/nebula/config
+      config:
+        bind-address: 111.0.0.1
+        proxy-body-size: 0
+      udp:
+      - 53: {{ .Values.id }}-app-pihole/pihole-dns-udp:53
+      tcp:
+      - 53: {{ .Values.id }}-app-pihole/pihole-dns-tcp:53
+- name: certificate-issuer
+  chart: ../../charts/certificate-issuer
+  namespace: {{ .Values.certManagerNamespace }} # {{ .Values.id }}-ingress-private
+  createNamespace: true
+  values:
+  - public:
+      name: {{ .Values.id }}-public
+      server: https://acme-v02.api.letsencrypt.org/directory
+      stagingServer: https://acme-staging-v02.api.letsencrypt.org/directory
+      contactEmail: {{ .Values.contactEmail }}
+      ingressClass: nginx
+  - private:
+      name: {{ .Values.id }}-private
+      domain: {{ .Values.id }}
+      ingressClassName: {{ .Values.id }}-ingress-private
+- name: core-auth-storage  # TODO(giolekva): merge with core-auth
+  chart: bitnami/postgresql
+  version: 10.13.5
+  namespace: {{ .Values.id }}-core-auth
+  createNamespace: true
+  values:
+  - fullnameOverride: postgres
+  - image:
+      repository: arm64v8/postgres
+      tag: 13.4
+  - service:
+      type: ClusterIP
+      port: 5432
+  - postgresqlPassword: psswd
+  - postgresqlDatabase: kratos
+  - persistence:
+      size: 1Gi
+  - securityContext:
+      enabled: true
+      fsGroup: 0
+  - containerSecurityContext:
+      enabled: true
+      runAsUser: 0
+  - volumePermissions:
+      securityContext:
+        runAsUser: 0
+- name: core-auth
+  chart: ../../charts/auth
+  namespace: {{ .Values.id }}-core-auth
+  createNamespace: true
+  values:
+  - kratos:
+      fullnameOverride: kratos
+      image:
+        repository: giolekva/ory-kratos
+        tag: latest
+        pullPolicy: Always
+      service:
+        admin:
+          enabled: true
+          type: ClusterIP
+          port: 80
+          name: http
+        public:
+          enabled: true
+          type: ClusterIP
+          port: 80
+          name: http
+      ingress:
+        admin:
+          enabled: true
+          className: {{ .Values.id }}-ingress-private
+          hosts:
+          - host: kratos.{{ .Values.id }}
+            paths:
+            - path: /
+              pathType: Prefix
+          annotations:
+            cert-manager.io/cluster-issuer: "{{ .Values.id }}-private"
+            acme.cert-manager.io/http01-edit-in-place: "true"
+          tls:
+          - hosts:
+            - kratos.{{ .Values.id }}
+            secretName: cert-kratos.{{ .Values.id }}
+        public:
+          enabled: true
+          className: nginx
+          hosts:
+          - host: accounts.{{ .Values.domain }}
+            paths:
+            - path: /
+              pathType: Prefix
+          annotations:
+            cert-manager.io/cluster-issuer: "{{ .Values.id }}-public"
+            acme.cert-manager.io/http01-edit-in-place: "true"
+          tls:
+          - hosts:
+            - accounts.{{ .Values.domain }}
+            # secretName: cert-accounts.{{ .Values.domain }}
+            secretName: cert-wildcard.{{ .Values.domain }}
+      secret:
+        enabled: true
+      kratos:
+        autoMigrate: true
+        development: false
+        config:
+          version: v0.7.1-alpha.1
+          dsn: postgres://postgres:psswd@postgres:5432/kratos?sslmode=disable&max_conns=20&max_idle_conns=4
+          serve:
+            public:
+              base_url: https://accounts.{{ .Values.domain }}
+              cors:
+                enabled: true
+                debug: false
+                allow_credentials: true
+                allowed_origins:
+                - https://{{ .Values.domain }}
+                - https://*.{{ .Values.domain }}
+            admin:
+              base_url: https://kratos.{{ .Values.id }}/
+          selfservice:
+            default_browser_return_url: https://accounts-ui.{{ .Values.domain }}
+            whitelisted_return_urls:
+              - https://accounts-ui.{{ .Values.domain }}
+            methods:
+              password:
+                enabled: true
+            flows:
+              error:
+                ui_url: https://accounts-ui.{{ .Values.domain }}/error
+              settings:
+                ui_url: https://accounts-ui.{{ .Values.domain }}/settings
+                privileged_session_max_age: 15m
+              recovery:
+                enabled: false
+              verification:
+                enabled: false
+              logout:
+                after:
+                  default_browser_return_url: https://accounts-ui.{{ .Values.domain }}/login
+              login:
+                ui_url: https://accounts-ui.{{ .Values.domain }}/login
+                lifespan: 10m
+                after:
+                  password:
+                    default_browser_return_url: https://accounts-ui.{{ .Values.domain }}/
+              registration:
+                lifespan: 10m
+                ui_url: https://accounts-ui.{{ .Values.domain }}/registration
+                after:
+                  password:
+                    hooks:
+                      -
+                        hook: session
+                    default_browser_return_url: https://accounts-ui.{{ .Values.domain }}/
+          log:
+            level: debug
+            format: text
+            leak_sensitive_values: true
+          cookies:
+            path: /
+            same_site: None
+            domain: {{ .Values.domain }}
+          secrets:
+            cookie:
+              - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE
+            # cipher:
+            #   - 32-LONG-SECRET-NOT-SECURE-AT-ALL
+          # ciphers:
+          #   algorithm: xchacha20-poly1305
+          hashers:
+            argon2:
+              parallelism: 1
+              memory: 128MB
+              iterations: 2
+              salt_length: 16
+              key_length: 16
+          identity:
+            default_schema_url: file:///etc/config/identity.schema.json
+          courier:
+            smtp:
+              connection_uri: smtps://test-z1VmkYfYPjgdPRgPFgmeZ31esT9rUgS%40{{ .Values.domain }}:iW%213Kk%5EPPLFrZa%24%21bbpTPN9Wv3b8mvwS6ZJvMLtce%23A2%2A4MotD@mx1.{{ .Values.domain }}
+        identitySchemas:
+          "identity.schema.json": |
+            {
+              "$id": "https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json",
+              "$schema": "http://json-schema.org/draft-07/schema#",
+              "title": "User",
+              "type": "object",
+              "properties": {
+                "traits": {
+                  "type": "object",
+                  "properties": {
+                    "username": {
+                      "type": "string",
+                      "format": "username",
+                      "title": "Username",
+                      "minLength": 3,
+                      "ory.sh/kratos": {
+                        "credentials": {
+                          "password": {
+                            "identifier": true
+                          }
+                        }
+                      }
+                    }
+                  },
+                  "additionalProperties": false
+                }
+              }
+            }
+  - hydra:
+      fullnameOverride: hydra
+      image:
+        repository: giolekva/ory-hydra
+        tag: latest
+        pullPolicy: Always
+      service:
+        admin:
+          enabled: true
+          type: ClusterIP
+          port: 80
+          name: http
+        public:
+          enabled: true
+          type: ClusterIP
+          port: 80
+          name: http
+      ingress:
+        admin:
+          enabled: true
+          className: {{ .Values.id }}-ingress-private
+          hosts:
+          - host: hydra.{{ .Values.id }}
+            paths:
+            - path: /
+              pathType: Prefix
+          annotations:
+            cert-manager.io/cluster-issuer: "{{ .Values.id }}-private"
+            acme.cert-manager.io/http01-edit-in-place: "true"
+          tls:
+          - hosts:
+            - hydra.{{ .Values.id }}
+            secretName: cert-hydra.{{ .Values.id }}
+        public:
+          enabled: true
+          className: nginx
+          hosts:
+          - host: hydra.{{ .Values.domain }}
+            paths:
+            - path: /
+              pathType: Prefix
+          annotations:
+            cert-manager.io/cluster-issuer: "{{ .Values.id }}-public"
+            acme.cert-manager.io/http01-edit-in-place: "true"
+          tls:
+          - hosts:
+            - hydra.{{ .Values.domain }}
+            # secretName: cert-hydra.{{ .Values.domain }}
+            secretName: cert-wildcard.{{ .Values.domain }}
+      secret:
+        enabled: true
+      maester:
+        enabled: true
+        hydraFullnameOverride: hydra
+      hydra-maester:
+        image:
+          repository: giolekva/ory-hydra-maester
+          tag: latest
+          pullPolicy: IfNotPresent
+        adminService:
+          name: hydra
+          port: 80
+      hydra:
+        autoMigrate: true
+        config:
+          version: v1.10.6
+          dsn: postgres://postgres:psswd@postgres:5432/kratos?sslmode=disable&max_conns=20&max_idle_conns=4
+          serve:
+            cookies:
+              same_site_mode: None
+            public:
+              cors:
+                enabled: true
+                debug: false
+                allow_credentials: true
+                allowed_origins:
+                  - https://{{ .Values.domain }}
+                  - https://*.{{ .Values.domain }}
+            admin:
+              # host: localhost
+              cors:
+                allowed_origins:
+                  - https://hydra.{{ .Values.id }}
+              tls:
+                allow_termination_from:
+                  - 0.0.0.0/0
+                  - 10.42.0.0/16
+                  - 10.43.0.0/16
+                  - 111.0.0.1/32
+            tls:
+              allow_termination_from:
+                - 0.0.0.0/0
+                - 10.42.0.0/16
+                - 10.43.0.0/16
+                - 111.0.0.1/32
+          urls:
+            self:
+              public: https://hydra.{{ .Values.domain }}
+              issuer: https://hydra.{{ .Values.domain }}
+            consent: https://accounts-ui.{{ .Values.domain }}/consent
+            login: https://accounts-ui.{{ .Values.domain }}/login
+            logout: https://accounts-ui.{{ .Values.domain }}/logout
+          secrets:
+            system:
+              - youReallyNeedToChangeThis
+          oidc:
+            subject_identifiers:
+              supported_types:
+                - pairwise
+                - public
+              pairwise:
+                salt: youReallyNeedToChangeThis
+          log:
+            level: trace
+            leak_sensitive_values: false
+  - ui:
+      certificateIssuer: {{ .Values.id }}-public
+      ingressClassName: nginx
+      domain: {{ .Values.domain }}
+      internalDomain: {{ .Values.id }}
+      nebula:
+        lighthouse:
+          name: ui-lighthouse
+          internalIP: 111.0.0.1
+          externalIP: 46.49.35.44
+          port: "4243"
+        node:
+          name: ui
+          ipCidr: 111.0.0.2/24
+          secretName: node-ui-cert
+        certificateAuthority:
+          name: {{ .Values.id }}
+          namespace: {{ .Values.id }}-ingress-private
+- name: vaultwarden
+  chart: ../../charts/vaultwarden
+  namespace: {{ .Values.id }}-app-vaultwarden
+  createNamespace: true
+  values:
+  - image:
+      repository: vaultwarden/server
+      tag: 1.22.2
+      pullPolicy: IfNotPresent
+  - storage:
+      size: 1Gi
+  - domain: bitwarden.{{ .Values.id }}
+  - certificateIssuer: {{ .Values.id }}-private
+  - ingressClassName: {{ .Values.id }}-ingress-private
+- name: matrix-storage  # TODO(giolekva): merge with core-auth
+  chart: bitnami/postgresql
+  version: 10.13.5
+  namespace: {{ .Values.id }}-app-matrix
+  createNamespace: true
+  values:
+  - fullnameOverride: postgres
+  - image:
+      repository: arm64v8/postgres
+      tag: 13.4
+  - service:
+      type: ClusterIP
+      port: 5432
+  - postgresqlPassword: psswd
+  - initdbScripts:
+      createdb.sh: |
+        #!/bin/sh
+        createdb -U postgres --encoding=UTF8 --locale=C --template=template0 --owner=postgres matrix
+  - persistence:
+      size: 1Gi
+  - securityContext:
+      enabled: true
+      fsGroup: 0
+  - containerSecurityContext:
+      enabled: true
+      runAsUser: 0
+  - volumePermissions:
+      securityContext:
+        runAsUser: 0
+- name: matrix
+  chart: ../../charts/matrix
+  namespace: {{ .Values.id }}-app-matrix
+  createNamespace: true
+  values:
+  - domain: {{ .Values.domain }}
+  - oauth2:
+      hydraAdmin: http://hydra-admin
+      hydraPublic: https://hydra.{{ .Values.domain }}
+      clientId: matrix
+      clientSecret: ""
+      secretName: oauth2-client
+  - postgresql:
+      host: postgres
+      port: 5432
+      database: matrix
+      user: postgres
+      password: psswd
+  - certificateIssuer: {{ .Values.id }}-public
+  - ingressClassName: nginx
+  - configMerge:
+      configName: config-to-merge
+      fileName: to-merge.yaml
 - name: pihole
   chart: ../../charts/pihole
   namespace: {{ .Values.id }}-app-pihole
@@ -492,7 +492,7 @@
       persistentVolumeClaim:
         enabled: true
         size: 5Gi
-      adminPassword: "admin"
+      adminPassword: admin
       ingress:
         enabled: false
       serviceDhcp:
@@ -515,7 +515,8 @@
           memory: "250M"
   - oauth2:
       clientId: pihole
-      clientSecret: ""
+      clientSecret: {{ .Values.piholeOAuth2ClientSecret }}
+      cookieSecret: {{ .Values.piholeOAuth2CookieSecret }}
       secretName: oauth2-secret
       configName: oauth2-proxy
       hydraAdmin: http://hydra-admin
@@ -526,6 +527,8 @@
 
 environments:
   shveli:
+    secrets:
+    - secrets.shveli.yaml
     values:
     - id: shveli
     - domain: shve.li
diff --git a/helmfile/users/secrets.shveli.yaml b/helmfile/users/secrets.shveli.yaml
new file mode 100644
index 0000000..b611f29
--- /dev/null
+++ b/helmfile/users/secrets.shveli.yaml
@@ -0,0 +1,31 @@
+piholeOAuth2ClientSecret: ENC[AES256_GCM,data:UIfnS8t5,iv:aB6RI+rmst/nQsMpPlJ6IchMinPjpTmHbsP9kRY/HeI=,tag:DNphtOeNz8hAzuDrkWOLvg==,type:str]
+piholeOAuth2CookieSecret: ENC[AES256_GCM,data:g2SWinNvxlkggX1SdV8CMHfZp6xz+46s+YLoH84rveU=,iv:wgW1rUtgJAw9XrshXeBCbwFAsGMwK8sctJ+t5xk8B4Q=,tag:zdulkLmSQ73U3aavZo2YHQ==,type:str]
+sops:
+    kms: []
+    gcp_kms: []
+    azure_kv: []
+    hc_vault: []
+    age: []
+    lastmodified: "2021-11-10T10:26:30Z"
+    mac: ENC[AES256_GCM,data:J4rVsVghKtOpFNI1H00f+60Q4iA8R9T1gcojyrbqpeVvFC69Vw1inzcICImCu5MqH8G6MmQ70ySQw3HP5lP0eRKwivyB2IXqaDznUsHfk36+vUXFoeY0NhR2kfpgEy91ccqvEfF0iKdzYlo/MOnngEsoHoJsyIqYIsKRB0ktN4A=,iv:B8JZtwYfpgCLDTCp1R6WPJUt8JGgoBXDOGuRLUm/3eE=,tag:lNPMfcdxW4jYxqDf4xMTJw==,type:str]
+    pgp:
+        - created_at: "2021-11-10T10:26:29Z"
+          enc: |
+            -----BEGIN PGP MESSAGE-----
+
+            hQGMA8PXnOzdTLRzAQv6Akac2MgQCDyMyiEdUMN7tBWcHEacw5WOCbJgGvgkwbm9
+            P0qRQp6zoxDE5wmeaSTz3VDk1vWRRl30t+QBUoqGk3KdSdG5E+2um4CBDmE2F6QL
+            A5zDrfVdP9LKrfI5W42jt+yTiCdOOP56SSav/I7xqALmyToLE1Owfi71o1a1ZjZf
+            j3Wh0YLlsrFc5ikadGTDxvqAYN5YxHiAA9C2c2giTCIkvE0ZyNfL3V58tjlcAc3e
+            ZKTr3kygpiux/Pq8VjbAlWk8e+VQ1bC/x9hvqyegxH4pRE62OtDmUk7UEwLu1tbf
+            KZZnxcM1FOoEDaBJNC5iplIV/2+jd9jSalzBhxQpnEOK9eYPlYosA8lVYQs5mhPe
+            5v1Nlt1RFFJb44JLknmFjrRMrVceFJ3t4/SGnSYT93zwlcXWNqFeuMSP9muSc+TU
+            OmSx21AZuUj6MmCR26/mWLqg6KOIhz4Ze2/jS3jx/IX1LZY2N+59gZwAquiX/PSo
+            4nFBqmXnRfHL/EqULTLm0lwB//ofG/AahOECrATLIx4ywSgEbZJm4dggHSeay5G5
+            QKNVqmFkmeGLQCzYJNxv+wjJmPOhLydQ09Tv/2AnfPSaw4IatX0aZbncOFPs/7jm
+            dy5N2SJh7wGbkgs4XQ==
+            =BaXl
+            -----END PGP MESSAGE-----
+          fp: 60584680BB48B3CE3FECFFBE7D1302EE361D316A
+    unencrypted_suffix: _unencrypted
+    version: 3.7.1