Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup the configuration loading #608

Merged
merged 11 commits into from
Jun 5, 2022
111 changes: 11 additions & 100 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import (
"errors"
"fmt"
"io"
"io/fs"
"net"
"net/http"
"net/url"
"os"
"os/signal"
"sort"
Expand Down Expand Up @@ -42,7 +40,6 @@ import (
"google.golang.org/grpc/reflection"
"google.golang.org/grpc/status"
"gorm.io/gorm"
"inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/dnstype"
"tailscale.com/types/key"
Expand Down Expand Up @@ -72,95 +69,9 @@ const (
EnforcedClientAuth = "enforced"
)

// Config contains the initial Headscale configuration.
type Config struct {
ServerURL string
Addr string
MetricsAddr string
GRPCAddr string
GRPCAllowInsecure bool
EphemeralNodeInactivityTimeout time.Duration
IPPrefixes []netaddr.IPPrefix
PrivateKeyPath string
BaseDomain string

DERP DERPConfig

DBtype string
DBpath string
DBhost string
DBport int
DBname string
DBuser string
DBpass string

TLSLetsEncryptListen string
TLSLetsEncryptHostname string
TLSLetsEncryptCacheDir string
TLSLetsEncryptChallengeType string

TLSCertPath string
TLSKeyPath string
TLSClientAuthMode tls.ClientAuthType

ACMEURL string
ACMEEmail string

DNSConfig *tailcfg.DNSConfig

UnixSocket string
UnixSocketPermission fs.FileMode

OIDC OIDCConfig

LogTail LogTailConfig

CLI CLIConfig

ACL ACLConfig
}

type OIDCConfig struct {
Issuer string
ClientID string
ClientSecret string
Scope []string
ExtraParams map[string]string
AllowedDomains []string
AllowedUsers []string
StripEmaildomain bool
}

type DERPConfig struct {
ServerEnabled bool
ServerRegionID int
ServerRegionCode string
ServerRegionName string
STUNAddr string
URLs []url.URL
Paths []string
AutoUpdate bool
UpdateFrequency time.Duration
}

type LogTailConfig struct {
Enabled bool
}

type CLIConfig struct {
Address string
APIKey string
Timeout time.Duration
Insecure bool
}

type ACLConfig struct {
PolicyPath string
}

// Headscale represents the base app of the service.
type Headscale struct {
cfg Config
cfg *Config
db *gorm.DB
dbString string
dbType string
Expand Down Expand Up @@ -204,7 +115,7 @@ func LookupTLSClientAuthMode(mode string) (tls.ClientAuthType, bool) {
}
}

func NewHeadscale(cfg Config) (*Headscale, error) {
func NewHeadscale(cfg *Config) (*Headscale, error) {
privKey, err := readOrCreatePrivateKey(cfg.PrivateKeyPath)
if err != nil {
return nil, fmt.Errorf("failed to read or create private key: %w", err)
Expand Down Expand Up @@ -778,23 +689,23 @@ func (h *Headscale) Serve() error {

func (h *Headscale) getTLSSettings() (*tls.Config, error) {
var err error
if h.cfg.TLSLetsEncryptHostname != "" {
if h.cfg.TLS.LetsEncrypt.Hostname != "" {
if !strings.HasPrefix(h.cfg.ServerURL, "https://") {
log.Warn().
Msg("Listening with TLS but ServerURL does not start with https://")
}

certManager := autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist(h.cfg.TLSLetsEncryptHostname),
Cache: autocert.DirCache(h.cfg.TLSLetsEncryptCacheDir),
HostPolicy: autocert.HostWhitelist(h.cfg.TLS.LetsEncrypt.Hostname),
Cache: autocert.DirCache(h.cfg.TLS.LetsEncrypt.CacheDir),
Client: &acme.Client{
DirectoryURL: h.cfg.ACMEURL,
},
Email: h.cfg.ACMEEmail,
}

switch h.cfg.TLSLetsEncryptChallengeType {
switch h.cfg.TLS.LetsEncrypt.ChallengeType {
case "TLS-ALPN-01":
// Configuration via autocert with TLS-ALPN-01 (https://tools.ietf.org/html/rfc8737)
// The RFC requires that the validation is done on port 443; in other words, headscale
Expand All @@ -808,7 +719,7 @@ func (h *Headscale) getTLSSettings() (*tls.Config, error) {
go func() {
log.Fatal().
Caller().
Err(http.ListenAndServe(h.cfg.TLSLetsEncryptListen, certManager.HTTPHandler(http.HandlerFunc(h.redirect)))).
Err(http.ListenAndServe(h.cfg.TLS.LetsEncrypt.Listen, certManager.HTTPHandler(http.HandlerFunc(h.redirect)))).
Msg("failed to set up a HTTP server")
}()

Expand All @@ -817,7 +728,7 @@ func (h *Headscale) getTLSSettings() (*tls.Config, error) {
default:
return nil, errUnsupportedLetsEncryptChallengeType
}
} else if h.cfg.TLSCertPath == "" {
} else if h.cfg.TLS.CertPath == "" {
if !strings.HasPrefix(h.cfg.ServerURL, "http://") {
log.Warn().Msg("Listening without TLS but ServerURL does not start with http://")
}
Expand All @@ -830,16 +741,16 @@ func (h *Headscale) getTLSSettings() (*tls.Config, error) {

log.Info().Msg(fmt.Sprintf(
"Client authentication (mTLS) is \"%s\". See the docs to learn about configuring this setting.",
h.cfg.TLSClientAuthMode))
h.cfg.TLS.ClientAuthMode))

tlsConfig := &tls.Config{
ClientAuth: h.cfg.TLSClientAuthMode,
ClientAuth: h.cfg.TLS.ClientAuthMode,
NextProtos: []string{"http/1.1"},
Certificates: make([]tls.Certificate, 1),
MinVersion: tls.VersionTLS12,
}

tlsConfig.Certificates[0], err = tls.LoadX509KeyPair(h.cfg.TLSCertPath, h.cfg.TLSKeyPath)
tlsConfig.Certificates[0], err = tls.LoadX509KeyPair(h.cfg.TLS.CertPath, h.cfg.TLS.KeyPath)

return tlsConfig, err
}
Expand Down
2 changes: 1 addition & 1 deletion app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (s *Suite) ResetDB(c *check.C) {
}

app = Headscale{
cfg: cfg,
cfg: &cfg,
dbType: "sqlite3",
dbString: tmpDir + "/headscale_test.db",
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/headscale/cli/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ var serveCmd = &cobra.Command{
return nil
},
Run: func(cmd *cobra.Command, args []string) {
h, err := getHeadscaleApp()
app, err := getHeadscaleApp()
if err != nil {
log.Fatal().Caller().Err(err).Msg("Error initializing")
}

err = h.Serve()
err = app.Serve()
if err != nil {
log.Fatal().Caller().Err(err).Msg("Error starting server")
}
Expand Down
Loading