package host import ( "net" "os" "path/filepath" "strconv" "strings" "github.com/carlosframework/platform/internal/instancectl" "github.com/carlosframework/platform/internal/provision" ) func claimBoundUnitDefinition(s claimUnitShape) ([]byte, error) { if !s.valid() || !claimHexDigest(s.Program) { return nil, os.ErrPermission } template := claimBoundTemplate body := strings.ReplaceAll(strings.ReplaceAll(string(template), "%i", instancectl.Escape(provision.InstanceID(s.Host))), "%I", provision.InstanceID(s.Host)) // With no live root gate, even a direct systemctl restart must execute no // tenant code. The temporary reservation drop-in replaces this command. body = strings.Replace(body, "ExecStart="+instancectl.SystemBinary+" instance-exec run "+provision.InstanceID(s.Host), "ExecStart=/usr/bin/false", 1) if s.Socket != "" && s.Socket != provision.NestedSocket(s.Host) { if !claimFlatSocket(s) { return nil, os.ErrPermission } body = strings.Replace(body, "TemporaryFileSystem=/run/carlos:ro\n", "", 1) body += "\n[Service]\nBindPaths=" + provision.InstanceRunDir(s.Host) + ":/run/carlos\nReadWritePaths=/run/carlos\n" } if s.Addr != "" { host, port, err := net.SplitHostPort(s.Addr) n, e := strconv.Atoi(port) if err != nil || e != nil || n < 1 || n > 65535 { return nil, os.ErrPermission } family, bits := "ipv4", "/32" if ip4 := net.ParseIP(host).To4(); ip4 != nil { host = ip4.String() } else { family, bits = "ipv6", "/128" } body += "\n[Service]\nIPAddressAllow=" + host + bits + "\nSocketBindDeny=any\nSocketBindAllow=" + family + ":tcp:" + port + "\n" } return []byte(body), nil } func claimFlatSocket(s claimUnitShape) bool { if filepath.Dir(s.Socket) != provision.SocketDir || !strings.HasSuffix(s.Socket, ".sock") { return false } for _, r := range filepath.Base(s.Socket) { if !(r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' || r == '-' || r == '_' || r == '.') { return false } } return true } func claimSocketTarget(s claimUnitShape) string { if claimFlatSocket(s) { return filepath.Join(provision.InstanceRunDir(s.Host), filepath.Base(s.Socket)) } return s.Socket }