Installer: migrate internal services to *.p.{domain}
diff --git a/charts/certificate-issuer/templates/gandi-credentials.yaml b/charts/certificate-issuer/templates/gandi-credentials.yaml
new file mode 100644
index 0000000..c14d298
--- /dev/null
+++ b/charts/certificate-issuer/templates/gandi-credentials.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Secret
+type: Opaque
+metadata:
+  name: gandi-credentials
+  namespace: {{ .Release.Namespace }}
+data:
+  api-token: {{ .Values.private.gandiAPIToken | b64enc }}
diff --git a/charts/certificate-issuer/templates/gandi-webhook-secret-reader.yaml b/charts/certificate-issuer/templates/gandi-webhook-secret-reader.yaml
new file mode 100644
index 0000000..b3d1491
--- /dev/null
+++ b/charts/certificate-issuer/templates/gandi-webhook-secret-reader.yaml
@@ -0,0 +1,26 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: cert-manager-gandi-webhook-secret-reader
+  namespace: {{ .Release.Namespace }}
+rules:
+- apiGroups:
+  - ""
+  resources:
+  - secrets
+  verbs:
+  - get
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: cert-manager-gandi-webhook-secret-reader
+  namespace: {{ .Release.Namespace }}
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: cert-manager-gandi-webhook-secret-reader
+subjects:
+- kind: ServiceAccount
+  name: {{ .Values.certManager.gandiWebhookSecretReader }}
+  namespace: {{ .Values.certManager.namespace }}
diff --git a/charts/certificate-issuer/templates/private.yaml b/charts/certificate-issuer/templates/private.yaml
index 1b5c06d..1ab6e90 100644
--- a/charts/certificate-issuer/templates/private.yaml
+++ b/charts/certificate-issuer/templates/private.yaml
@@ -1,33 +1,20 @@
 apiVersion: cert-manager.io/v1
-kind: ClusterIssuer
-metadata:
-  name: {{ .Values.private.name }}-bootstrap
-  namespace: {{ .Release.Namespace }}
-spec:
-  selfSigned: {}
----
-apiVersion: cert-manager.io/v1
-kind: Certificate
-metadata:
-  name: {{ .Values.private.name }}-ca-root
-  namespace: {{ .Release.Namespace }}
-spec:
-  isCA: true
-  commonName: {{ .Values.private.name }}-ca-root
-  secretName: {{ .Values.private.name }}-ca-root
-  privateKey:
-    algorithm: ECDSA
-    size: 256
-  issuerRef:
-    name: {{ .Values.private.name }}-bootstrap
-    kind: ClusterIssuer
-    group: cert-manager.io
----
-apiVersion: cert-manager.io/v1
-kind: ClusterIssuer
+kind: Issuer
 metadata:
   name: {{ .Values.private.name }}
   namespace: {{ .Release.Namespace }}
 spec:
-  ca:
-    secretName: {{ .Values.private.name }}-ca-root
+  acme:
+    server: {{ .Values.private.server }}
+    email: {{ .Values.private.contactEmail }}
+    privateKeySecretRef:
+      name: issuer-{{ .Values.private.name }}-account-key
+    solvers:
+    - dns01:
+        webhook:
+          groupName: acme.bwolf.me
+          solverName: gandi
+          config:
+            apiKeySecretRef:
+              key: api-token
+              name: gandi-credentials
diff --git a/charts/certificate-issuer/templates/public-staging.yaml b/charts/certificate-issuer/templates/public-staging.yaml
deleted file mode 100644
index 888b350..0000000
--- a/charts/certificate-issuer/templates/public-staging.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-apiVersion: cert-manager.io/v1
-kind: ClusterIssuer
-metadata:
-  name: {{ .Values.public.name }}-staging
-  namespace: {{ .Release.Namespace }}
-spec:
-  acme:
-    server: {{ .Values.public.stagingServer }}
-    email: {{ .Values.public.contactEmail }}
-    privateKeySecretRef:
-      name: cluster-issuer-{{ .Values.public.name }}-account-key
-    solvers:
-    - selector: {}
-      http01:
-        ingress:
-          class: {{ .Values.public.ingressClass }}
diff --git a/charts/certificate-issuer/templates/root-ca-server.yaml b/charts/certificate-issuer/templates/root-ca-server.yaml
deleted file mode 100644
index 06f38d8..0000000
--- a/charts/certificate-issuer/templates/root-ca-server.yaml
+++ /dev/null
@@ -1,85 +0,0 @@
-# TODO(giolekva): move to ingerss-nginx-private namespace
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: {{ .Values.private.name }}-root-ca
-  namespace: {{ .Release.Namespace }}
-spec:
-  selector:
-    matchLabels:
-      app: {{ .Values.private.name }}-root-ca
-  replicas: 1
-  template:
-    metadata:
-      labels:
-        app: {{ .Values.private.name }}-root-ca
-    spec:
-      volumes:
-      - name: root-ca-secret
-        secret:
-          secretName: {{ .Values.private.name }}-ca-root
-          items:
-          - key: ca.crt
-            path: private-root-ca.crt
-      containers:
-      - name: file-server
-        image: giolekva/static-file-server:latest
-        imagePullPolicy: Always
-        ports:
-        - name: http
-          containerPort: 80
-        command: ["static-file-server"]
-        args: ["-port=80", "-dir=/etc/static-file-server/data"]
-        volumeMounts:
-        - name: root-ca-secret
-          mountPath: /etc/static-file-server/data/
-          readOnly: true
-        resources:
-          requests:
-            memory: "10Mi"
-            cpu: "10m"
-          limits:
-            memory: "20Mi"
-            cpu: "100m"
-      tolerations:
-      - key: "pcloud"
-        operator: "Equal"
-        value: "role"
-        effect: "NoSchedule"
----
-apiVersion: v1
-kind: Service
-metadata:
-  name: {{ .Values.private.name }}-root-ca
-  namespace: {{ .Release.Namespace }}
-spec:
-  type: ClusterIP
-  selector:
-    app: {{ .Values.private.name }}-root-ca
-  ports:
-    - name: http
-      port: 80
-      targetPort: http
-      protocol: TCP
----
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
-  name: {{ .Values.private.name }}-root-ca
-  namespace: {{ .Release.Namespace }}
-  annotations:
-    nginx.ingress.kubernetes.io/ssl-redirect: "false"
-spec:
-  ingressClassName: {{ .Values.private.ingressClassName }}
-  rules:
-  - host: root-ca.{{ .Values.private.domain }}
-    http:
-      paths:
-      - pathType: Prefix
-        path: "/"
-        backend:
-          service:
-            name: {{ .Values.private.name }}-root-ca
-            port:
-              name: http
diff --git a/charts/certificate-issuer/templates/wildcard-certificate-private.yaml b/charts/certificate-issuer/templates/wildcard-certificate-private.yaml
new file mode 100644
index 0000000..f869875
--- /dev/null
+++ b/charts/certificate-issuer/templates/wildcard-certificate-private.yaml
@@ -0,0 +1,12 @@
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+  name: wildcard-{{ .Values.private.domain }}
+  namespace: {{ .Release.Namespace }}
+spec:
+  dnsNames:
+  - '*.{{ .Values.private.domain }}'
+  issuerRef:
+    name: {{ .Values.private.name }}
+    kind: Issuer
+  secretName: cert-wildcard.{{ .Values.private.domain }}
diff --git a/charts/certificate-issuer/values.yaml b/charts/certificate-issuer/values.yaml
index 57a0d43..904b5ac 100644
--- a/charts/certificate-issuer/values.yaml
+++ b/charts/certificate-issuer/values.yaml
@@ -1,3 +1,6 @@
+certManager:
+  namespace: cert-manager
+  gandiWebhookSecretReader: cert-manager-webhook-gandi
 public:
   name: letsencrypt-prod
   server: https://acme-v02.api.letsencrypt.org/directory
@@ -5,5 +8,8 @@
   ingressClass: ingress-nginx
 private:
   name: selfsigned-private
-  domain: pcloud
+  server: https://acme-v02.api.letsencrypt.org/directory
+  contactEmail: admin@example.com
+  gandiAPIToken: token
+  domain: p.example.com
 
diff --git a/charts/matrix/templates/matrix.yaml b/charts/matrix/templates/matrix.yaml
index 6770c91..49c97ac 100644
--- a/charts/matrix/templates/matrix.yaml
+++ b/charts/matrix/templates/matrix.yaml
@@ -47,9 +47,9 @@
 metadata:
   name: ingress
   namespace: {{ .Release.Namespace }}
-  annotations:
-    cert-manager.io/cluster-issuer: {{ .Values.certificateIssuer }}
-    acme.cert-manager.io/http01-edit-in-place: "true"
+  # annotations:
+  #   cert-manager.io/cluster-issuer: {{ .Values.certificateIssuer }}
+  #   acme.cert-manager.io/http01-edit-in-place: "true"
 spec:
   ingressClassName: {{ .Values.ingressClassName }}
   tls:
diff --git a/core/nebula/controller/controllers/ca.go b/core/nebula/controller/controllers/ca.go
index d31b6cd..f442db6 100644
--- a/core/nebula/controller/controllers/ca.go
+++ b/core/nebula/controller/controllers/ca.go
@@ -215,7 +215,7 @@
 		return nil
 	}
 	ca, err := c.getCA(node.Spec.CANamespace, node.Spec.CAName)
-	if ca.Status.State != nebulav1.NebulaCAStateReady {
+	if ca == nil || ca.Status.State != nebulav1.NebulaCAStateReady {
 		return fmt.Errorf("Referenced CA %s is not ready yet.", node.Spec.CAName)
 	}
 	caSecret, err := c.getSecret(ca.Namespace, ca.Spec.SecretName)
diff --git a/helmfile/users/helmfile.yaml b/helmfile/users/helmfile.yaml
index 75d62ee..0ab0ed4 100644
--- a/helmfile/users/helmfile.yaml
+++ b/helmfile/users/helmfile.yaml
@@ -76,19 +76,26 @@
       - 53: {{ .Values.id }}-app-pihole/pihole-dns-tcp:53
 - name: certificate-issuer
   chart: ../../charts/certificate-issuer
-  namespace: {{ .Values.certManagerNamespace }} # {{ .Values.id }}-ingress-private
+  namespace: {{ .Values.id }}-ingress-private
   createNamespace: true
   values:
+  - certManager:
+      namespace: cert-manager
+      gandiWebhookSecretReader: cert-manager-webhook-gandi
   - public:
       name: {{ .Values.id }}-public
       server: https://acme-v02.api.letsencrypt.org/directory
+      domain: {{ .Values.domain }}
       stagingServer: https://acme-staging-v02.api.letsencrypt.org/directory
       contactEmail: {{ .Values.contactEmail }}
       ingressClass: nginx
   - private:
       name: {{ .Values.id }}-private
-      domain: {{ .Values.id }}
+      server: https://acme-v02.api.letsencrypt.org/directory
+      domain: p.{{ .Values.domain }}
+      contactEmail: {{ .Values.contactEmail }}
       ingressClassName: {{ .Values.id }}-ingress-private
+      gandiAPIToken: {{ .Values.gandiAPIToken }}
 - name: core-auth-storage  # TODO(giolekva): merge with core-auth
   chart: bitnami/postgresql
   version: 10.13.5
@@ -142,17 +149,13 @@
           enabled: true
           className: {{ .Values.id }}-ingress-private
           hosts:
-          - host: kratos.{{ .Values.id }}
+          - host: kratos.p.{{ .Values.domain }}
             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 }}
+            - kratos.p.{{ .Values.domain }}
         public:
           enabled: true
           className: nginx
@@ -161,9 +164,9 @@
             paths:
             - path: /
               pathType: Prefix
-          annotations:
-            cert-manager.io/cluster-issuer: "{{ .Values.id }}-public"
-            acme.cert-manager.io/http01-edit-in-place: "true"
+          # annotations:
+          #   cert-manager.io/cluster-issuer: "{{ .Values.id }}-public"
+          #   acme.cert-manager.io/http01-edit-in-place: "true"
           tls:
           - hosts:
             - accounts.{{ .Values.domain }}
@@ -188,7 +191,7 @@
                 - https://{{ .Values.domain }}
                 - https://*.{{ .Values.domain }}
             admin:
-              base_url: https://kratos.{{ .Values.id }}/
+              base_url: https://kratos.p.{{ .Values.domain }}/
           selfservice:
             default_browser_return_url: https://accounts-ui.{{ .Values.domain }}
             whitelisted_return_urls:
@@ -302,17 +305,13 @@
           enabled: true
           className: {{ .Values.id }}-ingress-private
           hosts:
-          - host: hydra.{{ .Values.id }}
+          - host: hydra.p.{{ .Values.domain }}
             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 }}
+            - hydra.p.{{ .Values.domain }}
         public:
           enabled: true
           className: nginx
@@ -321,9 +320,9 @@
             paths:
             - path: /
               pathType: Prefix
-          annotations:
-            cert-manager.io/cluster-issuer: "{{ .Values.id }}-public"
-            acme.cert-manager.io/http01-edit-in-place: "true"
+          # annotations:
+          #   cert-manager.io/cluster-issuer: "{{ .Values.id }}-public"
+          #   acme.cert-manager.io/http01-edit-in-place: "true"
           tls:
           - hosts:
             - hydra.{{ .Values.domain }}
@@ -362,7 +361,7 @@
               # host: localhost
               cors:
                 allowed_origins:
-                  - https://hydra.{{ .Values.id }}
+                  - https://hydra.p.{{ .Values.domain }}
               tls:
                 allow_termination_from:
                   - 0.0.0.0/0
@@ -399,7 +398,7 @@
       certificateIssuer: {{ .Values.id }}-public
       ingressClassName: nginx
       domain: {{ .Values.domain }}
-      internalDomain: {{ .Values.id }}
+      internalDomain: p.{{ .Values.domain }}
       nebula:
         lighthouse:
           name: ui-lighthouse
@@ -466,7 +465,7 @@
       hydraAdmin: http://hydra-admin
       hydraPublic: https://hydra.{{ .Values.domain }}
       clientId: matrix
-      clientSecret: ""
+      clientSecret: {{ .Values.matrixOAuth2ClientSecret }}
       secretName: oauth2-client
   - postgresql:
       host: postgres
diff --git a/helmfile/users/secrets.shveli.yaml b/helmfile/users/secrets.shveli.yaml
index b611f29..77740f8 100644
--- a/helmfile/users/secrets.shveli.yaml
+++ b/helmfile/users/secrets.shveli.yaml
@@ -1,30 +1,32 @@
-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]
+gandiAPIToken: ENC[AES256_GCM,data:H0ty9QYwOd/hLaTCb7gsAwJoPrzOr8tZ,iv:Q0SgKzxb27FnqSUj9xFkm3if2QuTcf8TTFuOqek0BKw=,tag:sIzh6EyU0S8YzdiXZRsOIw==,type:str]
+piholeOAuth2ClientSecret: ENC[AES256_GCM,data:B+Hf5nMk,iv:CFh7h7pvJle7cDt+kqg6T7K4rvjvZ1J7hob10gw1ZSo=,tag:1UR6WtAJ84GA371ScJ927Q==,type:str]
+piholeOAuth2CookieSecret: ENC[AES256_GCM,data:lIk/S8koxT7P7EMiWMjGYu7HdGxM21Ew/prbCRkWiJ4=,iv:rWzXCDKHnIAv05iz2YpkJBlP4RpJuCXxEWD+fU1g8d8=,tag:IFxnFw06Leh4UsvI2IvB4w==,type:str]
+matrixOAuth2ClientSecret: ENC[AES256_GCM,data:aMOeYkt5/RoQ9H+l0TiizMoDK0YJEVTEXU8ymcVwbeU=,iv:zA1vETe7Q5L9BxiXIcV/rsomMFcgtBWS+0Vr4UOehzY=,tag:AcEiY1O6LLVgocqwFUZIEg==,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]
+    lastmodified: "2021-11-10T17:12:41Z"
+    mac: ENC[AES256_GCM,data:WglXHJLxcHMaNxCZhrgIqyX3J81M+bQbUNr7k44gYZ0zPIGnE9CntG+2Ua6QppncrHNjLuxLmdyGnNCQRdEZEH2vpLO6+vtL2uPV42K1OHs1xdtTeAcsoxyw+CJUe209C2XhWhCyPNxXqCPckB11M1z2V925VyutxuRj1qV/cyg=,iv:BeSFTZDrQvhE3wvMBPD9UEzo7KGUxrdflO2bKfyIqCg=,tag:AEOYZYeemflwSbqIhtKCmQ==,type:str]
     pgp:
-        - created_at: "2021-11-10T10:26:29Z"
+        - created_at: "2021-11-10T17:12:40Z"
           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
+            hQGMA8PXnOzdTLRzAQv/V9Yx09TNDRNZhn44ov1pU1sx+z2zW7ESP1MD9flqkIJ2
+            +9LsjRAAaLayDRjdw42mSBK+IsnnMMkORe+/xGbsRoeBDSZu6Xk9neMUrpvn45uB
+            ICJ2/ejpPq4hew2O8cssZKHByerNAcWCNXULWDB9CigJm5s6Ohq9hY9RhFBIxzjK
+            wJ6MflskGseTmGoXjJ7tQmO78xQDWW+gAN5BhqvtA8JKDcBGjD+Gj2znxGICO/3v
+            7NFZ//z05LU2zaLDQ0c+SI4JJMuumV2Vuhxl8jXSAdFyya1JBiyUef8E81c5oO3M
+            ybJLjKv8hGkytfb4mB7XEomTwLWWPdpg8YZbVAB7AFfKeaWraj3j7ScsijEdmg1C
+            LESbr9mDC1HvFMtwJ3TClv77lEE1iwcqY8w/YQHxhwePLapR8gzJF73ZPxPf8UxW
+            dEHHtN55cTTw48itQSY5agbGz6qYPM01ListEZ8m3QMEBHDI56iZVfJizHZ9B1TE
+            wH/iEQaR/4GsNRXsw3Ka0l4B1+FAVRuyZMUk0V53ctCB/6T40STXzHrYExPn8OH9
+            XGhQ8t++9iMu5ZSfFSBOqXt8pAIsVmdnNjOQBz67ffPmVVn8yC4RZJrLCjITED+G
+            RvBcMyZf5NxSlnp9Z5US
+            =K51p
             -----END PGP MESSAGE-----
           fp: 60584680BB48B3CE3FECFFBE7D1302EE361D316A
     unencrypted_suffix: _unencrypted