mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-08-10 13:20:33 +02:00
all: add custom hostname support for provisoning
This commit is contained in:
+25
-9
@@ -147,6 +147,7 @@ func initCLI() {
|
|||||||
runCmd.Flags().IntVarP(&cacheSize, "cache_size", "", 0, "Enable cache with size items")
|
runCmd.Flags().IntVarP(&cacheSize, "cache_size", "", 0, "Enable cache with size items")
|
||||||
runCmd.Flags().StringVarP(&cdUID, cdUidFlagName, "", "", "Control D resolver uid")
|
runCmd.Flags().StringVarP(&cdUID, cdUidFlagName, "", "", "Control D resolver uid")
|
||||||
runCmd.Flags().StringVarP(&cdOrg, cdOrgFlagName, "", "", "Control D provision token")
|
runCmd.Flags().StringVarP(&cdOrg, cdOrgFlagName, "", "", "Control D provision token")
|
||||||
|
runCmd.Flags().StringVarP(&customHostname, customHostnameFlagName, "", "", "Custom hostname passed to ControlD API")
|
||||||
runCmd.Flags().BoolVarP(&cdDev, "dev", "", false, "Use Control D dev resolver/domain")
|
runCmd.Flags().BoolVarP(&cdDev, "dev", "", false, "Use Control D dev resolver/domain")
|
||||||
_ = runCmd.Flags().MarkHidden("dev")
|
_ = runCmd.Flags().MarkHidden("dev")
|
||||||
runCmd.Flags().StringVarP(&homedir, "homedir", "", "", "")
|
runCmd.Flags().StringVarP(&homedir, "homedir", "", "", "")
|
||||||
@@ -319,7 +320,7 @@ NOTE: running "ctrld start" without any arguments will start already installed c
|
|||||||
} else if uid := cdUIDFromProvToken(); uid != "" {
|
} else if uid := cdUIDFromProvToken(); uid != "" {
|
||||||
cdUID = uid
|
cdUID = uid
|
||||||
mainLog.Load().Debug().Msg("using uid from provision token")
|
mainLog.Load().Debug().Msg("using uid from provision token")
|
||||||
removeProvTokenFromArgs(sc)
|
removeOrgFlagsFromArgs(sc)
|
||||||
// Pass --cd flag to "ctrld run" command, so the provision token takes no effect.
|
// Pass --cd flag to "ctrld run" command, so the provision token takes no effect.
|
||||||
sc.Arguments = append(sc.Arguments, "--cd="+cdUID)
|
sc.Arguments = append(sc.Arguments, "--cd="+cdUID)
|
||||||
}
|
}
|
||||||
@@ -440,6 +441,7 @@ NOTE: running "ctrld start" without any arguments will start already installed c
|
|||||||
startCmd.Flags().IntVarP(&cacheSize, "cache_size", "", 0, "Enable cache with size items")
|
startCmd.Flags().IntVarP(&cacheSize, "cache_size", "", 0, "Enable cache with size items")
|
||||||
startCmd.Flags().StringVarP(&cdUID, cdUidFlagName, "", "", "Control D resolver uid")
|
startCmd.Flags().StringVarP(&cdUID, cdUidFlagName, "", "", "Control D resolver uid")
|
||||||
startCmd.Flags().StringVarP(&cdOrg, cdOrgFlagName, "", "", "Control D provision token")
|
startCmd.Flags().StringVarP(&cdOrg, cdOrgFlagName, "", "", "Control D provision token")
|
||||||
|
startCmd.Flags().StringVarP(&customHostname, customHostnameFlagName, "", "", "Custom hostname passed to ControlD API")
|
||||||
startCmd.Flags().BoolVarP(&cdDev, "dev", "", false, "Use Control D dev resolver/domain")
|
startCmd.Flags().BoolVarP(&cdDev, "dev", "", false, "Use Control D dev resolver/domain")
|
||||||
_ = startCmd.Flags().MarkHidden("dev")
|
_ = startCmd.Flags().MarkHidden("dev")
|
||||||
startCmd.Flags().StringVarP(&iface, "iface", "", "", `Update DNS setting for iface, "auto" means the default interface gateway`)
|
startCmd.Flags().StringVarP(&iface, "iface", "", "", `Update DNS setting for iface, "auto" means the default interface gateway`)
|
||||||
@@ -2363,17 +2365,30 @@ func cdUIDFromProvToken() string {
|
|||||||
if cdOrg == "" {
|
if cdOrg == "" {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
// Validate custom hostname if provided.
|
||||||
|
if customHostname != "" && !validHostname(customHostname) {
|
||||||
|
mainLog.Load().Fatal().Msgf("invalid custom hostname: %q", customHostname)
|
||||||
|
}
|
||||||
|
req := &controld.UtilityOrgRequest{ProvToken: cdOrg, Hostname: customHostname}
|
||||||
// Process provision token if provided.
|
// Process provision token if provided.
|
||||||
resolverConfig, err := controld.FetchResolverUID(cdOrg, rootCmd.Version, cdDev)
|
resolverConfig, err := controld.FetchResolverUID(req, rootCmd.Version, cdDev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mainLog.Load().Fatal().Err(err).Msgf("failed to fetch resolver uid with provision token: %s", cdOrg)
|
mainLog.Load().Fatal().Err(err).Msgf("failed to fetch resolver uid with provision token: %s", cdOrg)
|
||||||
}
|
}
|
||||||
return resolverConfig.UID
|
return resolverConfig.UID
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeProvTokenFromArgs removes the --cd-org from command line arguments.
|
// removeOrgFlagsFromArgs removes organization flags from command line arguments.
|
||||||
func removeProvTokenFromArgs(sc *service.Config) {
|
// The flags are:
|
||||||
|
//
|
||||||
|
// - "--cd-org"
|
||||||
|
// - "--custom-hostname"
|
||||||
|
//
|
||||||
|
// This is necessary because "ctrld run" only need a valid UID, which could be fetched
|
||||||
|
// using "--cd-org". So if "ctrld start" have already been called with "--cd-org", we
|
||||||
|
// already have a valid UID to pass to "ctrld run", so we don't have to force "ctrld run"
|
||||||
|
// to re-do the already done job.
|
||||||
|
func removeOrgFlagsFromArgs(sc *service.Config) {
|
||||||
a := sc.Arguments[:0]
|
a := sc.Arguments[:0]
|
||||||
skip := false
|
skip := false
|
||||||
for _, x := range sc.Arguments {
|
for _, x := range sc.Arguments {
|
||||||
@@ -2381,13 +2396,14 @@ func removeProvTokenFromArgs(sc *service.Config) {
|
|||||||
skip = false
|
skip = false
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// For "--cd-org XXX", skip it and mark next arg skipped.
|
// For "--cd-org XXX"/"--custom-hostname XXX", skip them and mark next arg skipped.
|
||||||
if x == "--"+cdOrgFlagName {
|
if x == "--"+cdOrgFlagName || x == "--"+customHostnameFlagName {
|
||||||
skip = true
|
skip = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// For "--cd-org=XXX", just skip it.
|
// For "--cd-org=XXX"/"--custom-hostname=XXX", just skip them.
|
||||||
if strings.HasPrefix(x, "--"+cdOrgFlagName+"=") {
|
if strings.HasPrefix(x, "--"+cdOrgFlagName+"=") ||
|
||||||
|
strings.HasPrefix(x, "--"+customHostnameFlagName+"=") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
a = append(a, x)
|
a = append(a, x)
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
// validHostname reports whether hostname is a valid hostname.
|
||||||
|
// A valid hostname contains 3 -> 64 characters and conform to RFC1123.
|
||||||
|
func validHostname(hostname string) bool {
|
||||||
|
hostnameLen := len(hostname)
|
||||||
|
if hostnameLen < 3 || hostnameLen > 64 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
validHostnameRfc1123 := regexp.MustCompile(`^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$`)
|
||||||
|
return validHostnameRfc1123.MatchString(hostname)
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_validHostname(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
hostname string
|
||||||
|
valid bool
|
||||||
|
}{
|
||||||
|
{"localhost", "localhost", true},
|
||||||
|
{"localdomain", "localhost.localdomain", true},
|
||||||
|
{"localhost6", "localhost6.localdomain6", true},
|
||||||
|
{"ip6", "ip6-localhost", true},
|
||||||
|
{"non-domain", "controld", true},
|
||||||
|
{"domain", "controld.com", true},
|
||||||
|
{"empty", "", false},
|
||||||
|
{"min length", "fo", false},
|
||||||
|
{"max length", strings.Repeat("a", 65), false},
|
||||||
|
{"special char", "foo!", false},
|
||||||
|
{"non-ascii", "fooΩ", false},
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.hostname, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert.True(t, validHostname(tc.hostname) == tc.valid)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-3
@@ -29,6 +29,7 @@ var (
|
|||||||
silent bool
|
silent bool
|
||||||
cdUID string
|
cdUID string
|
||||||
cdOrg string
|
cdOrg string
|
||||||
|
customHostname string
|
||||||
cdDev bool
|
cdDev bool
|
||||||
iface string
|
iface string
|
||||||
ifaceStartStop string
|
ifaceStartStop string
|
||||||
@@ -45,9 +46,10 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
cdUidFlagName = "cd"
|
cdUidFlagName = "cd"
|
||||||
cdOrgFlagName = "cd-org"
|
cdOrgFlagName = "cd-org"
|
||||||
nextdnsFlagName = "nextdns"
|
customHostnameFlagName = "custom-hostname"
|
||||||
|
nextdnsFlagName = "nextdns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@@ -64,7 +65,8 @@ type utilityRequest struct {
|
|||||||
ClientID string `json:"client_id,omitempty"`
|
ClientID string `json:"client_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type utilityOrgRequest struct {
|
// UtilityOrgRequest contains request data for calling Org API.
|
||||||
|
type UtilityOrgRequest struct {
|
||||||
ProvToken string `json:"prov_token"`
|
ProvToken string `json:"prov_token"`
|
||||||
Hostname string `json:"hostname"`
|
Hostname string `json:"hostname"`
|
||||||
}
|
}
|
||||||
@@ -81,9 +83,15 @@ func FetchResolverConfig(rawUID, version string, cdDev bool) (*ResolverConfig, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FetchResolverUID fetch resolver uid from provision token.
|
// FetchResolverUID fetch resolver uid from provision token.
|
||||||
func FetchResolverUID(pt, version string, cdDev bool) (*ResolverConfig, error) {
|
func FetchResolverUID(req *UtilityOrgRequest, version string, cdDev bool) (*ResolverConfig, error) {
|
||||||
hostname, _ := os.Hostname()
|
if req == nil {
|
||||||
body, _ := json.Marshal(utilityOrgRequest{ProvToken: pt, Hostname: hostname})
|
return nil, errors.New("invalid request")
|
||||||
|
}
|
||||||
|
hostname := req.Hostname
|
||||||
|
if hostname == "" {
|
||||||
|
hostname, _ = os.Hostname()
|
||||||
|
}
|
||||||
|
body, _ := json.Marshal(UtilityOrgRequest{ProvToken: req.ProvToken, Hostname: hostname})
|
||||||
return postUtilityAPI(version, cdDev, false, bytes.NewReader(body))
|
return postUtilityAPI(version, cdDev, false, bytes.NewReader(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user