env-manager: dynamically generate cidr for new env (#85)

* env-manager: allocate env cidrs dynamically

* fix: net.IP to netip.Addr conversion

* bootstrapper: generate empty env-cidrs.yaml

* fix: net.IP to netip.Addr conversion for IP pool

* infra: expose provided startIP subnet via tailscale proxy

* headscale: pass private network ip subnet to expose to api service

* dns: make ingress IP configurable

---------

Co-authored-by: Giorgi Lekveishvili <lekva@gl-mbp-m1-max.local>
diff --git a/core/headscale/main.go b/core/headscale/main.go
index 942d71a..87d245b 100644
--- a/core/headscale/main.go
+++ b/core/headscale/main.go
@@ -5,6 +5,7 @@
 	"flag"
 	"fmt"
 	"log"
+	"net"
 	"net/http"
 	"os"
 	"text/template"
@@ -15,28 +16,21 @@
 var port = flag.Int("port", 3000, "Port to listen on")
 var config = flag.String("config", "", "Path to headscale config")
 var acls = flag.String("acls", "", "Path to the headscale acls file")
-var domain = flag.String("domain", "", "Environment domain")
+var ipSubnet = flag.String("ip-subnet", "10.1.0.0/24", "IP subnet of the private network")
 
 // TODO(gio): make internal network cidr and proxy user configurable
 const defaultACLs = `
 {
   "autoApprovers": {
     "routes": {
-      // "10.1.0.0/24": ["private-network-proxy@{{ .Domain }}"],
-      "10.1.0.0/24": ["*"],
+      "{{ .ipSubnet }}": ["*"],
     },
   },
   "acls": [
     { // Everyone has passthough access to private-network-proxy node
       "action": "accept",
       "src": ["*"],
-      "dst": ["10.1.0.0/24:*", "private-network-proxy:0"],
-    },
-  ],
-  "tests": [
-    {
-      "src": "*",
-      "accept": ["10.1.0.1:80", "10.1.0.1:443"],
+      "dst": ["{{ .ipSubnet }}:*", "private-network-proxy:0"],
     },
   ],
 }
@@ -94,24 +88,28 @@
 	}
 }
 
-func updateACLs(domain, acls string) error {
+func updateACLs(cidr net.IPNet, aclsPath string) error {
 	tmpl, err := template.New("acls").Parse(defaultACLs)
 	if err != nil {
 		return err
 	}
-	out, err := os.Create(acls)
+	out, err := os.Create(aclsPath)
 	if err != nil {
 		return err
 	}
 	defer out.Close()
 	return tmpl.Execute(out, map[string]any{
-		"Domain": domain,
+		"ipSubnet": cidr.String(),
 	})
 }
 
 func main() {
 	flag.Parse()
-	updateACLs(*domain, *acls)
+	_, cidr, err := net.ParseCIDR(*ipSubnet)
+	if err != nil {
+		panic(err)
+	}
+	updateACLs(*cidr, *acls)
 	c := newClient(*config)
 	s := newServer(*port, c)
 	s.start()