Installer: configure cert-manager(-webhook-gandi), kubed as part of infrastructure
diff --git a/apps/bwolf-gandi/.gitignore b/apps/bwolf-gandi/.gitignore
new file mode 100644
index 0000000..fcff3af
--- /dev/null
+++ b/apps/bwolf-gandi/.gitignore
@@ -0,0 +1,2 @@
+cert-manager-webhook-gandi
+webhook
diff --git a/apps/bwolf-gandi/Dockerfile b/apps/bwolf-gandi/Dockerfile
new file mode 100644
index 0000000..6fee6aa
--- /dev/null
+++ b/apps/bwolf-gandi/Dockerfile
@@ -0,0 +1,8 @@
+FROM alpine:3.9
+
+RUN apk add --no-cache ca-certificates
+
+COPY webhook /usr/local/bin/webhook
+RUN chmod +x /usr/local/bin/webhook
+
+ENTRYPOINT ["webhook"]
diff --git a/apps/bwolf-gandi/Makefile b/apps/bwolf-gandi/Makefile
new file mode 100644
index 0000000..cdfc936
--- /dev/null
+++ b/apps/bwolf-gandi/Makefile
@@ -0,0 +1,24 @@
+IMAGE_NAME := "giolekva/cert-manager-webhook-gandi"
+IMAGE_TAG := "v0.2.0"
+
+checkout:
+	git clone --depth 1 --branch v0.2.0 https://github.com/bwolf/cert-manager-webhook-gandi.git
+
+clean:
+	rm -f webhook
+
+build: export CGO_ENABLED=0
+build: export GO111MODULE=on
+build: clean
+	cd cert-manager-webhook-gandi && go build -o ../webhook -ldflags '-w -extldflags "-static"' .
+
+image: build
+	docker build --tag=$(IMAGE_NAME):$(IMAGE_TAG) . --platform=linux/arm64
+
+push: image
+	docker push $(IMAGE_NAME):$(IMAGE_TAG)
+
+
+push_arm64: export GOOS=linux
+push_arm64: export GOARCH=arm64
+push_arm64: push
diff --git a/charts/auth/templates/ui.yaml b/charts/auth/templates/ui.yaml
index 8c07d74..ee79d7e 100644
--- a/charts/auth/templates/ui.yaml
+++ b/charts/auth/templates/ui.yaml
@@ -18,16 +18,16 @@
 metadata:
   name: ui
   namespace: {{ .Release.Namespace }}
-  # annotations:
-  #   cert-manager.io/cluster-issuer: {{ .Values.ui.certificateIssuer }}
-  #   acme.cert-manager.io/http01-edit-in-place: "true"
+  annotations:
+    cert-manager.io/cluster-issuer: {{ .Values.ui.certificateIssuer }}
+    acme.cert-manager.io/http01-edit-in-place: "true"
 spec:
   ingressClassName: {{ .Values.ui.ingressClassName }}
   tls:
   - hosts:
     - accounts-ui.{{ .Values.ui.domain }}
-    # secretName: cert-accounts-ui.{{ .Values.ui.domain }}
-    secretName: cert-wildcard.{{ .Values.ui.domain }}
+    secretName: cert-accounts-ui.{{ .Values.ui.domain }}
+    # secretName: cert-wildcard.{{ .Values.ui.domain }}
   rules:
   - host: accounts-ui.{{ .Values.ui.domain }}
     http:
diff --git a/charts/certificate-issuer/templates/wildcard-certificate-private.yaml b/charts/certificate-issuer/templates/wildcard-certificate-private.yaml
index f869875..408b76c 100644
--- a/charts/certificate-issuer/templates/wildcard-certificate-private.yaml
+++ b/charts/certificate-issuer/templates/wildcard-certificate-private.yaml
@@ -10,3 +10,6 @@
     name: {{ .Values.private.name }}
     kind: Issuer
   secretName: cert-wildcard.{{ .Values.private.domain }}
+  secretTemplate:
+    annotations:
+      kubed.appscode.com/sync: "pcloud-instance-id={{ .Values.pcloudInstanceId }}"
diff --git a/charts/certificate-issuer/templates/www-certificate-public.yaml b/charts/certificate-issuer/templates/www-certificate-public.yaml
new file mode 100644
index 0000000..d6b4dc6
--- /dev/null
+++ b/charts/certificate-issuer/templates/www-certificate-public.yaml
@@ -0,0 +1,16 @@
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+  name: www-{{ .Values.public.domain }}
+  namespace: {{ .Release.Namespace }}
+spec:
+  dnsNames:
+  - '{{ .Values.public.domain }}'
+  - 'www.{{ .Values.public.domain }}'
+  issuerRef:
+    name: {{ .Values.public.name }}
+    kind: ClusterIssuer
+  secretName: cert-www.{{ .Values.private.domain }}
+  secretTemplate:
+    annotations:
+      kubed.appscode.com/sync: "pcloud-instance-id={{ .Values.pcloudInstanceId }}"
diff --git a/charts/certificate-issuer/values.yaml b/charts/certificate-issuer/values.yaml
index 904b5ac..b9660ae 100644
--- a/charts/certificate-issuer/values.yaml
+++ b/charts/certificate-issuer/values.yaml
@@ -1,3 +1,4 @@
+pcloudInstanceId: example
 certManager:
   namespace: cert-manager
   gandiWebhookSecretReader: cert-manager-webhook-gandi
@@ -6,6 +7,7 @@
   server: https://acme-v02.api.letsencrypt.org/directory
   contactEmail: admin@example.com
   ingressClass: ingress-nginx
+  domain: example.com
 private:
   name: selfsigned-private
   server: https://acme-v02.api.letsencrypt.org/directory
diff --git a/charts/matrix/templates/matrix.yaml b/charts/matrix/templates/matrix.yaml
index 49c97ac..d9bd47f 100644
--- a/charts/matrix/templates/matrix.yaml
+++ b/charts/matrix/templates/matrix.yaml
@@ -47,16 +47,16 @@
 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:
   - hosts:
     - matrix.{{ .Values.domain }}
-    # secretName: cert-matrix.{{ .Values.domain }}
-    secretName: cert-wildcard.{{ .Values.domain }}
+    secretName: cert-matrix.{{ .Values.domain }}
+    # secretName: cert-wildcard.{{ .Values.domain }}
   rules:
   - host: matrix.{{ .Values.domain }}
     http:
diff --git a/charts/matrix/templates/well-known.yaml b/charts/matrix/templates/well-known.yaml
index ef79b05..218e335 100644
--- a/charts/matrix/templates/well-known.yaml
+++ b/charts/matrix/templates/well-known.yaml
@@ -27,7 +27,7 @@
   tls:
   - hosts:
     - {{ .Values.domain }}
-    secretName: cert-{{ .Values.domain }}
+    secretName: cert-www.{{ .Values.domain }}
   - hosts:
     - www.{{ .Values.domain }}
     secretName: cert-www.{{ .Values.domain }}
diff --git a/charts/namespaces/.helmignore b/charts/namespaces/.helmignore
new file mode 100644
index 0000000..0e8a0eb
--- /dev/null
+++ b/charts/namespaces/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/charts/namespaces/Chart.yaml b/charts/namespaces/Chart.yaml
new file mode 100644
index 0000000..9dd3c96
--- /dev/null
+++ b/charts/namespaces/Chart.yaml
@@ -0,0 +1,6 @@
+apiVersion: v2
+name: namespaces
+description: A Helm chart for creating PCloud namespaces
+type: application
+version: 0.0.1
+appVersion: "0.0.1"
diff --git a/charts/namespaces/templates/namespace.yaml b/charts/namespaces/templates/namespace.yaml
new file mode 100644
index 0000000..5ce22a0
--- /dev/null
+++ b/charts/namespaces/templates/namespace.yaml
@@ -0,0 +1,10 @@
+{{ $id := .Values.pcloudInstanceId }}
+{{ range .Values.namespaces }}
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: {{ $id }}-{{ . }}
+  labels:
+    pcloud-instance-id: {{ $id }}
+---
+{{ end }}
diff --git a/charts/namespaces/values.yaml b/charts/namespaces/values.yaml
new file mode 100644
index 0000000..d17d865
--- /dev/null
+++ b/charts/namespaces/values.yaml
@@ -0,0 +1,4 @@
+pcloudInstanceId: example
+namespaces:
+- foo
+- bar
diff --git a/helmfile/base/helmfile.yaml b/helmfile/base/helmfile.yaml
index d9da8e9..1a053d4 100644
--- a/helmfile/base/helmfile.yaml
+++ b/helmfile/base/helmfile.yaml
@@ -1,37 +1,109 @@
 repositories:
+- name: appscode
+  url: https://charts.appscode.com/stable/
 - name: ingress-nginx
   url: https://kubernetes.github.io/ingress-nginx
+- name: jetstack
+  url: https://charts.jetstack.io
+- name: bwolf
+  url: https://bwolf.github.io/cert-manager-webhook-gandi
 
 helmDefaults:
   tillerless: true
+  waitForJobs: false
+  createNamespace: true
 
 releases:
-- name: ingress-public
-  chart: ingress-nginx/ingress-nginx
-  version: 4.0.3
-  namespace: {{ .Values.id }}-ingress-public
-  createNamespace: true
+- name: kubed
+  chart: appscode/kubed
+  version: v0.12.0
+  namespace: {{ .Values.name }}-kubed
   values:
-  - fullnameOverride: {{ .Values.id }}-ingress-public
-  - controller:
-      service:
-        type: LoadBalancer
-      ingressClassByName: true
-      ingressClassResource:
-        name: {{ .Values.id }}-ingress-public
-        enabled: true
-        default: false
-        controllerValue: k8s.io/{{ .Values.id }}-ingress-public
-      config:
-        proxy-body-size: 100M
-      tcp:
-      - 25: {{ .Values.id }}-app-maddy/maddy:25
-      - 143: {{ .Values.id }}-app-maddy/maddy:143
-      - 993: {{ .Values.id }}-app-maddy/maddy:993
-      - 587: {{ .Values.id }}-app-maddy/maddy:587
-      - 465: {{ .Values.id }}-app-maddy/maddy:465
+  - enableAnalytics: false
+  - fullnameOverride: {{ .Values.name }}-kubed
+  - operator:
+      registry: appscode
+      repository: kubed
+      tag: v0.12.0
+  - criticalAddon: true
+  - config:
+      clusterName: {{ .Values.name }}
+- name: cert-manager
+  chart: jetstack/cert-manager
+  version: v1.6
+  namespace: {{ .Values.name }}-cert-manager
+  values:
+  - installCRDs: true
+  - fullnameOverride: {{ .Values.name}}-cert-manager
+  - image:
+      tag: v1.6.1
+      pullPolicy: IfNotPresent
+  - resources:
+      requests:
+        cpu: "100m"
+        memory: "50M"
+      limits:
+        cpu: "250m"
+        memory: "150M"
+  - tolerations:
+    - key: "pcloud"
+      operator: "Equal"
+      value: "role"
+      effect: "NoSchedule"
+  - cainjector:
+      resources:
+        requests:
+          cpu: "100m"
+          memory: "50M"
+        limits:
+          cpu: "250m"
+          memory: "150M"
+      tolerations:
+      - key: "pcloud"
+        operator: "Equal"
+        value: "role"
+        effect: "NoSchedule"
+  - webhook:
+      resources:
+        requests:
+          cpu: "100m"
+          memory: "50M"
+        limits:
+          cpu: "250m"
+          memory: "150M"
+      tolerations:
+      - key: "pcloud"
+        operator: "Equal"
+        value: "role"
+        effect: "NoSchedule"
+- name: cert-manager-gandi
+  chart: bwolf/cert-manager-webhook-gandi
+  version: v0.2.0
+  namespace: {{ .Values.name }}-cert-manager
+  values:
+  - certManager:
+      namespace: {{ .Values.name }}-cert-manager
+      serviceAccountName: {{ .Values.name }}-cert-manager
+  - fullnameOverride: {{ .Values.name }}-cert-manager-webhook-gandi
+  - image:
+      repository: giolekva/cert-manager-webhook-gandi
+      tag: v0.2.0
+      pullPolicy: IfNotPresent
+  - logLevel: 2
+  - resources:
+      requests:
+        cpu: "100m"
+        memory: "50M"
+      limits:
+        cpu: "250m"
+        memory: "150M"
+  - tolerations:
+    - key: "pcloud"
+      operator: "Equal"
+      value: "role"
+      effect: "NoSchedule"
 
 environments:
-  shveli:
+  prod:
     values:
-      - id: shveli
+      - name: pcloud
diff --git a/helmfile/users/helmfile.yaml b/helmfile/users/helmfile.yaml
index 4902095..aad46b7 100644
--- a/helmfile/users/helmfile.yaml
+++ b/helmfile/users/helmfile.yaml
@@ -7,12 +7,25 @@
 helmDefaults:
   tillerless: true
   waitForJobs: false
+  createNamespace: false
 
 releases:
+- name: namespaces
+  chart: ../../charts/namespaces
+  namespace: {{ .Values.id }}
+  createNamespace: true
+  values:
+  - pcloudInstanceId: {{ .Values.id }}
+  - namespaces:
+      - app-maddy
+      - app-matrix
+      - app-pihole
+      - app-vaultwarden
+      - core-auth
+      - ingress-private
 - name: vpn-mesh-config
   chart: ../../charts/vpn-mesh-config
   namespace: {{ .Values.id }}-ingress-private
-  createNamespace: true
   values:
   - certificateAuthority:
       name: {{ .Values.id }}
@@ -25,7 +38,6 @@
   chart: ingress-nginx/ingress-nginx
   version: 4.0.3
   namespace: {{ .Values.id }}-ingress-private
-  createNamespace: true
   values:
   - fullnameOverride: {{ .Values.id }}-nginx-private
   - controller:
@@ -81,11 +93,11 @@
 - name: certificate-issuer
   chart: ../../charts/certificate-issuer
   namespace: {{ .Values.id }}-ingress-private
-  createNamespace: true
   values:
+  - pcloudInstanceId: {{ .Values.id }}
   - certManager:
-      namespace: cert-manager
-      gandiWebhookSecretReader: cert-manager-webhook-gandi
+      namespace: {{ .Values.pcloudEnvName }}-cert-manager
+      gandiWebhookSecretReader: {{ .Values.pcloudEnvName }}-cert-manager-webhook-gandi
   - public:
       name: {{ .Values.id }}-public
       server: https://acme-v02.api.letsencrypt.org/directory
@@ -104,7 +116,6 @@
   chart: bitnami/postgresql
   version: 10.13.5
   namespace: {{ .Values.id }}-core-auth
-  createNamespace: true
   values:
   - fullnameOverride: postgres
   - image:
@@ -129,7 +140,6 @@
 - name: core-auth
   chart: ../../charts/auth
   namespace: {{ .Values.id }}-core-auth
-  createNamespace: true
   values:
   - kratos:
       fullnameOverride: kratos
@@ -168,14 +178,14 @@
             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 }}
-            # secretName: cert-accounts.{{ .Values.domain }}
-            secretName: cert-wildcard.{{ .Values.domain }}
+            secretName: cert-accounts.{{ .Values.domain }}
+            # secretName: cert-wildcard.{{ .Values.domain }}
       secret:
         enabled: true
       kratos:
@@ -324,14 +334,14 @@
             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 }}
-            # secretName: cert-hydra.{{ .Values.domain }}
-            secretName: cert-wildcard.{{ .Values.domain }}
+            secretName: cert-hydra.{{ .Values.domain }}
+            # secretName: cert-wildcard.{{ .Values.domain }}
       secret:
         enabled: true
       maester:
@@ -419,7 +429,6 @@
 - name: vaultwarden
   chart: ../../charts/vaultwarden
   namespace: {{ .Values.id }}-app-vaultwarden
-  createNamespace: true
   values:
   - image:
       repository: vaultwarden/server
@@ -434,7 +443,6 @@
   chart: bitnami/postgresql
   version: 10.13.5
   namespace: {{ .Values.id }}-app-matrix
-  createNamespace: true
   values:
   - fullnameOverride: postgres
   - image:
@@ -462,7 +470,6 @@
 - name: matrix
   chart: ../../charts/matrix
   namespace: {{ .Values.id }}-app-matrix
-  createNamespace: true
   values:
   - domain: {{ .Values.domain }}
   - oauth2:
@@ -485,7 +492,6 @@
 - name: pihole
   chart: ../../charts/pihole
   namespace: {{ .Values.id }}-app-pihole
-  createNamespace: true
   values:
   - domain: {{ .Values.domain }}
   - pihole:
@@ -530,7 +536,6 @@
 - name: maddy
   chart: ../../charts/maddy
   namespace: {{ .Values.id }}-app-maddy
-  createNamespace: true
   values:
   - ingress:
       private:
@@ -551,6 +556,7 @@
     secrets:
     - secrets.shveli.yaml
     values:
+    - pcloudEnvName: pcloud
     - id: shveli
     - domain: shve.li
     - contactEmail: giolekva@gmail.com