DNS: run separate CoreDNS instance for each PCloud env.

Previously shared CoreDNS instance was used to handle all domains. This has multiple downsides, most important which is security. For example DNS-Sec keys of all domains were persisted on the same shared volume. Also key itself was generated by PCloud env-manager as part of bootstrapping new env. Which is counter to the main aspirations of PCloud, that environment internal private data must not leak outside of the environment.

With new approach implemented in this change, environment starts up it’s own CoreDNS and DNS record manager servers. Manager generates dns-sec keys internally and only exposes public information to the outside world. PCloud infrastructure runes another instance of CoreDNS which acts as a proxy service forwarding requests to individual environments based an requested domain.

This simplifies DNS based TLS challenge solvers, as private certificate issuer of each env will point directly to the DNS record manager of the same environment.

Change-Id: Ifb0f36d2a133e3b53da22030cc7d6b9099136b3d
diff --git a/charts/certificate-issuer-private/templates/issuer.yaml b/charts/certificate-issuer-private/templates/issuer.yaml
index e05ff82..bc29bed 100644
--- a/charts/certificate-issuer-private/templates/issuer.yaml
+++ b/charts/certificate-issuer-private/templates/issuer.yaml
@@ -15,5 +15,5 @@
           groupName: dodo.cloud # TODO(gio): configurable, this and one below
           solverName: dns-resolver-pcloud
           config:
-            apiConfigMapName: {{ .Values.apiConfigMap.name }}
-            apiConfigMapNamespace: {{ .Values.apiConfigMap.namespace }}
+            createTXTAddr: {{ .Values.config.createTXTAddr }}
+            deleteTXTAddr: {{ .Values.config.deleteTXTAddr }}
diff --git a/charts/certificate-issuer-private/values.yaml b/charts/certificate-issuer-private/values.yaml
index e332987..fd0d9bd 100644
--- a/charts/certificate-issuer-private/values.yaml
+++ b/charts/certificate-issuer-private/values.yaml
@@ -4,6 +4,6 @@
   contactEmail: admin@example.com
   gandiAPIToken: token
   domain: p.example.com
-apiConfigMap:
-  name: api-config
-  namespace: pcloud-dns-zone-manager
+config:
+  createTXTAddr: http://10.44.0.1/create-txt-record
+  deleteTXTAddr: http://10.44.0.1/delete-txt-record
diff --git a/charts/dns-api/.helmignore b/charts/dns-api/.helmignore
new file mode 100644
index 0000000..0e8a0eb
--- /dev/null
+++ b/charts/dns-api/.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/dns-api/Chart.yaml b/charts/dns-api/Chart.yaml
new file mode 100644
index 0000000..1c00bce
--- /dev/null
+++ b/charts/dns-api/Chart.yaml
@@ -0,0 +1,6 @@
+apiVersion: v2
+name: dns-api
+description: A Helm chart for dns-api
+type: application
+version: 0.0.1
+appVersion: "0.0.1"
diff --git a/charts/dns-api/templates/install.yaml b/charts/dns-api/templates/install.yaml
new file mode 100644
index 0000000..33d9a2d
--- /dev/null
+++ b/charts/dns-api/templates/install.yaml
@@ -0,0 +1,72 @@
+# TODO(gio): we'll need to separate intra-dns service and one accessible from k8s cluster
+apiVersion: v1
+kind: Service
+metadata:
+  name: dns-api
+  namespace: {{ .Release.Namespace }}
+  {{- if .Values.service.annotations }}
+  annotations:
+    {{- toYaml .Values.service.annotations | nindent 4 }}
+  {{- end }}
+spec:
+  type: {{ .Values.service.type }}
+  selector:
+    app: dns-api
+  ports:
+  - name: http
+    port: 80
+    targetPort: http
+    protocol: TCP
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: dns-api
+  namespace: {{ .Release.Namespace }}
+spec:
+  selector:
+    matchLabels:
+      app: dns-api
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: dns-api
+    spec:
+      containers:
+      - name: dns-api
+        image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
+        imagePullPolicy: {{ .Values.image.pullPolicy }}
+        ports:
+        - name: http
+          containerPort: 8080
+          protocol: TCP
+        command:
+        - dns-api
+        - --port=8080
+        - --root-dir={{ .Values.volume.mountPath }}
+        - --config={{ .Values.config }}
+        - --db={{ .Values.db }}
+        - --zone={{ .Values.zone }}
+        - --public-ip={{ .Values.publicIP }}
+        - --private-ip={{ .Values.privateIP }}
+        - --nameserver-ip={{ .Values.nameserverIP }}
+        volumeMounts:
+        - name: data
+          mountPath: {{ .Values.volume.mountPath }}
+        resources:
+          requests:
+            memory: "10Mi"
+            cpu: "10m"
+          limits:
+            memory: "20Mi"
+            cpu: "100m"
+      volumes:
+      - name: data
+        persistentVolumeClaim:
+          claimName: {{ .Values.volume.claimName }}
+      tolerations:
+      - key: "pcloud"
+        operator: "Equal"
+        value: "role"
+        effect: "NoSchedule"
diff --git a/charts/dns-api/values.yaml b/charts/dns-api/values.yaml
new file mode 100644
index 0000000..6c0f3d7
--- /dev/null
+++ b/charts/dns-api/values.yaml
@@ -0,0 +1,16 @@
+image:
+  repository: giolekva/dns-api
+  tag: latest
+  pullPolicy: Always
+config: "coredns.conf"
+db: "records.db"
+zone: "example.com"
+publicIP: "1.2.3.4,5.6.7.8"
+privateIP: "10.0.1.0"
+nameserverIP: "4.3.2.1,8.7.6.5"
+volume:
+  claimName: "data"
+  mountPath: "/pcloud"
+service:
+  type: "ClusterIP"
+  annotations: {}
diff --git a/charts/dns-ns-controller/Chart.yaml b/charts/dns-ns-controller/Chart.yaml
deleted file mode 100644
index 82e665e..0000000
--- a/charts/dns-ns-controller/Chart.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-apiVersion: v2
-name: dns-ns-controller
-description: A Helm chart for dns-ns-controller
-type: application
-version: 0.0.1
-appVersion: "0.0.1"
diff --git a/charts/dns-ns-controller/templates/api-config.yaml b/charts/dns-ns-controller/templates/api-config.yaml
deleted file mode 100644
index f902fd8..0000000
--- a/charts/dns-ns-controller/templates/api-config.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-apiVersion: v1
-kind: ConfigMap
-metadata:
-  name: {{ .Values.apiConfigMapName }}
-  namespace: {{ .Release.Namespace }}
-data:
-  createTXTAddr: "http://ns-controller.{{ .Release.Namespace }}.svc.cluster.local/create-txt-record"
-  deleteTXTAddr: "http://ns-controller.{{ .Release.Namespace }}.svc.cluster.local/delete-txt-record"
diff --git a/charts/dns-ns-controller/templates/crds.yaml b/charts/dns-ns-controller/templates/crds.yaml
deleted file mode 100644
index b48b418..0000000
--- a/charts/dns-ns-controller/templates/crds.yaml
+++ /dev/null
@@ -1,69 +0,0 @@
-{{ if .Values.installCRDs }}
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
-metadata:
-  annotations:
-    controller-gen.kubebuilder.io/version: v0.9.2
-  creationTimestamp: null
-  name: dnszones.dodo.cloud.dodo.cloud
-spec:
-  group: dodo.cloud.dodo.cloud
-  names:
-    kind: DNSZone
-    listKind: DNSZoneList
-    plural: dnszones
-    singular: dnszone
-  scope: Namespaced
-  versions:
-  - name: v1
-    schema:
-      openAPIV3Schema:
-        description: DNSZone is the Schema for the dnszones API
-        properties:
-          apiVersion:
-            description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
-            type: string
-          kind:
-            description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
-            type: string
-          metadata:
-            type: object
-          spec:
-            description: DNSZoneSpec defines the desired state of DNSZone
-            properties:
-              dnssec:
-                properties:
-                  enabled:
-                    type: boolean
-                  secretName:
-                    type: string
-                type: object
-              nameservers:
-                items:
-                  type: string
-                type: array
-              privateIP:
-                type: string
-              publicIPs:
-                items:
-                  type: string
-                type: array
-              zone:
-                description: Foo is an example field of DNSZone. Edit dnszone_types.go to remove/update
-                type: string
-            type: object
-          status:
-            description: DNSZoneStatus defines the observed state of DNSZone
-            properties:
-              ready:
-                description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file'
-                type: boolean
-              recordsToPublish:
-                type: string
-            type: object
-        type: object
-    served: true
-    storage: true
-    subresources:
-      status: {}
-{{ end }}
diff --git a/charts/dns-ns-controller/templates/install.yaml b/charts/dns-ns-controller/templates/install.yaml
deleted file mode 100644
index d6f9d36..0000000
--- a/charts/dns-ns-controller/templates/install.yaml
+++ /dev/null
@@ -1,311 +0,0 @@
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  name: ns-controller-controller-manager
-  namespace: {{ .Release.Namespace }}
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: Role
-metadata:
-  name: ns-controller-leader-election-role
-  namespace: {{ .Release.Namespace }}
-rules:
-- apiGroups:
-  - ""
-  resources:
-  - configmaps
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - coordination.k8s.io
-  resources:
-  - leases
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - ""
-  resources:
-  - events
-  verbs:
-  - create
-  - patch
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  creationTimestamp: null
-  name: ns-controller-manager-role
-rules:
-- apiGroups:
-  - ""
-  resources:
-  - secrets
-  verbs:
-  - create
-  - delete
-  - get
-  - list
-  - patch
-  - update
-  - watch
-- apiGroups:
-  - dodo.cloud.dodo.cloud
-  resources:
-  - dnszones
-  verbs:
-  - create
-  - delete
-  - get
-  - list
-  - patch
-  - update
-  - watch
-- apiGroups:
-  - dodo.cloud.dodo.cloud
-  resources:
-  - dnszones/finalizers
-  verbs:
-  - update
-- apiGroups:
-  - dodo.cloud.dodo.cloud
-  resources:
-  - dnszones/status
-  verbs:
-  - get
-  - patch
-  - update
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  name: ns-controller-metrics-reader
-rules:
-- nonResourceURLs:
-  - /metrics
-  verbs:
-  - get
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  name: ns-controller-proxy-role
-rules:
-- apiGroups:
-  - authentication.k8s.io
-  resources:
-  - tokenreviews
-  verbs:
-  - create
-- apiGroups:
-  - authorization.k8s.io
-  resources:
-  - subjectaccessreviews
-  verbs:
-  - create
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: RoleBinding
-metadata:
-  name: ns-controller-leader-election-rolebinding
-  namespace: {{ .Release.Namespace }}
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: Role
-  name: ns-controller-leader-election-role
-subjects:
-- kind: ServiceAccount
-  name: ns-controller-controller-manager
-  namespace: {{ .Release.Namespace }}
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  name: ns-controller-manager-rolebinding
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: ns-controller-manager-role
-subjects:
-- kind: ServiceAccount
-  name: ns-controller-controller-manager
-  namespace: {{ .Release.Namespace }}
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  name: ns-controller-proxy-rolebinding
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: ns-controller-proxy-role
-subjects:
-- kind: ServiceAccount
-  name: ns-controller-controller-manager
-  namespace: {{ .Release.Namespace }}
----
-apiVersion: v1
-data:
-  controller_manager_config.yaml: |
-    apiVersion: controller-runtime.sigs.k8s.io/v1alpha1
-    kind: ControllerManagerConfig
-    health:
-      healthProbeBindAddress: :8081
-    metrics:
-      bindAddress: 127.0.0.1:8080
-    webhook:
-      port: 9443
-    leaderElection:
-      leaderElect: true
-      resourceName: c1db6143.dodo.cloud
-    # leaderElectionReleaseOnCancel defines if the leader should step down volume
-    # when the Manager ends. This requires the binary to immediately end when the
-    # Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
-    # speeds up voluntary leader transitions as the new leader don't have to wait
-    # LeaseDuration time first.
-    # In the default scaffold provided, the program ends immediately after
-    # the manager stops, so would be fine to enable this option. However,
-    # if you are doing or is intended to do any operation such as perform cleanups
-    # after the manager stops then its usage might be unsafe.
-    # leaderElectionReleaseOnCancel: true
-kind: ConfigMap
-metadata:
-  name: ns-controller-manager-config
-  namespace: {{ .Release.Namespace }}
----
-apiVersion: v1
-kind: Service
-metadata:
-  labels:
-    control-plane: controller-manager
-  name: ns-controller-controller-manager-metrics-service
-  namespace: {{ .Release.Namespace }}
-spec:
-  ports:
-  - name: https
-    port: 8443
-    protocol: TCP
-    targetPort: https
-  selector:
-    control-plane: controller-manager
----
-apiVersion: v1
-kind: Service
-metadata:
-  labels:
-    control-plane: controller-manager
-  name: ns-controller # TODO(gio): move to _helpers
-  namespace: {{ .Release.Namespace }}
-spec:
-  ports:
-  - name: http
-    port: 80
-    protocol: TCP
-    targetPort: http
-  selector:
-    control-plane: controller-manager
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  labels:
-    control-plane: controller-manager
-  name: ns-controller-controller-manager
-  namespace: {{ .Release.Namespace }}
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      control-plane: controller-manager
-  template:
-    metadata:
-      annotations:
-        kubectl.kubernetes.io/default-container: manager
-      labels:
-        control-plane: controller-manager
-    spec:
-      volumes:
-      - name: zone-configs
-        persistentVolumeClaim:
-          claimName: {{ .Values.volume.claimName }}
-      containers:
-      - args:
-        - --secure-listen-address=0.0.0.0:8443
-        - --upstream=http://127.0.0.1:8080/
-        - --logtostderr=true
-        - --v=0
-        image: {{ .Values.kubeRBACProxy.image.repository }}:{{ .Values.kubeRBACProxy.image.tag }}
-        name: kube-rbac-proxy
-        ports:
-        - containerPort: 8443
-          name: https
-          protocol: TCP
-        resources:
-          limits:
-            cpu: 500m
-            memory: 128Mi
-          requests:
-            cpu: 5m
-            memory: 64Mi
-        securityContext:
-          allowPrivilegeEscalation: false
-          capabilities:
-            drop:
-            - ALL
-      - args:
-        - --health-probe-bind-address=:8081
-        - --metrics-bind-address=127.0.0.1:8080
-        - --leader-elect
-        - --config-dir=/etc/pcloud/dns-zone-configs
-        - --api-port=8082
-        command:
-        - /manager
-        image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
-        imagePullPolicy: {{ .Values.image.pullPolicy }}
-        volumeMounts:
-        - name: zone-configs
-          mountPath: {{ .Values.volume.mountPath }}
-        livenessProbe:
-          httpGet:
-            path: /healthz
-            port: 8081
-          initialDelaySeconds: 15
-          periodSeconds: 20
-        name: manager
-        ports:
-        - containerPort: 8082
-          name: http
-          protocol: TCP
-        readinessProbe:
-          httpGet:
-            path: /readyz
-            port: 8081
-          initialDelaySeconds: 5
-          periodSeconds: 10
-        resources:
-          limits:
-            cpu: 500m
-            memory: 128Mi
-          requests:
-            cpu: 10m
-            memory: 64Mi
-        securityContext:
-          allowPrivilegeEscalation: false
-          capabilities:
-            drop:
-            - ALL
-      securityContext:
-        runAsNonRoot: true
-      serviceAccountName: ns-controller-controller-manager
-      terminationGracePeriodSeconds: 10
diff --git a/charts/dns-ns-controller/values.yaml b/charts/dns-ns-controller/values.yaml
deleted file mode 100644
index 9ed3ae2..0000000
--- a/charts/dns-ns-controller/values.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-image:
-  repository: giolekva/dns-ns-controller
-  tag: latest
-  pullPolicy: Always
-kubeRBACProxy:
-  image:
-    repository: gcr.io/kubebuilder/kube-rbac-proxy
-    tag: v0.13.0
-    pullPolicy: IfNotPresent
-installCRDs: false
-volume:
-  claimName: data
-  mountPath: /etc/zone-configs
-apiConfigMapName: api-config
diff --git a/charts/service/.helmignore b/charts/service/.helmignore
new file mode 100644
index 0000000..0e8a0eb
--- /dev/null
+++ b/charts/service/.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/service/Chart.yaml b/charts/service/Chart.yaml
new file mode 100644
index 0000000..6141c00
--- /dev/null
+++ b/charts/service/Chart.yaml
@@ -0,0 +1,6 @@
+apiVersion: v2
+name: rpuppy
+description: A Helm chart for Kubernetes service definition
+type: application
+version: 0.0.1
+appVersion: "0.0.1"
diff --git a/charts/service/templates/install.yaml b/charts/service/templates/install.yaml
new file mode 100644
index 0000000..1871f44
--- /dev/null
+++ b/charts/service/templates/install.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ .Values.name }}
+  namespace: {{ .Release.Namespace }}
+  {{- if or .Values.annotations }}
+  annotations:
+    {{- toYaml .Values.annotations | nindent 4 }}
+  {{- end }}
+spec:
+  type: {{ .Values.type }}
+  selector:
+    {{- toYaml .Values.selector | nindent 4 }}
+  ports:
+    {{- toYaml .Values.ports | nindent 4 }}
diff --git a/charts/service/values.yaml b/charts/service/values.yaml
new file mode 100644
index 0000000..b476b59
--- /dev/null
+++ b/charts/service/values.yaml
@@ -0,0 +1,5 @@
+name: "example"
+type: "ClusterIP"
+ports: {}
+selector: {}
+annotations: {}