diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 963663a7..589d8c58 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v2 with: - go-version: "1.17.3" + go-version: "1.17" - name: Install dependencies run: | diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b3c64006..6fb985f7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,6 +14,12 @@ jobs: with: version: latest + # Only block PRs on new problems. + # If this is not enabled, we will end up having PRs + # blocked because new linters has appared and other + # parts of the code is affected. + only-new-issues: true + prettier-lint: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/test-integration.yml b/.github/workflows/test-integration.yml index abf46d32..d4179ea2 100644 --- a/.github/workflows/test-integration.yml +++ b/.github/workflows/test-integration.yml @@ -17,7 +17,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v2 with: - go-version: "1.17.3" + go-version: "1.17" - name: Run Integration tests run: go test -tags integration -timeout 30m diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4540482c..651063fc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v2 with: - go-version: "1.17.3" # The Go version to download (if necessary) and use. + go-version: "1.17" # The Go version to download (if necessary) and use. # Install all the dependencies - name: Install dependencies diff --git a/CHANGELOG.md b/CHANGELOG.md index 38904b0a..f823f0c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ **TBD (TBD):** +- Fixed issue where hosts deleted from control server may be written back to the database, as long as they are connected to the control server [#278](https://github.com/juanfont/headscale/pull/278) + ) + **0.12.3 (2022-01-13):** **Changes**: diff --git a/Dockerfile b/Dockerfile index 75fd0c5f..050439b5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,6 +9,7 @@ RUN go mod download COPY . . RUN go install -a -ldflags="-extldflags=-static" -tags netgo,sqlite_omit_load_extension ./cmd/headscale +RUN strip /go/bin/headscale RUN test -e /go/bin/headscale # Production image diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 8e0ef6d2..a75bbfea 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -10,6 +10,7 @@ RUN go mod download COPY . . RUN go install -a -ldflags="-extldflags=-static" -tags netgo,sqlite_omit_load_extension ./cmd/headscale +RUN strip /go/bin/headscale RUN test -e /go/bin/headscale # Production image diff --git a/README.md b/README.md index 07da4aab..9a599d3d 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ To contribute to Headscale you would need the lastest version of [Go](https://go ### Code style -To ensure we have some consistency with a growing number of contributes, this project has adopted linting and style/formatting rules: +To ensure we have some consistency with a growing number of contributions, this project has adopted linting and style/formatting rules: The **Go** code is linted with [`golangci-lint`](https://golangci-lint.run) and formatted with [`golines`](https://github.com/segmentio/golines) (width 88) and @@ -76,7 +76,7 @@ run `make lint` and `make fmt` before committing any code. The **Proto** code is linted with [`buf`](https://docs.buf.build/lint/overview) and formatted with [`clang-format`](https://clang.llvm.org/docs/ClangFormat.html). -The **rest** (markdown, yaml, etc) is formatted with [`prettier`](https://prettier.io). +The **rest** (Markdown, YAML, etc) is formatted with [`prettier`](https://prettier.io). Check out the `.golangci.yaml` and `Makefile` to see the specific configuration. @@ -92,7 +92,7 @@ make install-protobuf-plugins ### Testing and building -Some parts of the project requires the generation of Go code from Protobuf (if changes is made in `proto/`) and it must be (re-)generated with: +Some parts of the project require the generation of Go code from Protobuf (if changes are made in `proto/`) and it must be (re-)generated with: ```shell make generate diff --git a/app.go b/app.go index b7dc8c67..8abc0f2b 100644 --- a/app.go +++ b/app.go @@ -722,7 +722,8 @@ func readOrCreatePrivateKey(path string) (*key.MachinePrivate, error) { return nil, fmt.Errorf("failed to read private key file: %w", err) } - privateKeyEnsurePrefix := PrivateKeyEnsurePrefix(string(privateKey)) + trimmedPrivateKey := strings.TrimSpace(string(privateKey)) + privateKeyEnsurePrefix := PrivateKeyEnsurePrefix(trimmedPrivateKey) var machineKey key.MachinePrivate if err = machineKey.UnmarshalText([]byte(privateKeyEnsurePrefix)); err != nil { diff --git a/docs/running-headscale-linux.md b/docs/running-headscale-linux.md index d53b72f8..7a62fcc6 100644 --- a/docs/running-headscale-linux.md +++ b/docs/running-headscale-linux.md @@ -21,7 +21,7 @@ wget --output-document=/usr/local/bin/headscale \ chmod +x /usr/local/bin/headscale ``` -3. Prepare a direction to hold `headscale` configuration and the [SQLite](https://www.sqlite.org/) database: +3. Prepare a directory to hold `headscale` configuration and the [SQLite](https://www.sqlite.org/) database: ```shell # Directory for configuration @@ -32,7 +32,7 @@ mkdir -p /etc/headscale mkdir -p /var/lib/headscale ``` -4. Create an empty SQlite datebase: +4. Create an empty SQLite database: ```shell touch /var/lib/headscale/db.sqlite @@ -106,7 +106,7 @@ tailscale up --login-server --authkey ## Running `headscale` in the background with SystemD -In this section it will be demonstrated how to run `headscale` as a service in the background with [SystemD](https://www.freedesktop.org/wiki/Software/systemd/). +This section demonstrates how to run `headscale` as a service in the background with [SystemD](https://www.freedesktop.org/wiki/Software/systemd/). This should work on most modern Linux distributions. 1. Create a SystemD service configuration at `/etc/systemd/system/headscale.service` containing: diff --git a/machine.go b/machine.go index d0be2df7..64fd11b8 100644 --- a/machine.go +++ b/machine.go @@ -362,6 +362,14 @@ func (h *Headscale) DeleteMachine(machine *Machine) error { return h.RequestMapUpdates(namespaceID) } +func (h *Headscale) TouchMachine(machine *Machine) error { + return h.db.Updates(Machine{ + ID: machine.ID, + LastSeen: machine.LastSeen, + LastSuccessfulUpdate: machine.LastSuccessfulUpdate, + }).Error +} + // HardDeleteMachine hard deletes a Machine from the database. func (h *Headscale) HardDeleteMachine(machine *Machine) error { err := h.RemoveSharedMachineFromAllNamespaces(machine) diff --git a/poll.go b/poll.go index 883aa621..bcd6c33f 100644 --- a/poll.go +++ b/poll.go @@ -76,6 +76,8 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) { Str("handler", "PollNetMap"). Msgf("Failed to fetch machine from the database with Machine key: %s", machineKey.String()) ctx.String(http.StatusInternalServerError, "") + + return } log.Trace(). Str("handler", "PollNetMap"). @@ -102,7 +104,7 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) { machine.Endpoints = datatypes.JSON(endpoints) machine.LastSeen = &now } - h.db.Save(&machine) + h.db.Updates(machine) data, err := h.getMapResponse(machineKey, req, machine) if err != nil { @@ -313,6 +315,10 @@ func (h *Headscale) PollNetMapStream( Str("channel", "pollData"). Err(err). Msg("Cannot update machine from database") + + // client has been removed from database + // since the stream opened, terminate connection. + return false } now := time.Now().UTC() machine.LastSeen = &now @@ -321,13 +327,22 @@ func (h *Headscale) PollNetMapStream( Set(float64(now.Unix())) machine.LastSuccessfulUpdate = &now - h.db.Save(&machine) - log.Trace(). - Str("handler", "PollNetMapStream"). - Str("machine", machine.Name). - Str("channel", "pollData"). - Int("bytes", len(data)). - Msg("Machine entry in database updated successfully after sending pollData") + err = h.TouchMachine(machine) + if err != nil { + log.Error(). + Str("handler", "PollNetMapStream"). + Str("machine", machine.Name). + Str("channel", "pollData"). + Err(err). + Msg("Cannot update machine LastSuccessfulUpdate") + } else { + log.Trace(). + Str("handler", "PollNetMapStream"). + Str("machine", machine.Name). + Str("channel", "pollData"). + Int("bytes", len(data)). + Msg("Machine entry in database updated successfully after sending pollData") + } return true @@ -366,16 +381,29 @@ func (h *Headscale) PollNetMapStream( Str("channel", "keepAlive"). Err(err). Msg("Cannot update machine from database") + + // client has been removed from database + // since the stream opened, terminate connection. + return false } now := time.Now().UTC() machine.LastSeen = &now - h.db.Save(&machine) - log.Trace(). - Str("handler", "PollNetMapStream"). - Str("machine", machine.Name). - Str("channel", "keepAlive"). - Int("bytes", len(data)). - Msg("Machine updated successfully after sending keep alive") + err = h.TouchMachine(machine) + if err != nil { + log.Error(). + Str("handler", "PollNetMapStream"). + Str("machine", machine.Name). + Str("channel", "keepAlive"). + Err(err). + Msg("Cannot update machine LastSeen") + } else { + log.Trace(). + Str("handler", "PollNetMapStream"). + Str("machine", machine.Name). + Str("channel", "keepAlive"). + Int("bytes", len(data)). + Msg("Machine updated successfully after sending keep alive") + } return true @@ -443,6 +471,10 @@ func (h *Headscale) PollNetMapStream( Str("channel", "update"). Err(err). Msg("Cannot update machine from database") + + // client has been removed from database + // since the stream opened, terminate connection. + return false } now := time.Now().UTC() @@ -450,7 +482,15 @@ func (h *Headscale) PollNetMapStream( Set(float64(now.Unix())) machine.LastSuccessfulUpdate = &now - h.db.Save(&machine) + err = h.TouchMachine(machine) + if err != nil { + log.Error(). + Str("handler", "PollNetMapStream"). + Str("machine", machine.Name). + Str("channel", "update"). + Err(err). + Msg("Cannot update machine LastSuccessfulUpdate") + } } else { var lastUpdate time.Time if machine.LastSuccessfulUpdate != nil { @@ -482,10 +522,22 @@ func (h *Headscale) PollNetMapStream( Str("channel", "Done"). Err(err). Msg("Cannot update machine from database") + + // client has been removed from database + // since the stream opened, terminate connection. + return false } now := time.Now().UTC() machine.LastSeen = &now - h.db.Save(&machine) + err = h.TouchMachine(machine) + if err != nil { + log.Error(). + Str("handler", "PollNetMapStream"). + Str("machine", machine.Name). + Str("channel", "Done"). + Err(err). + Msg("Cannot update machine LastSeen") + } return false }