dns-zone-controller: with env-manager generating dnssec key and zone records
diff --git a/core/installer/go.mod b/core/installer/go.mod
index 275673a..b987410 100644
--- a/core/installer/go.mod
+++ b/core/installer/go.mod
@@ -10,6 +10,7 @@
 	github.com/go-git/go-git/v5 v5.10.0
 	github.com/gorilla/mux v1.8.0
 	github.com/labstack/echo/v4 v4.10.2
+	github.com/miekg/dns v1.0.14
 	github.com/spf13/cobra v1.4.0
 	golang.org/x/crypto v0.14.0
 	golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
diff --git a/core/installer/go.sum b/core/installer/go.sum
index 5f126a9..b926358 100644
--- a/core/installer/go.sum
+++ b/core/installer/go.sum
@@ -504,6 +504,7 @@
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
 github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
 github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4=
diff --git a/core/installer/welcome/env.go b/core/installer/welcome/env.go
index aa3e061..2c5d332 100644
--- a/core/installer/welcome/env.go
+++ b/core/installer/welcome/env.go
@@ -16,8 +16,10 @@
 	"strings"
 	"text/template"
 
+	"github.com/Masterminds/sprig/v3"
 	"github.com/charmbracelet/keygen"
 	"github.com/gorilla/mux"
+	"github.com/miekg/dns"
 
 	"github.com/giolekva/pcloud/core/installer"
 	"github.com/giolekva/pcloud/core/installer/soft"
@@ -359,6 +361,30 @@
 	}
 }
 
+type DNSSecKey struct {
+	Basename string `json:"basename,omitempty"`
+	Key      []byte `json:"key,omitempty"`
+	Private  []byte `json:"private,omitempty"`
+	DS       []byte `json:"ds,omitempty"`
+}
+
+func newDNSSecKey(zone string) (DNSSecKey, error) {
+	key := &dns.DNSKEY{
+		Hdr:       dns.RR_Header{Name: dns.Fqdn(zone), Class: dns.ClassINET, Ttl: 3600, Rrtype: dns.TypeDNSKEY},
+		Algorithm: dns.ECDSAP256SHA256, Flags: 257, Protocol: 3,
+	}
+	priv, err := key.Generate(256)
+	if err != nil {
+		return DNSSecKey{}, err
+	}
+	return DNSSecKey{
+		Basename: fmt.Sprintf("K%s+%03d+%05d", key.Header().Name, key.Algorithm, key.KeyTag()),
+		Key:      []byte(key.String()),
+		Private:  []byte(key.PrivateKeyString(priv)),
+		DS:       []byte(key.ToDS(dns.SHA256).String()),
+	}, nil
+}
+
 func initNewEnv(
 	ss *soft.Client,
 	r installer.RepoIO,
@@ -393,7 +419,7 @@
 			return err
 		}
 		defer out.Close()
-		_, err = out.Write([]byte(fmt.Sprintf(`
+		_, err = fmt.Fprintf(out, `
 apiVersion: source.toolkit.fluxcd.io/v1
 kind: GitRepository
 metadata:
@@ -404,13 +430,67 @@
   url: https://github.com/giolekva/pcloud
   ref:
     branch: main
-`, req.Name)))
+`, req.Name)
 		if err != nil {
 			return err
 		}
 	}
+	{
+		key, err := newDNSSecKey(req.Domain)
+		if err != nil {
+			return err
+		}
+		out, err := r.Writer("dns-zone.yaml")
+		if err != nil {
+			return err
+		}
+		defer out.Close()
+		dnsZoneTmpl, err := template.New("config").Funcs(sprig.TxtFuncMap()).Parse(`
+apiVersion: dodo.cloud.dodo.cloud/v1
+kind: DNSZone
+metadata:
+  name: dns-zone
+  namespace: {{ .namespace }}
+spec:
+  zone: {{ .zone }}
+  privateIP: 10.1.0.1
+  publicIPs:
+  - 135.181.48.180
+  - 65.108.39.172
+  - 65.108.39.171
+  nameservers:
+  - 135.181.48.180
+  - 65.108.39.172
+  - 65.108.39.171
+  dnssec:
+    enabled: true
+    secretName: dnssec-key
+---
+apiVersion: v1
+kind: Secret
+metadata:
+  name: dnssec-key
+  namespace: {{ .namespace }}
+type: Opaque
+data:
+  basename: {{ .dnssec.Basename | b64enc }}
+  key: {{ .dnssec.Key | toString | b64enc }}
+  private: {{ .dnssec.Private | toString | b64enc }}
+  ds: {{ .dnssec.DS | toString | b64enc }}
+`)
+		if err != nil {
+			return err
+		}
+		if err := dnsZoneTmpl.Execute(out, map[string]any{
+			"namespace": req.Name,
+			"zone":      req.Domain,
+			"dnssec":    key,
+		}); err != nil {
+			return err
+		}
+	}
 	rootKust := installer.NewKustomization()
-	rootKust.AddResources("pcloud-charts.yaml", "apps")
+	rootKust.AddResources("pcloud-charts.yaml", "dns-zone.yaml", "apps")
 	if err := r.WriteKustomization("kustomization.yaml", rootKust); err != nil {
 		return err
 	}