2023-06-06 08:23:39 +00:00
package types
2022-06-03 07:05:41 +00:00
import (
2022-06-03 07:26:36 +00:00
"errors"
"fmt"
2022-06-03 07:05:41 +00:00
"io/fs"
2022-09-04 09:32:29 +00:00
"net/netip"
2022-06-03 07:05:41 +00:00
"net/url"
2023-01-10 11:46:42 +00:00
"os"
2022-06-03 07:26:36 +00:00
"strings"
2022-06-03 07:05:41 +00:00
"time"
2022-06-03 07:26:36 +00:00
"github.com/coreos/go-oidc/v3/oidc"
2024-02-18 18:31:29 +00:00
"github.com/juanfont/headscale/hscontrol/util"
2023-01-31 11:40:38 +00:00
"github.com/prometheus/common/model"
2022-06-03 08:37:45 +00:00
"github.com/rs/zerolog"
2022-06-03 07:26:36 +00:00
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
2022-09-01 22:04:04 +00:00
"go4.org/netipx"
2023-05-06 09:30:15 +00:00
"tailscale.com/net/tsaddr"
2022-06-03 07:05:41 +00:00
"tailscale.com/tailcfg"
2022-06-03 07:26:36 +00:00
"tailscale.com/types/dnstype"
2022-06-03 07:05:41 +00:00
)
2022-07-11 18:33:24 +00:00
const (
2023-01-31 11:40:38 +00:00
defaultOIDCExpiryTime = 180 * 24 * time . Hour // 180 Days
maxDuration time . Duration = 1 << 63 - 1
2022-07-11 18:33:24 +00:00
)
2023-01-31 11:40:38 +00:00
var errOidcMutuallyExclusive = errors . New (
"oidc_client_secret and oidc_client_secret_path are mutually exclusive" ,
)
2023-01-10 11:46:42 +00:00
2024-04-17 05:03:06 +00:00
type IPAllocationStrategy string
const (
IPAllocationStrategySequential IPAllocationStrategy = "sequential"
IPAllocationStrategyRandom IPAllocationStrategy = "random"
)
2022-06-03 07:05:41 +00:00
// Config contains the initial Headscale configuration.
type Config struct {
ServerURL string
Addr string
MetricsAddr string
GRPCAddr string
GRPCAllowInsecure bool
EphemeralNodeInactivityTimeout time . Duration
2022-07-12 10:52:03 +00:00
NodeUpdateCheckInterval time . Duration
2024-02-18 18:31:29 +00:00
PrefixV4 * netip . Prefix
PrefixV6 * netip . Prefix
2024-04-17 05:03:06 +00:00
IPAllocation IPAllocationStrategy
2022-08-13 09:14:38 +00:00
NoisePrivateKeyPath string
2022-06-03 07:05:41 +00:00
BaseDomain string
2022-09-11 19:37:23 +00:00
Log LogConfig
2022-06-03 08:37:45 +00:00
DisableUpdateCheck bool
2022-06-03 07:05:41 +00:00
2024-02-09 06:27:00 +00:00
Database DatabaseConfig
2022-06-03 07:05:41 +00:00
2024-02-09 06:27:00 +00:00
DERP DERPConfig
2022-06-03 07:05:41 +00:00
2022-06-03 08:14:14 +00:00
TLS TLSConfig
2022-06-03 07:05:41 +00:00
ACMEURL string
ACMEEmail string
DNSConfig * tailcfg . DNSConfig
UnixSocket string
UnixSocketPermission fs . FileMode
OIDC OIDCConfig
2022-06-09 19:20:11 +00:00
LogTail LogTailConfig
RandomizeClientPort bool
2022-06-03 07:05:41 +00:00
CLI CLIConfig
ACL ACLConfig
2024-02-23 09:59:24 +00:00
Tuning Tuning
2022-06-03 07:05:41 +00:00
}
2024-02-09 06:27:00 +00:00
type SqliteConfig struct {
Path string
}
type PostgresConfig struct {
2024-02-09 16:34:28 +00:00
Host string
Port int
Name string
User string
Pass string
Ssl string
MaxOpenConnections int
MaxIdleConnections int
ConnMaxIdleTimeSecs int
2024-02-09 06:27:00 +00:00
}
type DatabaseConfig struct {
// Type sets the database type, either "sqlite3" or "postgres"
Type string
Debug bool
Sqlite SqliteConfig
Postgres PostgresConfig
}
2022-06-03 08:14:14 +00:00
type TLSConfig struct {
2022-11-19 10:33:15 +00:00
CertPath string
KeyPath string
2022-06-03 08:14:14 +00:00
LetsEncrypt LetsEncryptConfig
}
type LetsEncryptConfig struct {
Listen string
Hostname string
CacheDir string
ChallengeType string
}
2022-06-03 07:05:41 +00:00
type OIDCConfig struct {
2022-09-26 07:57:28 +00:00
OnlyStartIfOIDCIsAvailable bool
Issuer string
ClientID string
ClientSecret string
Scope [ ] string
ExtraParams map [ string ] string
AllowedDomains [ ] string
AllowedUsers [ ] string
2022-12-07 00:08:01 +00:00
AllowedGroups [ ] string
2022-09-26 07:57:28 +00:00
StripEmaildomain bool
2023-01-31 11:40:38 +00:00
Expiry time . Duration
UseExpiryFromToken bool
2022-06-03 07:05:41 +00:00
}
type DERPConfig struct {
2024-01-16 15:04:03 +00:00
ServerEnabled bool
AutomaticallyAddEmbeddedDerpRegion bool
ServerRegionID int
ServerRegionCode string
ServerRegionName string
ServerPrivateKeyPath string
STUNAddr string
URLs [ ] url . URL
Paths [ ] string
AutoUpdate bool
UpdateFrequency time . Duration
IPv4 string
IPv6 string
2022-06-03 07:05:41 +00:00
}
type LogTailConfig struct {
Enabled bool
}
type CLIConfig struct {
Address string
APIKey string
Timeout time . Duration
Insecure bool
}
type ACLConfig struct {
PolicyPath string
}
2022-06-03 07:26:36 +00:00
2022-09-11 19:37:23 +00:00
type LogConfig struct {
Format string
Level zerolog . Level
}
2024-02-23 09:59:24 +00:00
type Tuning struct {
BatchChangeDelay time . Duration
NodeMapSessionBufferedChanSize int
}
2022-06-07 14:24:35 +00:00
func LoadConfig ( path string , isFile bool ) error {
if isFile {
viper . SetConfigFile ( path )
2022-06-03 07:26:36 +00:00
} else {
2022-06-07 14:24:35 +00:00
viper . SetConfigName ( "config" )
if path == "" {
viper . AddConfigPath ( "/etc/headscale/" )
viper . AddConfigPath ( "$HOME/.headscale" )
viper . AddConfigPath ( "." )
} else {
// For testing
viper . AddConfigPath ( path )
}
2022-06-03 07:26:36 +00:00
}
viper . SetEnvPrefix ( "headscale" )
viper . SetEnvKeyReplacer ( strings . NewReplacer ( "." , "_" ) )
viper . AutomaticEnv ( )
viper . SetDefault ( "tls_letsencrypt_cache_dir" , "/var/www/.cache" )
2023-06-06 09:12:36 +00:00
viper . SetDefault ( "tls_letsencrypt_challenge_type" , HTTP01ChallengeType )
2022-06-03 07:26:36 +00:00
2022-09-11 19:37:23 +00:00
viper . SetDefault ( "log.level" , "info" )
viper . SetDefault ( "log.format" , TextLogFormat )
2022-06-03 07:26:36 +00:00
viper . SetDefault ( "dns_config" , nil )
2022-10-31 15:26:18 +00:00
viper . SetDefault ( "dns_config.override_local_dns" , true )
2022-06-03 07:26:36 +00:00
viper . SetDefault ( "derp.server.enabled" , false )
viper . SetDefault ( "derp.server.stun.enabled" , true )
2024-01-16 15:04:03 +00:00
viper . SetDefault ( "derp.server.automatically_add_embedded_derp_region" , true )
2022-06-03 07:26:36 +00:00
2023-05-10 14:32:15 +00:00
viper . SetDefault ( "unix_socket" , "/var/run/headscale/headscale.sock" )
2022-06-03 07:26:36 +00:00
viper . SetDefault ( "unix_socket_permission" , "0o770" )
viper . SetDefault ( "grpc_listen_addr" , ":50443" )
viper . SetDefault ( "grpc_allow_insecure" , false )
viper . SetDefault ( "cli.timeout" , "5s" )
viper . SetDefault ( "cli.insecure" , false )
2024-02-09 06:27:00 +00:00
viper . SetDefault ( "database.postgres.ssl" , false )
2024-02-09 16:34:28 +00:00
viper . SetDefault ( "database.postgres.max_open_conns" , 10 )
viper . SetDefault ( "database.postgres.max_idle_conns" , 10 )
viper . SetDefault ( "database.postgres.conn_max_idle_time_secs" , 3600 )
2022-12-07 08:37:45 +00:00
2022-06-03 07:26:36 +00:00
viper . SetDefault ( "oidc.scope" , [ ] string { oidc . ScopeOpenID , "profile" , "email" } )
viper . SetDefault ( "oidc.strip_email_domain" , true )
2022-09-26 07:57:28 +00:00
viper . SetDefault ( "oidc.only_start_if_oidc_is_available" , true )
2023-01-31 11:40:38 +00:00
viper . SetDefault ( "oidc.expiry" , "180d" )
viper . SetDefault ( "oidc.use_expiry_from_token" , false )
2022-06-03 07:26:36 +00:00
viper . SetDefault ( "logtail.enabled" , false )
2022-06-09 19:20:11 +00:00
viper . SetDefault ( "randomize_client_port" , false )
2022-06-03 07:26:36 +00:00
2022-06-12 13:12:53 +00:00
viper . SetDefault ( "ephemeral_node_inactivity_timeout" , "120s" )
2022-07-12 10:52:03 +00:00
viper . SetDefault ( "node_update_check_interval" , "10s" )
2022-07-12 10:27:28 +00:00
2024-02-23 09:59:24 +00:00
viper . SetDefault ( "tuning.batch_change_delay" , "800ms" )
viper . SetDefault ( "tuning.node_mapsession_buffered_chan_size" , 30 )
2024-04-17 09:09:22 +00:00
viper . SetDefault ( "prefixes.allocation" , string ( IPAllocationStrategySequential ) )
2024-04-17 05:03:06 +00:00
2022-11-18 17:02:34 +00:00
if IsCLIConfigured ( ) {
return nil
}
2022-06-03 07:26:36 +00:00
if err := viper . ReadInConfig ( ) ; err != nil {
2022-06-12 13:32:16 +00:00
log . Warn ( ) . Err ( err ) . Msg ( "Failed to read configuration from disk" )
2022-06-03 07:26:36 +00:00
return fmt . Errorf ( "fatal error reading config file: %w" , err )
}
// Collect any validation errors and return them all at once
var errorText string
if ( viper . GetString ( "tls_letsencrypt_hostname" ) != "" ) &&
( ( viper . GetString ( "tls_cert_path" ) != "" ) || ( viper . GetString ( "tls_key_path" ) != "" ) ) {
errorText += "Fatal config error: set either tls_letsencrypt_hostname or tls_cert_path/tls_key_path, not both\n"
}
2022-08-21 08:42:23 +00:00
if ! viper . IsSet ( "noise" ) || viper . GetString ( "noise.private_key_path" ) == "" {
errorText += "Fatal config error: headscale now requires a new `noise.private_key_path` field in the config file for the Tailscale v2 protocol\n"
2022-08-14 10:35:14 +00:00
}
2022-06-03 07:26:36 +00:00
if ( viper . GetString ( "tls_letsencrypt_hostname" ) != "" ) &&
2023-06-06 09:12:36 +00:00
( viper . GetString ( "tls_letsencrypt_challenge_type" ) == TLSALPN01ChallengeType ) &&
2022-06-03 07:26:36 +00:00
( ! strings . HasSuffix ( viper . GetString ( "listen_addr" ) , ":443" ) ) {
// this is only a warning because there could be something sitting in front of headscale that redirects the traffic (e.g. an iptables rule)
log . Warn ( ) .
Msg ( "Warning: when using tls_letsencrypt_hostname with TLS-ALPN-01 as challenge type, headscale must be reachable on port 443, i.e. listen_addr should probably end in :443" )
}
2023-06-06 09:12:36 +00:00
if ( viper . GetString ( "tls_letsencrypt_challenge_type" ) != HTTP01ChallengeType ) &&
( viper . GetString ( "tls_letsencrypt_challenge_type" ) != TLSALPN01ChallengeType ) {
2022-06-03 07:26:36 +00:00
errorText += "Fatal config error: the only supported values for tls_letsencrypt_challenge_type are HTTP-01 and TLS-ALPN-01\n"
}
if ! strings . HasPrefix ( viper . GetString ( "server_url" ) , "http://" ) &&
! strings . HasPrefix ( viper . GetString ( "server_url" ) , "https://" ) {
errorText += "Fatal config error: server_url must start with https:// or http://\n"
}
2022-06-12 13:12:43 +00:00
// Minimum inactivity time out is keepalive timeout (60s) plus a few seconds
// to avoid races
minInactivityTimeout , _ := time . ParseDuration ( "65s" )
if viper . GetDuration ( "ephemeral_node_inactivity_timeout" ) <= minInactivityTimeout {
errorText += fmt . Sprintf (
"Fatal config error: ephemeral_node_inactivity_timeout (%s) is set too low, must be more than %s" ,
viper . GetString ( "ephemeral_node_inactivity_timeout" ) ,
minInactivityTimeout ,
)
}
2022-07-12 10:52:03 +00:00
maxNodeUpdateCheckInterval , _ := time . ParseDuration ( "60s" )
if viper . GetDuration ( "node_update_check_interval" ) > maxNodeUpdateCheckInterval {
2022-07-12 10:27:28 +00:00
errorText += fmt . Sprintf (
2022-07-12 10:52:03 +00:00
"Fatal config error: node_update_check_interval (%s) is set too high, must be less than %s" ,
viper . GetString ( "node_update_check_interval" ) ,
maxNodeUpdateCheckInterval ,
2022-07-12 10:27:28 +00:00
)
}
2022-06-03 07:26:36 +00:00
if errorText != "" {
2024-02-09 16:34:28 +00:00
// nolint
2022-06-03 07:26:36 +00:00
return errors . New ( strings . TrimSuffix ( errorText , "\n" ) )
} else {
return nil
}
}
2022-06-03 08:14:14 +00:00
func GetTLSConfig ( ) TLSConfig {
return TLSConfig {
LetsEncrypt : LetsEncryptConfig {
Hostname : viper . GetString ( "tls_letsencrypt_hostname" ) ,
Listen : viper . GetString ( "tls_letsencrypt_listen" ) ,
2023-05-11 07:09:18 +00:00
CacheDir : util . AbsolutePathFromConfigPath (
2022-06-03 08:14:14 +00:00
viper . GetString ( "tls_letsencrypt_cache_dir" ) ,
) ,
ChallengeType : viper . GetString ( "tls_letsencrypt_challenge_type" ) ,
} ,
2023-05-11 07:09:18 +00:00
CertPath : util . AbsolutePathFromConfigPath (
2022-06-03 08:14:14 +00:00
viper . GetString ( "tls_cert_path" ) ,
) ,
2023-05-11 07:09:18 +00:00
KeyPath : util . AbsolutePathFromConfigPath (
2022-06-03 08:14:14 +00:00
viper . GetString ( "tls_key_path" ) ,
) ,
}
}
2022-06-03 07:26:36 +00:00
func GetDERPConfig ( ) DERPConfig {
serverEnabled := viper . GetBool ( "derp.server.enabled" )
serverRegionID := viper . GetInt ( "derp.server.region_id" )
serverRegionCode := viper . GetString ( "derp.server.region_code" )
serverRegionName := viper . GetString ( "derp.server.region_name" )
stunAddr := viper . GetString ( "derp.server.stun_listen_addr" )
2024-01-16 15:04:03 +00:00
privateKeyPath := util . AbsolutePathFromConfigPath (
viper . GetString ( "derp.server.private_key_path" ) ,
)
ipv4 := viper . GetString ( "derp.server.ipv4" )
ipv6 := viper . GetString ( "derp.server.ipv6" )
automaticallyAddEmbeddedDerpRegion := viper . GetBool (
"derp.server.automatically_add_embedded_derp_region" ,
)
2022-06-03 07:26:36 +00:00
if serverEnabled && stunAddr == "" {
log . Fatal ( ) .
Msg ( "derp.server.stun_listen_addr must be set if derp.server.enabled is true" )
}
urlStrs := viper . GetStringSlice ( "derp.urls" )
urls := make ( [ ] url . URL , len ( urlStrs ) )
for index , urlStr := range urlStrs {
urlAddr , err := url . Parse ( urlStr )
if err != nil {
log . Error ( ) .
Str ( "url" , urlStr ) .
Err ( err ) .
Msg ( "Failed to parse url, ignoring..." )
}
urls [ index ] = * urlAddr
}
paths := viper . GetStringSlice ( "derp.paths" )
2024-01-16 15:04:03 +00:00
if serverEnabled && ! automaticallyAddEmbeddedDerpRegion && len ( paths ) == 0 {
log . Fatal ( ) .
Msg ( "Disabling derp.server.automatically_add_embedded_derp_region requires to configure the derp server in derp.paths" )
}
2022-06-03 07:26:36 +00:00
autoUpdate := viper . GetBool ( "derp.auto_update_enabled" )
updateFrequency := viper . GetDuration ( "derp.update_frequency" )
return DERPConfig {
2024-01-16 15:04:03 +00:00
ServerEnabled : serverEnabled ,
ServerRegionID : serverRegionID ,
ServerRegionCode : serverRegionCode ,
ServerRegionName : serverRegionName ,
ServerPrivateKeyPath : privateKeyPath ,
STUNAddr : stunAddr ,
URLs : urls ,
Paths : paths ,
AutoUpdate : autoUpdate ,
UpdateFrequency : updateFrequency ,
IPv4 : ipv4 ,
IPv6 : ipv6 ,
AutomaticallyAddEmbeddedDerpRegion : automaticallyAddEmbeddedDerpRegion ,
2022-06-03 07:26:36 +00:00
}
}
func GetLogTailConfig ( ) LogTailConfig {
enabled := viper . GetBool ( "logtail.enabled" )
return LogTailConfig {
Enabled : enabled ,
}
}
func GetACLConfig ( ) ACLConfig {
policyPath := viper . GetString ( "acl_policy_path" )
return ACLConfig {
PolicyPath : policyPath ,
}
}
2022-09-11 19:37:23 +00:00
func GetLogConfig ( ) LogConfig {
logLevelStr := viper . GetString ( "log.level" )
logLevel , err := zerolog . ParseLevel ( logLevelStr )
if err != nil {
logLevel = zerolog . DebugLevel
}
logFormatOpt := viper . GetString ( "log.format" )
var logFormat string
switch logFormatOpt {
case "json" :
logFormat = JSONLogFormat
case "text" :
logFormat = TextLogFormat
case "" :
logFormat = TextLogFormat
default :
log . Error ( ) .
Str ( "func" , "GetLogConfig" ) .
Msgf ( "Could not parse log format: %s. Valid choices are 'json' or 'text'" , logFormatOpt )
}
return LogConfig {
Format : logFormat ,
Level : logLevel ,
}
}
2024-02-09 06:27:00 +00:00
func GetDatabaseConfig ( ) DatabaseConfig {
debug := viper . GetBool ( "database.debug" )
type_ := viper . GetString ( "database.type" )
switch type_ {
case DatabaseSqlite , DatabasePostgres :
break
case "sqlite" :
type_ = "sqlite3"
default :
2024-02-09 16:34:28 +00:00
log . Fatal ( ) .
Msgf ( "invalid database type %q, must be sqlite, sqlite3 or postgres" , type_ )
2024-02-09 06:27:00 +00:00
}
return DatabaseConfig {
Type : type_ ,
Debug : debug ,
Sqlite : SqliteConfig {
2024-02-09 16:34:28 +00:00
Path : util . AbsolutePathFromConfigPath (
viper . GetString ( "database.sqlite.path" ) ,
) ,
2024-02-09 06:27:00 +00:00
} ,
Postgres : PostgresConfig {
2024-02-09 16:34:28 +00:00
Host : viper . GetString ( "database.postgres.host" ) ,
Port : viper . GetInt ( "database.postgres.port" ) ,
Name : viper . GetString ( "database.postgres.name" ) ,
User : viper . GetString ( "database.postgres.user" ) ,
Pass : viper . GetString ( "database.postgres.pass" ) ,
Ssl : viper . GetString ( "database.postgres.ssl" ) ,
MaxOpenConnections : viper . GetInt ( "database.postgres.max_open_conns" ) ,
MaxIdleConnections : viper . GetInt ( "database.postgres.max_idle_conns" ) ,
ConnMaxIdleTimeSecs : viper . GetInt (
"database.postgres.conn_max_idle_time_secs" ,
) ,
2024-02-09 06:27:00 +00:00
} ,
}
}
2022-06-03 07:26:36 +00:00
func GetDNSConfig ( ) ( * tailcfg . DNSConfig , string ) {
if viper . IsSet ( "dns_config" ) {
dnsConfig := & tailcfg . DNSConfig { }
2022-10-31 15:26:18 +00:00
overrideLocalDNS := viper . GetBool ( "dns_config.override_local_dns" )
2022-06-03 07:26:36 +00:00
if viper . IsSet ( "dns_config.nameservers" ) {
nameserversStr := viper . GetStringSlice ( "dns_config.nameservers" )
2022-11-07 20:10:06 +00:00
nameservers := [ ] netip . Addr { }
resolvers := [ ] * dnstype . Resolver { }
for _ , nameserverStr := range nameserversStr {
// Search for explicit DNS-over-HTTPS resolvers
if strings . HasPrefix ( nameserverStr , "https://" ) {
resolvers = append ( resolvers , & dnstype . Resolver {
Addr : nameserverStr ,
} )
// This nameserver can not be parsed as an IP address
continue
}
2022-06-03 07:26:36 +00:00
2022-11-07 20:10:06 +00:00
// Parse nameserver as a regular IP
2022-09-01 22:04:04 +00:00
nameserver , err := netip . ParseAddr ( nameserverStr )
2022-06-03 07:26:36 +00:00
if err != nil {
log . Error ( ) .
Str ( "func" , "getDNSConfig" ) .
Err ( err ) .
Msgf ( "Could not parse nameserver IP: %s" , nameserverStr )
}
2022-11-07 20:10:06 +00:00
nameservers = append ( nameservers , nameserver )
resolvers = append ( resolvers , & dnstype . Resolver {
2022-06-03 07:26:36 +00:00
Addr : nameserver . String ( ) ,
2022-11-07 20:10:06 +00:00
} )
2022-06-03 07:26:36 +00:00
}
dnsConfig . Nameservers = nameservers
2022-10-31 15:26:18 +00:00
if overrideLocalDNS {
dnsConfig . Resolvers = resolvers
} else {
dnsConfig . FallbackResolvers = resolvers
}
2022-06-03 07:26:36 +00:00
}
if viper . IsSet ( "dns_config.restricted_nameservers" ) {
2023-01-23 11:34:12 +00:00
dnsConfig . Routes = make ( map [ string ] [ ] * dnstype . Resolver )
domains := [ ] string { }
restrictedDNS := viper . GetStringMapStringSlice (
"dns_config.restricted_nameservers" ,
)
for domain , restrictedNameservers := range restrictedDNS {
restrictedResolvers := make (
[ ] * dnstype . Resolver ,
len ( restrictedNameservers ) ,
2022-06-03 07:26:36 +00:00
)
2023-01-23 11:34:12 +00:00
for index , nameserverStr := range restrictedNameservers {
nameserver , err := netip . ParseAddr ( nameserverStr )
if err != nil {
log . Error ( ) .
Str ( "func" , "getDNSConfig" ) .
Err ( err ) .
Msgf ( "Could not parse restricted nameserver IP: %s" , nameserverStr )
}
restrictedResolvers [ index ] = & dnstype . Resolver {
Addr : nameserver . String ( ) ,
2022-06-03 07:26:36 +00:00
}
}
2023-01-23 11:34:12 +00:00
dnsConfig . Routes [ domain ] = restrictedResolvers
domains = append ( domains , domain )
2022-06-03 07:26:36 +00:00
}
2023-01-23 11:34:12 +00:00
dnsConfig . Domains = domains
2022-06-03 07:26:36 +00:00
}
if viper . IsSet ( "dns_config.domains" ) {
2022-10-31 14:59:50 +00:00
domains := viper . GetStringSlice ( "dns_config.domains" )
2022-12-27 16:37:13 +00:00
if len ( dnsConfig . Resolvers ) > 0 {
2022-10-31 14:59:50 +00:00
dnsConfig . Domains = domains
} else if domains != nil {
2022-06-03 07:26:36 +00:00
log . Warn ( ) .
2022-10-31 14:59:50 +00:00
Msg ( "Warning: dns_config.domains is set, but no nameservers are configured. Ignoring domains." )
2022-06-03 07:26:36 +00:00
}
}
2022-12-02 01:03:26 +00:00
if viper . IsSet ( "dns_config.extra_records" ) {
var extraRecords [ ] tailcfg . DNSRecord
err := viper . UnmarshalKey ( "dns_config.extra_records" , & extraRecords )
if err != nil {
log . Error ( ) .
Str ( "func" , "getDNSConfig" ) .
Err ( err ) .
Msgf ( "Could not parse dns_config.extra_records" )
}
dnsConfig . ExtraRecords = extraRecords
}
2022-10-31 14:59:50 +00:00
if viper . IsSet ( "dns_config.magic_dns" ) {
dnsConfig . Proxied = viper . GetBool ( "dns_config.magic_dns" )
}
2022-06-03 07:26:36 +00:00
var baseDomain string
if viper . IsSet ( "dns_config.base_domain" ) {
baseDomain = viper . GetString ( "dns_config.base_domain" )
} else {
baseDomain = "headscale.net" // does not really matter when MagicDNS is not enabled
}
2023-08-31 16:37:18 +00:00
log . Trace ( ) . Interface ( "dns_config" , dnsConfig ) . Msg ( "DNS configuration loaded" )
2022-06-03 07:26:36 +00:00
return dnsConfig , baseDomain
}
return nil , ""
}
2024-04-17 05:03:06 +00:00
func PrefixV4 ( ) ( * netip . Prefix , error ) {
2024-02-18 18:31:29 +00:00
prefixV4Str := viper . GetString ( "prefixes.v4" )
2024-04-17 05:03:06 +00:00
if prefixV4Str == "" {
return nil , nil
2024-02-18 18:31:29 +00:00
}
2024-04-17 05:03:06 +00:00
prefixV4 , err := netip . ParsePrefix ( prefixV4Str )
2024-02-18 18:31:29 +00:00
if err != nil {
2024-04-17 05:03:06 +00:00
return nil , fmt . Errorf ( "parsing IPv4 prefix from config: %w" , err )
2024-02-18 18:31:29 +00:00
}
builder := netipx . IPSetBuilder { }
builder . AddPrefix ( tsaddr . CGNATRange ( ) )
builder . AddPrefix ( tsaddr . TailscaleULARange ( ) )
ipSet , _ := builder . IPSet ( )
if ! ipSet . ContainsPrefix ( prefixV4 ) {
log . Warn ( ) .
Msgf ( "Prefix %s is not in the %s range. This is an unsupported configuration." ,
prefixV4Str , tsaddr . CGNATRange ( ) )
}
2024-04-17 05:03:06 +00:00
return & prefixV4 , nil
}
func PrefixV6 ( ) ( * netip . Prefix , error ) {
prefixV6Str := viper . GetString ( "prefixes.v6" )
if prefixV6Str == "" {
return nil , nil
}
prefixV6 , err := netip . ParsePrefix ( prefixV6Str )
if err != nil {
return nil , fmt . Errorf ( "parsing IPv6 prefix from config: %w" , err )
}
builder := netipx . IPSetBuilder { }
builder . AddPrefix ( tsaddr . CGNATRange ( ) )
builder . AddPrefix ( tsaddr . TailscaleULARange ( ) )
ipSet , _ := builder . IPSet ( )
2024-02-18 18:31:29 +00:00
if ! ipSet . ContainsPrefix ( prefixV6 ) {
log . Warn ( ) .
Msgf ( "Prefix %s is not in the %s range. This is an unsupported configuration." ,
prefixV6Str , tsaddr . TailscaleULARange ( ) )
}
2024-04-17 05:03:06 +00:00
return & prefixV6 , nil
2024-02-18 18:31:29 +00:00
}
2022-06-05 15:47:12 +00:00
func GetHeadscaleConfig ( ) ( * Config , error ) {
2022-11-18 17:02:34 +00:00
if IsCLIConfigured ( ) {
return & Config {
CLI : CLIConfig {
Address : viper . GetString ( "cli.address" ) ,
APIKey : viper . GetString ( "cli.api_key" ) ,
Timeout : viper . GetDuration ( "cli.timeout" ) ,
Insecure : viper . GetBool ( "cli.insecure" ) ,
} ,
} , nil
}
2024-04-17 05:03:06 +00:00
prefix4 , err := PrefixV4 ( )
if err != nil {
return nil , err
}
prefix6 , err := PrefixV6 ( )
2024-02-18 18:31:29 +00:00
if err != nil {
return nil , err
}
2024-04-17 05:03:06 +00:00
allocStr := viper . GetString ( "prefixes.allocation" )
var alloc IPAllocationStrategy
switch allocStr {
case string ( IPAllocationStrategySequential ) :
alloc = IPAllocationStrategySequential
case string ( IPAllocationStrategyRandom ) :
alloc = IPAllocationStrategyRandom
default :
log . Fatal ( ) . Msgf ( "config error, prefixes.allocation is set to %s, which is not a valid strategy, allowed options: %s, %s" , allocStr , IPAllocationStrategySequential , IPAllocationStrategyRandom )
}
2022-06-03 07:26:36 +00:00
dnsConfig , baseDomain := GetDNSConfig ( )
derpConfig := GetDERPConfig ( )
logConfig := GetLogTailConfig ( )
2022-06-09 19:20:11 +00:00
randomizeClientPort := viper . GetBool ( "randomize_client_port" )
2022-06-03 07:26:36 +00:00
2023-01-10 11:46:42 +00:00
oidcClientSecret := viper . GetString ( "oidc.client_secret" )
oidcClientSecretPath := viper . GetString ( "oidc.client_secret_path" )
if oidcClientSecretPath != "" && oidcClientSecret != "" {
return nil , errOidcMutuallyExclusive
}
if oidcClientSecretPath != "" {
secretBytes , err := os . ReadFile ( os . ExpandEnv ( oidcClientSecretPath ) )
if err != nil {
return nil , err
}
2024-01-25 08:53:34 +00:00
oidcClientSecret = strings . TrimSpace ( string ( secretBytes ) )
2023-01-10 11:46:42 +00:00
}
2022-06-05 15:47:12 +00:00
return & Config {
2022-06-03 08:37:45 +00:00
ServerURL : viper . GetString ( "server_url" ) ,
Addr : viper . GetString ( "listen_addr" ) ,
MetricsAddr : viper . GetString ( "metrics_listen_addr" ) ,
GRPCAddr : viper . GetString ( "grpc_listen_addr" ) ,
GRPCAllowInsecure : viper . GetBool ( "grpc_allow_insecure" ) ,
DisableUpdateCheck : viper . GetBool ( "disable_check_updates" ) ,
2022-06-03 07:26:36 +00:00
2024-04-17 05:03:06 +00:00
PrefixV4 : prefix4 ,
PrefixV6 : prefix6 ,
IPAllocation : IPAllocationStrategy ( alloc ) ,
2024-02-18 18:31:29 +00:00
2023-05-11 07:09:18 +00:00
NoisePrivateKeyPath : util . AbsolutePathFromConfigPath (
2022-08-21 08:42:23 +00:00
viper . GetString ( "noise.private_key_path" ) ,
2022-08-13 09:14:38 +00:00
) ,
2022-06-03 07:26:36 +00:00
BaseDomain : baseDomain ,
DERP : derpConfig ,
EphemeralNodeInactivityTimeout : viper . GetDuration (
"ephemeral_node_inactivity_timeout" ,
) ,
2022-07-12 10:52:03 +00:00
NodeUpdateCheckInterval : viper . GetDuration (
"node_update_check_interval" ,
2022-07-12 10:27:28 +00:00
) ,
2024-02-09 06:27:00 +00:00
Database : GetDatabaseConfig ( ) ,
2022-06-03 07:26:36 +00:00
2022-06-03 08:14:14 +00:00
TLS : GetTLSConfig ( ) ,
2022-06-03 07:26:36 +00:00
DNSConfig : dnsConfig ,
ACMEEmail : viper . GetString ( "acme_email" ) ,
ACMEURL : viper . GetString ( "acme_url" ) ,
UnixSocket : viper . GetString ( "unix_socket" ) ,
2023-05-11 07:09:18 +00:00
UnixSocketPermission : util . GetFileMode ( "unix_socket_permission" ) ,
2022-06-03 07:26:36 +00:00
OIDC : OIDCConfig {
2022-09-26 07:57:28 +00:00
OnlyStartIfOIDCIsAvailable : viper . GetBool (
"oidc.only_start_if_oidc_is_available" ,
) ,
2022-06-03 07:26:36 +00:00
Issuer : viper . GetString ( "oidc.issuer" ) ,
ClientID : viper . GetString ( "oidc.client_id" ) ,
2023-01-10 11:46:42 +00:00
ClientSecret : oidcClientSecret ,
2022-06-03 07:26:36 +00:00
Scope : viper . GetStringSlice ( "oidc.scope" ) ,
ExtraParams : viper . GetStringMapString ( "oidc.extra_params" ) ,
AllowedDomains : viper . GetStringSlice ( "oidc.allowed_domains" ) ,
AllowedUsers : viper . GetStringSlice ( "oidc.allowed_users" ) ,
2022-12-07 00:08:01 +00:00
AllowedGroups : viper . GetStringSlice ( "oidc.allowed_groups" ) ,
2022-06-03 07:26:36 +00:00
StripEmaildomain : viper . GetBool ( "oidc.strip_email_domain" ) ,
2023-01-31 11:40:38 +00:00
Expiry : func ( ) time . Duration {
// if set to 0, we assume no expiry
if value := viper . GetString ( "oidc.expiry" ) ; value == "0" {
return maxDuration
} else {
expiry , err := model . ParseDuration ( value )
if err != nil {
log . Warn ( ) . Msg ( "failed to parse oidc.expiry, defaulting back to 180 days" )
return defaultOIDCExpiryTime
}
return time . Duration ( expiry )
}
} ( ) ,
UseExpiryFromToken : viper . GetBool ( "oidc.use_expiry_from_token" ) ,
2022-06-03 07:26:36 +00:00
} ,
2022-06-09 19:20:11 +00:00
LogTail : logConfig ,
RandomizeClientPort : randomizeClientPort ,
2022-06-03 07:26:36 +00:00
ACL : GetACLConfig ( ) ,
2022-09-11 19:37:23 +00:00
2022-11-18 17:48:34 +00:00
CLI : CLIConfig {
Address : viper . GetString ( "cli.address" ) ,
APIKey : viper . GetString ( "cli.api_key" ) ,
Timeout : viper . GetDuration ( "cli.timeout" ) ,
Insecure : viper . GetBool ( "cli.insecure" ) ,
} ,
2022-09-11 19:37:23 +00:00
Log : GetLogConfig ( ) ,
2024-02-23 09:59:24 +00:00
// TODO(kradalby): Document these settings when more stable
Tuning : Tuning {
BatchChangeDelay : viper . GetDuration ( "tuning.batch_change_delay" ) ,
NodeMapSessionBufferedChanSize : viper . GetInt ( "tuning.node_mapsession_buffered_chan_size" ) ,
} ,
2022-06-05 15:47:12 +00:00
} , nil
2022-06-03 07:26:36 +00:00
}
2022-11-18 17:02:34 +00:00
func IsCLIConfigured ( ) bool {
return viper . GetString ( "cli.address" ) != "" && viper . GetString ( "cli.api_key" ) != ""
}