diff --git a/acls.go b/acls.go index 4017e28e..1d54b26d 100644 --- a/acls.go +++ b/acls.go @@ -25,8 +25,11 @@ const ( ) const ( + Base8 = 8 Base10 = 10 BitSize16 = 16 + BitSize32 = 32 + BitSize64 = 64 portRangeBegin = 0 portRangeEnd = 65535 expectedTokenItems = 2 diff --git a/app.go b/app.go index b9d570c5..7ea76b2e 100644 --- a/app.go +++ b/app.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "io/fs" "net" "net/http" "net/url" @@ -95,7 +96,8 @@ type Config struct { DNSConfig *tailcfg.DNSConfig - UnixSocket string + UnixSocket string + UnixSocketPermission fs.FileMode OIDC OIDCConfig @@ -426,6 +428,11 @@ func (h *Headscale) Serve() error { return fmt.Errorf("failed to set up gRPC socket: %w", err) } + // Change socket permissions + if err := os.Chmod(h.cfg.UnixSocket, h.cfg.UnixSocketPermission); err != nil { + return fmt.Errorf("failed change permission of gRPC socket: %w", err) + } + // Handle common process-killing signals so we can gracefully shut down: sigc := make(chan os.Signal, 1) signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index a4856642..c2016271 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -5,10 +5,12 @@ import ( "encoding/json" "errors" "fmt" + "io/fs" "net/url" "os" "path/filepath" "regexp" + "strconv" "strings" "time" @@ -23,6 +25,10 @@ import ( "tailscale.com/types/dnstype" ) +const ( + PermissionFallback = 0o700 +) + func LoadConfig(path string) error { viper.SetConfigName("config") if path == "" { @@ -48,6 +54,7 @@ func LoadConfig(path string) error { viper.SetDefault("dns_config", nil) viper.SetDefault("unix_socket", "/var/run/headscale.sock") + viper.SetDefault("unix_socket_permission", "0o770") viper.SetDefault("cli.insecure", false) viper.SetDefault("cli.timeout", "5s") @@ -257,7 +264,8 @@ func getHeadscaleConfig() headscale.Config { ACMEEmail: viper.GetString("acme_email"), ACMEURL: viper.GetString("acme_url"), - UnixSocket: viper.GetString("unix_socket"), + UnixSocket: viper.GetString("unix_socket"), + UnixSocketPermission: GetFileMode("unix_socket_permission"), OIDC: headscale.OIDCConfig{ Issuer: viper.GetString("oidc.issuer"), @@ -448,3 +456,14 @@ func loadOIDCMatchMap() map[string]string { return strMap } + +func GetFileMode(key string) fs.FileMode { + modeStr := viper.GetString(key) + + mode, err := strconv.ParseUint(modeStr, headscale.Base8, headscale.BitSize64) + if err != nil { + return PermissionFallback + } + + return fs.FileMode(mode) +} diff --git a/cmd/headscale/headscale_test.go b/cmd/headscale/headscale_test.go index 0b56a073..218e458e 100644 --- a/cmd/headscale/headscale_test.go +++ b/cmd/headscale/headscale_test.go @@ -1,6 +1,7 @@ package main import ( + "io/fs" "io/ioutil" "os" "path/filepath" @@ -60,6 +61,7 @@ func (*Suite) TestConfigLoading(c *check.C) { c.Assert(viper.GetString("tls_letsencrypt_listen"), check.Equals, ":http") c.Assert(viper.GetString("tls_letsencrypt_challenge_type"), check.Equals, "HTTP-01") c.Assert(viper.GetStringSlice("dns_config.nameservers")[0], check.Equals, "1.1.1.1") + c.Assert(cli.GetFileMode("unix_socket_permission"), check.Equals, fs.FileMode(0o770)) } func (*Suite) TestDNSConfigLoading(c *check.C) { diff --git a/config-example.yaml b/config-example.yaml index 3301669d..3a867a36 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -149,6 +149,7 @@ dns_config: # Note: for local development, you probably want to change this to: # unix_socket: ./headscale.sock unix_socket: /var/run/headscale.sock +unix_socket_permission: "0770" # # headscale supports experimental OpenID connect support, # it is still being tested and might have some bugs, please