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

improve errors for missing directories #1765

Merged
merged 3 commits into from
Feb 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,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

Expand Down
11 changes: 9 additions & 2 deletions docs/running-headscale-container.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
db_type: sqlite3
db_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
Expand Down
14 changes: 14 additions & 0 deletions hscontrol/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
_ "net/http/pprof" //nolint
"os"
"os/signal"
"path/filepath"
"runtime"
"strings"
"sync"
Expand Down Expand Up @@ -70,6 +71,7 @@ const (
AuthPrefix = "Bearer "
updateInterval = 5000
privateKeyFileMode = 0o600
headscaleDirPerm = 0o700

registerCacheExpiration = time.Minute * 15
registerCacheCleanup = time.Minute * 20
Expand Down Expand Up @@ -556,6 +558,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)
Expand Down Expand Up @@ -923,6 +931,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...")
Expand Down
7 changes: 7 additions & 0 deletions hscontrol/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"net/netip"
"path/filepath"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -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{
Expand Down
20 changes: 20 additions & 0 deletions hscontrol/util/file.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package util

import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
Expand Down Expand Up @@ -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
}
Loading