diff --git a/CHANGELOG.md b/CHANGELOG.md index 16475c9f..c6a89498 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ after improving the test harness as part of adopting [#1460](https://github.com/ - Add a filepath entry to [`derp.server.private_key_path`](https://github.com/juanfont/headscale/blob/b35993981297e18393706b2c963d6db882bba6aa/config-example.yaml#L95) - Docker images are now built with goreleaser (ko) [#1716](https://github.com/juanfont/headscale/pull/1716) [#1763](https://github.com/juanfont/headscale/pull/1763) - Entrypoint of container image has changed from shell to headscale, require change from `headscale serve` to `serve` + - `/var/lib/headscale` and `/var/run/headscale` is no longer created automatically, see [container docs](./docs/running-headscale-container.md) ### Changes diff --git a/docs/running-headscale-container.md b/docs/running-headscale-container.md index 74fca474..862ba032 100644 --- a/docs/running-headscale-container.md +++ b/docs/running-headscale-container.md @@ -57,15 +57,22 @@ server_url: http://your-host-name:8080 # Listen to 0.0.0.0 so it's accessible outside the container metrics_listen_addr: 0.0.0.0:9090 # The default /var/lib/headscale path is not writable in the container -private_key_path: /etc/headscale/private.key -# The default /var/lib/headscale path is not writable in the container noise: private_key_path: /etc/headscale/noise_private.key +# The default /var/lib/headscale path is not writable in the container +derp: + private_key_path: /etc/headscale/private.key +# The default /var/run/headscale path is not writable in the container +unix_socket: /etc/headscale/headscale.sock # The default /var/lib/headscale path is not writable in the container database.type: sqlite3 database.sqlite.path: /etc/headscale/db.sqlite ``` +Alternatively, you can mount `/var/lib` and `/var/run` from your host system by adding +`--volume $(pwd)/lib:/var/lib/headscale` and `--volume $(pwd)/run:/var/run/headscale` +in the next step. + 4. Start the headscale server while working in the host headscale directory: ```shell diff --git a/hscontrol/app.go b/hscontrol/app.go index bb7253b5..0075eb46 100644 --- a/hscontrol/app.go +++ b/hscontrol/app.go @@ -11,6 +11,7 @@ import ( _ "net/http/pprof" //nolint "os" "os/signal" + "path/filepath" "runtime" "strings" "sync" @@ -69,6 +70,7 @@ const ( AuthPrefix = "Bearer " updateInterval = 5000 privateKeyFileMode = 0o600 + headscaleDirPerm = 0o700 registerCacheExpiration = time.Minute * 15 registerCacheCleanup = time.Minute * 20 @@ -552,6 +554,12 @@ func (h *Headscale) Serve() error { return fmt.Errorf("unable to remove old socket file: %w", err) } + socketDir := filepath.Dir(h.cfg.UnixSocket) + err = util.EnsureDir(socketDir) + if err != nil { + return fmt.Errorf("setting up unix socket: %w", err) + } + socketListener, err := net.Listen("unix", h.cfg.UnixSocket) if err != nil { return fmt.Errorf("failed to set up gRPC socket: %w", err) @@ -919,6 +927,12 @@ func notFoundHandler( } func readOrCreatePrivateKey(path string) (*key.MachinePrivate, error) { + dir := filepath.Dir(path) + err := util.EnsureDir(dir) + if err != nil { + return nil, fmt.Errorf("ensuring private key directory: %w", err) + } + privateKey, err := os.ReadFile(path) if errors.Is(err, os.ErrNotExist) { log.Info().Str("path", path).Msg("No private key file at path, creating...") diff --git a/hscontrol/db/db.go b/hscontrol/db/db.go index 4ded07fb..ff9e5f27 100644 --- a/hscontrol/db/db.go +++ b/hscontrol/db/db.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "net/netip" + "path/filepath" "strconv" "strings" "time" @@ -344,6 +345,12 @@ func openDB(cfg types.DatabaseConfig) (*gorm.DB, error) { switch cfg.Type { case types.DatabaseSqlite: + dir := filepath.Dir(cfg.Sqlite.Path) + err := util.EnsureDir(dir) + if err != nil { + return nil, fmt.Errorf("creating directory for sqlite: %w", err) + } + db, err := gorm.Open( sqlite.Open(cfg.Sqlite.Path+"?_synchronous=1&_journal_mode=WAL"), &gorm.Config{ diff --git a/hscontrol/util/file.go b/hscontrol/util/file.go index 5b8656ff..86af636c 100644 --- a/hscontrol/util/file.go +++ b/hscontrol/util/file.go @@ -1,6 +1,8 @@ package util import ( + "errors" + "fmt" "io/fs" "os" "path/filepath" @@ -42,3 +44,21 @@ func GetFileMode(key string) fs.FileMode { return fs.FileMode(mode) } + +func EnsureDir(dir string) error { + if _, err := os.Stat(dir); os.IsNotExist(err) { + err := os.MkdirAll(dir, PermissionFallback) + if err != nil { + if errors.Is(err, os.ErrPermission) { + return fmt.Errorf( + "creating directory %s, failed with permission error, is it located somewhere Headscale can write?", + dir, + ) + } + + return fmt.Errorf("creating directory %s: %w", dir, err) + } + } + + return nil +}