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/installer/tasks/dns.go b/core/installer/tasks/dns.go
index 02d8fee..0880782 100644
--- a/core/installer/tasks/dns.go
+++ b/core/installer/tasks/dns.go
@@ -14,10 +14,10 @@
type Check func(ch Check) error
-func SetupZoneTask(env Env, st *state) Task {
+func SetupZoneTask(env Env, ingressIP net.IP, st *state) Task {
return newSequentialParentTask(
fmt.Sprintf("Setup DNS zone records for %s", env.Domain),
- CreateZoneRecords(env.Domain, st.publicIPs, env, st),
+ CreateZoneRecords(env.Domain, st.publicIPs, ingressIP, env, st),
WaitToPropagate(env.Domain, st.publicIPs),
)
}
@@ -25,6 +25,7 @@
func CreateZoneRecords(
name string,
expected []net.IP,
+ ingressIP net.IP,
env Env,
st *state,
) Task {
@@ -52,7 +53,7 @@
namespace: {{ .namespace }}
spec:
zone: {{ .zone }}
- privateIP: 10.1.0.1
+ privateIP: {{ .ingressIP }}
publicIPs:
{{ range .publicIPs }}
- {{ .String }}
@@ -85,6 +86,7 @@
"zone": env.Domain,
"dnssec": key,
"publicIPs": st.publicIPs,
+ "ingressIP": ingressIP.String(),
}); err != nil {
return err
}
diff --git a/core/installer/tasks/env.go b/core/installer/tasks/env.go
index 04b3c9a..eb537da 100644
--- a/core/installer/tasks/env.go
+++ b/core/installer/tasks/env.go
@@ -41,6 +41,7 @@
func NewCreateEnvTask(
env Env,
publicIPs []net.IP,
+ startIP net.IP,
nsCreator installer.NamespaceCreator,
repo installer.RepoIO,
) (Task, DNSZoneRef) {
@@ -55,9 +56,9 @@
[]Task{
SetupConfigRepoTask(env, &st),
NewActivateEnvTask(env, &st),
- SetupZoneTask(env, &st),
+ SetupZoneTask(env, startIP, &st),
},
- SetupInfra(env, &st)...,
+ SetupInfra(env, startIP, &st)...,
)...,
)
rctx, done := context.WithCancel(context.Background())
diff --git a/core/installer/tasks/infra.go b/core/installer/tasks/infra.go
index 39d9f50..cd7ad14 100644
--- a/core/installer/tasks/infra.go
+++ b/core/installer/tasks/infra.go
@@ -2,6 +2,7 @@
import (
"fmt"
+ "net"
"net/netip"
"github.com/miekg/dns"
@@ -9,7 +10,7 @@
"github.com/giolekva/pcloud/core/installer"
)
-func SetupInfra(env Env, st *state) []Task {
+func SetupInfra(env Env, startIP net.IP, st *state) []Task {
t := newLeafTask("Create client", func() error {
repo, err := st.ssClient.GetRepo("config")
if err != nil {
@@ -31,10 +32,10 @@
&t,
newConcurrentParentTask(
"Core services",
- SetupNetwork(env, st),
+ SetupNetwork(env, startIP, st),
SetupCertificateIssuers(env, st),
SetupAuth(env, st),
- SetupHeadscale(env, st),
+ SetupHeadscale(env, startIP, st),
SetupWelcome(env, st),
SetupAppStore(env, st),
),
@@ -101,13 +102,31 @@
return &t
}
-func SetupNetwork(env Env, st *state) Task {
+func SetupNetwork(env Env, startIP net.IP, st *state) Task {
t := newLeafTask("Setup network", func() error {
- ingressPrivateIP, err := netip.ParseAddr("10.1.0.1")
+ startAddr, err := netip.ParseAddr(startIP.String())
if err != nil {
return err
}
+ if !startAddr.Is4() {
+ return fmt.Errorf("Expected IPv4, got %s instead", startAddr)
+ }
+ addr := startAddr.AsSlice()
+ if addr[3] != 0 {
+ return fmt.Errorf("Expected last byte to be zero, got %d instead", addr[3])
+ }
+ addr[3] = 10
+ fromIP, ok := netip.AddrFromSlice(addr)
+ if !ok {
+ return fmt.Errorf("Must not reach")
+ }
+ addr[3] = 254
+ toIP, ok := netip.AddrFromSlice(addr)
+ if !ok {
+ return fmt.Errorf("Must not reach")
+ }
{
+ ingressPrivateIP := startAddr
headscaleIP := ingressPrivateIP.Next()
app, err := st.appsRepo.Find("metallb-ipaddresspool")
if err != nil {
@@ -133,8 +152,8 @@
}
if err := st.appManager.Install(app, st.nsGen, st.emptySuffixGen, map[string]any{
"name": env.Name,
- "from": "10.1.0.100", // TODO(gio): auto-generate
- "to": "10.1.0.254",
+ "from": fromIP.String(),
+ "to": toIP.String(),
"autoAssign": false,
"namespace": "metallb-system",
}); err != nil {
@@ -150,7 +169,7 @@
"privateNetwork": map[string]any{
"hostname": "private-network-proxy",
"username": "private-network-proxy",
- "ipSubnet": "10.1.0.0/24",
+ "ipSubnet": fmt.Sprintf("%s/24", startIP.String()),
},
}); err != nil {
return err
@@ -210,7 +229,7 @@
)
}
-func SetupHeadscale(env Env, st *state) Task {
+func SetupHeadscale(env Env, startIP net.IP, st *state) Task {
t := newLeafTask("Setup", func() error {
app, err := st.appsRepo.Find("headscale")
if err != nil {
@@ -218,6 +237,7 @@
}
if err := st.appManager.Install(app, st.nsGen, st.emptySuffixGen, map[string]any{
"subdomain": "headscale",
+ "ipSubnet": fmt.Sprintf("%s/24", startIP),
}); err != nil {
return err
}