diff --git a/.dockerignore b/.dockerignore index 057a20e7..e3acf996 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,6 +3,7 @@ // development integration_test.go integration_test/ +!integration_test/etc_embedded_derp/tls/server.crt Dockerfile* docker-compose* diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 565684fe..fa1c06da 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -7,3 +7,4 @@ Dockerfile* @ohdearaugustin .goreleaser.yaml @ohdearaugustin /docs/ @ohdearaugustin /.github/workflows/ @ohdearaugustin +/.github/renovate.json @ohdearaugustin diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index c7015806..abd3b185 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,10 +1,10 @@ -- [] read the [CONTRIBUTING guidelines](README.md#user-content-contributing) -- [] raised a GitHub issue or discussed it on the projects chat beforehand -- [] added unit tests -- [] added integration tests -- [] updated documentation if needed -- [] updated CHANGELOG.md +- [ ] read the [CONTRIBUTING guidelines](README.md#user-content-contributing) +- [ ] raised a GitHub issue or discussed it on the projects chat beforehand +- [ ] added unit tests +- [ ] added integration tests +- [ ] updated documentation if needed +- [ ] updated CHANGELOG.md diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 00000000..ce38ba96 --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,38 @@ +{ + "baseBranches": ["main"], + "username": "renovate-release", + "gitAuthor": "Renovate Bot ", + "branchPrefix": "renovateaction/", + "onboarding": false, + "extends": ["config:base", ":rebaseStalePrs"], + "ignorePresets": [":prHourlyLimit2"], + "enabledManagers": ["dockerfile", "gomod", "github-actions","regex" ], + "includeForks": true, + "repositories": ["juanfont/headscale"], + "platform": "github", + "packageRules": [ + { + "matchDatasources": ["go"], + "groupName": "Go modules", + "groupSlug": "gomod", + "separateMajorMinor": false + }, + { + "matchDatasources": ["docker"], + "groupName": "Dockerfiles", + "groupSlug": "dockerfiles" + } + ], + "regexManagers": [ + { + "fileMatch": [ + ".github/workflows/.*.yml$" + ], + "matchStrings": [ + "\\s*go-version:\\s*\"?(?.*?)\"?\\n" + ], + "datasourceTemplate": "golang-version", + "depNameTemplate": "actions/go-version" + } + ] +} diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml index e27b6eda..aa0d9264 100644 --- a/.github/workflows/contributors.yml +++ b/.github/workflows/contributors.yml @@ -4,13 +4,24 @@ on: push: branches: - main - + workflow_dispatch: jobs: add-contributors: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: BobAnkh/add-contributors@master + - name: Delete upstream contributor branch + # Allow continue on failure to account for when the + # upstream branch is deleted or does not exist. + continue-on-error: true + run: git push origin --delete update-contributors + - name: Create up-to-date contributors branch + run: git checkout -B update-contributors + - name: Push empty contributors branch + run: git push origin update-contributors + - name: Switch back to main + run: git checkout main + - uses: BobAnkh/add-contributors@v0.2.2 with: CONTRIBUTOR: "## Contributors" COLUMN_PER_ROW: "6" diff --git a/.github/workflows/renovatebot.yml b/.github/workflows/renovatebot.yml new file mode 100644 index 00000000..53b976ca --- /dev/null +++ b/.github/workflows/renovatebot.yml @@ -0,0 +1,27 @@ +--- +name: Renovate +on: + schedule: + - cron: "* * 5,20 * *" # Every 5th and 20th of the month + workflow_dispatch: +jobs: + renovate: + runs-on: ubuntu-latest + steps: + - name: Get token + id: get_token + uses: machine-learning-apps/actions-app-token@master + with: + APP_PEM: ${{ secrets.RENOVATEBOT_SECRET }} + APP_ID: ${{ secrets.RENOVATEBOT_APP_ID }} + + - name: Checkout + uses: actions/checkout@v2.0.0 + + - name: Self-hosted Renovate + uses: renovatebot/github-action@v31.81.3 + with: + configurationFile: .github/renovate.json + token: "x-access-token:${{ steps.get_token.outputs.app_token }}" + # env: + # LOG_LEVEL: "debug" diff --git a/.github/workflows/test-integration.yml b/.github/workflows/test-integration.yml index d9c52c7b..b8fd85a6 100644 --- a/.github/workflows/test-integration.yml +++ b/.github/workflows/test-integration.yml @@ -29,4 +29,4 @@ jobs: - name: Run Integration tests if: steps.changed-files.outputs.any_changed == 'true' - run: go test -tags integration -timeout 30m + run: make test_integration diff --git a/.golangci.yaml b/.golangci.yaml index 965f5496..56fdc28e 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -29,6 +29,7 @@ linters: - wrapcheck - dupl - makezero + - maintidx # We might want to enable this, but it might be a lot of work - cyclop @@ -48,6 +49,7 @@ linters-settings: - ip - ok - c + - tt gocritic: disabled-checks: diff --git a/.goreleaser.yml b/.goreleaser.yml index d1dec26f..bd517d9f 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -10,15 +10,12 @@ builds: - id: darwin-amd64 main: ./cmd/headscale/headscale.go mod_timestamp: "{{ .CommitTimestamp }}" + env: + - CGO_ENABLED=0 goos: - darwin goarch: - amd64 - env: - - PKG_CONFIG_SYSROOT_DIR=/sysroot/macos/amd64 - - PKG_CONFIG_PATH=/sysroot/macos/amd64/usr/local/lib/pkgconfig - - CC=o64-clang - - CXX=o64-clang++ flags: - -mod=readonly ldflags: @@ -27,46 +24,40 @@ builds: - id: linux-armhf main: ./cmd/headscale/headscale.go mod_timestamp: "{{ .CommitTimestamp }}" + env: + - CGO_ENABLED=0 goos: - linux goarch: - arm goarm: - "7" - env: - - CC=arm-linux-gnueabihf-gcc - - CXX=arm-linux-gnueabihf-g++ - - CGO_FLAGS=--sysroot=/sysroot/linux/armhf - - CGO_LDFLAGS=--sysroot=/sysroot/linux/armhf - - PKG_CONFIG_SYSROOT_DIR=/sysroot/linux/armhf - - PKG_CONFIG_PATH=/sysroot/linux/armhf/opt/vc/lib/pkgconfig:/sysroot/linux/armhf/usr/lib/arm-linux-gnueabihf/pkgconfig:/sysroot/linux/armhf/usr/lib/pkgconfig:/sysroot/linux/armhf/usr/local/lib/pkgconfig flags: - -mod=readonly ldflags: - -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}} - id: linux-amd64 + mod_timestamp: "{{ .CommitTimestamp }}" env: - - CGO_ENABLED=1 + - CGO_ENABLED=0 goos: - linux goarch: - amd64 main: ./cmd/headscale/headscale.go - mod_timestamp: "{{ .CommitTimestamp }}" ldflags: - -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}} - id: linux-arm64 + mod_timestamp: "{{ .CommitTimestamp }}" + env: + - CGO_ENABLED=0 goos: - linux goarch: - arm64 - env: - - CGO_ENABLED=1 - - CC=aarch64-linux-gnu-gcc main: ./cmd/headscale/headscale.go - mod_timestamp: "{{ .CommitTimestamp }}" ldflags: - -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}} diff --git a/CHANGELOG.md b/CHANGELOG.md index 70bda12d..6ce0c35b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,75 @@ # CHANGELOG -**TBD (TBD):** +## 0.15.0 (2022-xx-xx) + +**Note:** Take a backup of your database before upgrading. + +### BREAKING + +- Boundaries between Namespaces has been removed and all nodes can communicate by default [#357](https://github.com/juanfont/headscale/pull/357) + - To limit access between nodes, use [ACLs](./docs/acls.md). +- `/metrics` is now a configurable host:port endpoint: [#344](https://github.com/juanfont/headscale/pull/344). You must update your `config.yaml` file to include: + ```yaml + metrics_listen_addr: 127.0.0.1:9090 + ``` + +### Features + +- Add support for writing ACL files with YAML [#359](https://github.com/juanfont/headscale/pull/359) +- Users can now use emails in ACL's groups [#372](https://github.com/juanfont/headscale/issues/372) +- Add shorthand aliases for commands and subcommands [#376](https://github.com/juanfont/headscale/pull/376) +- Add `/windows` endpoint for Windows configuration instructions + registry file download [#392](https://github.com/juanfont/headscale/pull/392) +- Added embedded DERP server into Headscale [#388](https://github.com/juanfont/headscale/pull/388) + +### Changes + +- Fix a bug were the same IP could be assigned to multiple hosts if joined in quick succession [#346](https://github.com/juanfont/headscale/pull/346) +- Simplify the code behind registration of machines [#366](https://github.com/juanfont/headscale/pull/366) + - Nodes are now only written to database if they are registrated successfully +- Fix a limitation in the ACLs that prevented users to write rules with `*` as source [#374](https://github.com/juanfont/headscale/issues/374) +- Reduce the overhead of marshal/unmarshal for Hostinfo, routes and endpoints by using specific types in Machine [#371](https://github.com/juanfont/headscale/pull/371) +- Apply normalization function to FQDN on hostnames when hosts registers and retrieve informations [#363](https://github.com/juanfont/headscale/issues/363) + +## 0.14.0 (2022-02-24) + +**UPCOMING ### BREAKING +From the **next\*\* version (`0.15.0`), all machines will be able to communicate regardless of +if they are in the same namespace. This means that the behaviour currently limited to ACLs +will become default. From version `0.15.0`, all limitation of communications must be done +with ACLs. + +This is a part of aligning `headscale`'s behaviour with Tailscale's upstream behaviour. + +### BREAKING + +- ACLs have been rewritten to align with the bevaviour Tailscale Control Panel provides. **NOTE:** This is only active if you use ACLs + - Namespaces are now treated as Users + - All machines can communicate with all machines by default + - Tags should now work correctly and adding a host to Headscale should now reload the rules. + - The documentation have a [fictional example](docs/acls.md) that should cover some use cases of the ACLs features + +### Features + +- Add support for configurable mTLS [docs](docs/tls.md#configuring-mutual-tls-authentication-mtls) [#297](https://github.com/juanfont/headscale/pull/297) + +### Changes + +- Remove dependency on CGO (switch from CGO SQLite to pure Go) [#346](https://github.com/juanfont/headscale/pull/346) **0.13.0 (2022-02-18):** -**Features**: +### Features - Add IPv6 support to the prefix assigned to namespaces - Add API Key support - Enable remote control of `headscale` via CLI [docs](docs/remote-cli.md) - Enable HTTP API (beta, subject to change) +- OpenID Connect users will be mapped per namespaces + - Each user will get its own namespace, created if it does not exist + - `oidc.domain_map` option has been removed + - `strip_email_domain` option has been added (see [config-example.yaml](./config_example.yaml)) -**Changes**: +### Changes - `ip_prefix` is now superseded by `ip_prefixes` in the configuration [#208](https://github.com/juanfont/headscale/pull/208) - Upgrade `tailscale` (1.20.4) and other dependencies to latest [#314](https://github.com/juanfont/headscale/pull/314) @@ -20,35 +78,35 @@ **0.12.4 (2022-01-29):** -**Changes**: +### Changes - Make gRPC Unix Socket permissions configurable [#292](https://github.com/juanfont/headscale/pull/292) - Trim whitespace before reading Private Key from file [#289](https://github.com/juanfont/headscale/pull/289) - Add new command to generate a private key for `headscale` [#290](https://github.com/juanfont/headscale/pull/290) - 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):** +## 0.12.3 (2022-01-13) -**Changes**: +### Changes - Added Alpine container [#270](https://github.com/juanfont/headscale/pull/270) - Minor updates in dependencies [#271](https://github.com/juanfont/headscale/pull/271) -**0.12.2 (2022-01-11):** +## 0.12.2 (2022-01-11) Happy New Year! -**Changes**: +### Changes - Fix Docker release [#258](https://github.com/juanfont/headscale/pull/258) - Rewrite main docs [#262](https://github.com/juanfont/headscale/pull/262) - Improve Docker docs [#263](https://github.com/juanfont/headscale/pull/263) -**0.12.1 (2021-12-24):** +## 0.12.1 (2021-12-24) (We are skipping 0.12.0 to correct a mishap done weeks ago with the version tagging) -**BREAKING**: +### BREAKING - Upgrade to Tailscale 1.18 [#229](https://github.com/juanfont/headscale/pull/229) - This change requires a new format for private key, private keys are now generated automatically: @@ -56,19 +114,19 @@ Happy New Year! 2. Restart `headscale`, a new key will be generated. 3. Restart all Tailscale clients to fetch the new key -**Changes**: +### Changes - Unify configuration example [#197](https://github.com/juanfont/headscale/pull/197) - Add stricter linting and formatting [#223](https://github.com/juanfont/headscale/pull/223) -**Features**: +### Features - Add gRPC and HTTP API (HTTP API is currently disabled) [#204](https://github.com/juanfont/headscale/pull/204) - Use gRPC between the CLI and the server [#206](https://github.com/juanfont/headscale/pull/206), [#212](https://github.com/juanfont/headscale/pull/212) - Beta OpenID Connect support [#126](https://github.com/juanfont/headscale/pull/126), [#227](https://github.com/juanfont/headscale/pull/227) -**0.11.0 (2021-10-25):** +## 0.11.0 (2021-10-25) -**BREAKING**: +### BREAKING - Make headscale fetch DERP map from URL and file [#196](https://github.com/juanfont/headscale/pull/196) diff --git a/Dockerfile b/Dockerfile index 6e577753..3ab9c1d1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Builder image -FROM docker.io/golang:1.17.7-bullseye AS build +FROM docker.io/golang:1.17.8-bullseye AS build ENV GOPATH /go WORKDIR /go/src/headscale @@ -8,7 +8,7 @@ RUN go mod download COPY . . -RUN go install -a -ldflags="-extldflags=-static" -tags netgo,sqlite_omit_load_extension ./cmd/headscale +RUN GGO_ENABLED=0 GOOS=linux go install -a ./cmd/headscale RUN strip /go/bin/headscale RUN test -e /go/bin/headscale diff --git a/Dockerfile.alpine b/Dockerfile.alpine index b07e1904..1f0d6353 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,5 +1,5 @@ # Builder image -FROM docker.io/golang:1.17.7-alpine AS build +FROM docker.io/golang:1.17.8-alpine AS build ENV GOPATH /go WORKDIR /go/src/headscale @@ -9,7 +9,7 @@ RUN go mod download COPY . . -RUN go install -a -ldflags="-extldflags=-static" -tags netgo,sqlite_omit_load_extension ./cmd/headscale +RUN GGO_ENABLED=0 GOOS=linux go install -a ./cmd/headscale RUN strip /go/bin/headscale RUN test -e /go/bin/headscale diff --git a/Dockerfile.debug b/Dockerfile.debug index 3d2675f9..e73c0647 100644 --- a/Dockerfile.debug +++ b/Dockerfile.debug @@ -1,5 +1,5 @@ # Builder image -FROM docker.io/golang:1.17.7-bullseye AS build +FROM docker.io/golang:1.17.8-bullseye AS build ENV GOPATH /go WORKDIR /go/src/headscale @@ -8,7 +8,7 @@ RUN go mod download COPY . . -RUN go install -a -ldflags="-extldflags=-static" -tags netgo,sqlite_omit_load_extension ./cmd/headscale +RUN GGO_ENABLED=0 GOOS=linux go install -a ./cmd/headscale RUN test -e /go/bin/headscale # Debug image diff --git a/Dockerfile.tailscale b/Dockerfile.tailscale index f96c6b9c..32a8ce7b 100644 --- a/Dockerfile.tailscale +++ b/Dockerfile.tailscale @@ -7,5 +7,10 @@ RUN apt-get update \ && curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.gpg | apt-key add - \ && curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.list | tee /etc/apt/sources.list.d/tailscale.list \ && apt-get update \ - && apt-get install -y tailscale=${TAILSCALE_VERSION} dnsutils \ + && apt-get install -y ca-certificates tailscale=${TAILSCALE_VERSION} dnsutils \ && rm -rf /var/lib/apt/lists/* + +ADD integration_test/etc_embedded_derp/tls/server.crt /usr/local/share/ca-certificates/ +RUN chmod 644 /usr/local/share/ca-certificates/server.crt + +RUN update-ca-certificates diff --git a/Makefile b/Makefile index 69935bc0..73630d3f 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ PROTO_SOURCES = $(call rwildcard,,*.proto) build: - go build -ldflags "-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$(version)" cmd/headscale/headscale.go + GGO_ENABLED=0 go build -ldflags "-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$(version)" cmd/headscale/headscale.go dev: lint test build @@ -18,11 +18,14 @@ test: @go test -coverprofile=coverage.out ./... test_integration: - go test -tags integration -timeout 30m -count=1 ./... + go test -failfast -tags integration -timeout 30m -count=1 ./... test_integration_cli: go test -tags integration -v integration_cli_test.go integration_common_test.go +test_integration_derp: + go test -tags integration -v integration_embedded_derp_test.go integration_common_test.go + coverprofile_func: go tool cover -func=coverage.out diff --git a/README.md b/README.md index 1d86380e..d74b19f4 100644 --- a/README.md +++ b/README.md @@ -2,44 +2,68 @@ ![ci](https://github.com/juanfont/headscale/actions/workflows/test.yml/badge.svg) -An open source, self-hosted implementation of the Tailscale coordination server. +An open source, self-hosted implementation of the Tailscale control server. Join our [Discord](https://discord.gg/XcQxk2VHjx) server for a chat. -**Note:** Always select the same GitHub tag as the released version you use to ensure you have the correct example configuration and documentation. The `main` branch might contain unreleased changes. +**Note:** Always select the same GitHub tag as the released version you use +to ensure you have the correct example configuration and documentation. +The `main` branch might contain unreleased changes. -## Overview +## What is Tailscale -Tailscale is [a modern VPN](https://tailscale.com/) built on top of [Wireguard](https://www.wireguard.com/). It [works like an overlay network](https://tailscale.com/blog/how-tailscale-works/) between the computers of your networks - using [NAT traversal](https://tailscale.com/blog/how-nat-traversal-works/). +Tailscale is [a modern VPN](https://tailscale.com/) built on top of +[Wireguard](https://www.wireguard.com/). +It [works like an overlay network](https://tailscale.com/blog/how-tailscale-works/) +between the computers of your networks - using +[NAT traversal](https://tailscale.com/blog/how-nat-traversal-works/). -Everything in Tailscale is Open Source, except the GUI clients for proprietary OS (Windows and macOS/iOS), and the 'coordination/control server'. +Everything in Tailscale is Open Source, except the GUI clients for proprietary OS +(Windows and macOS/iOS), and the control server. -The control server works as an exchange point of Wireguard public keys for the nodes in the Tailscale network. It also assigns the IP addresses of the clients, creates the boundaries between each user, enables sharing machines between users, and exposes the advertised routes of your nodes. +The control server works as an exchange point of Wireguard public keys for the +nodes in the Tailscale network. It assigns the IP addresses of the clients, +creates the boundaries between each user, enables sharing machines between users, +and exposes the advertised routes of your nodes. -headscale implements this coordination server. +A [Tailscale network (tailnet)](https://tailscale.com/kb/1136/tailnet/) is private +network which Tailscale assigns to a user in terms of private users or an +organisations. + +## Design goal + +`headscale` aims to implement a self-hosted, open source alternative to the Tailscale +control server. `headscale` has a narrower scope and an instance of `headscale` +implements a _single_ Tailnet, which is typically what a single organisation, or +home/personal setup would use. + +`headscale` uses terms that maps to Tailscale's control server, consult the +[glossary](./docs/glossary.md) for explainations. ## Support -If you like `headscale` and find it useful, there is sponsorship and donation buttons available in the repo. +If you like `headscale` and find it useful, there is a sponsorship and donation +buttons available in the repo. -If you would like to sponsor features, bugs or prioritisation, reach out to one of the maintainers. +If you would like to sponsor features, bugs or prioritisation, reach out to +one of the maintainers. -## Status +## Features -- [x] Base functionality (nodes can communicate with each other) -- [x] Node registration through the web flow -- [x] Network changes are relayed to the nodes -- [x] Namespaces support (~tailnets in Tailscale.com naming) -- [x] Routing (advertise & accept, including exit nodes) -- [x] Node registration via pre-auth keys (including reusable keys, and ephemeral node support) -- [x] JSON-formatted output -- [x] ACLs -- [x] Taildrop (File Sharing) -- [x] Support for alternative IP ranges in the tailnets (default Tailscale's 100.64.0.0/10) -- [x] DNS (passing DNS servers to nodes) -- [x] Single-Sign-On (via Open ID Connect) -- [x] Share nodes between namespaces -- [x] MagicDNS (see `docs/`) +- Full "base" support of Tailscale's features +- Configurable DNS + - [Split DNS](https://tailscale.com/kb/1054/dns/#using-dns-settings-in-the-admin-console) +- Node registration + - Single-Sign-On (via Open ID Connect) + - Pre authenticated key +- Taildrop (File Sharing) +- [Access control lists](https://tailscale.com/kb/1018/acls/) +- [MagicDNS](https://tailscale.com/kb/1081/magicdns) +- Support for multiple IP ranges in the tailnet +- Dual stack (IPv4 and IPv6) +- Routing advertising (including exit nodes) +- Ephemeral nodes +- Embedded [DERP server](https://tailscale.com/blog/how-tailscale-works/#encrypted-tcp-relays-derp) ## Client OS support @@ -47,15 +71,12 @@ If you would like to sponsor features, bugs or prioritisation, reach out to one | ------- | ----------------------------------------------------------------------------------------------------------------- | | Linux | Yes | | OpenBSD | Yes | +| FreeBSD | Yes | | macOS | Yes (see `/apple` on your headscale for more information) | | Windows | Yes [docs](./docs/windows-client.md) | | Android | [You need to compile the client yourself](https://github.com/juanfont/headscale/issues/58#issuecomment-885255270) | | iOS | Not yet | -## Roadmap - -Suggestions/PRs welcomed! - ## Running headscale Please have a look at the documentation under [`docs/`](docs/). @@ -67,11 +88,15 @@ Please have a look at the documentation under [`docs/`](docs/). ## Contributing -To contribute to Headscale you would need the lastest version of [Go](https://golang.org) and [Buf](https://buf.build)(Protobuf generator). +To contribute to headscale you would need the lastest version of [Go](https://golang.org) +and [Buf](https://buf.build)(Protobuf generator). + +PRs and suggestions are welcome. ### Code style -To ensure we have some consistency with a growing number of contributions, 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 @@ -98,7 +123,8 @@ make install-protobuf-plugins ### Testing and building -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: +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 @@ -122,6 +148,13 @@ make build + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + - - + + + + + +
+ + Kristoffer +
+ Kristoffer Dalby +
+
Juan @@ -130,10 +163,10 @@ make build - - Kristoffer + + Adrien
- Kristoffer Dalby + Adrien Raffin-Caboisse
@@ -150,6 +183,29 @@ make build ohdearaugustin + + e-zk/ +
+ e-zk +
+
+ + Justin +
+ Justin Angel +
+
+ + Alessandro +
+ Alessandro (Ale) Segala +
+
unreality/ @@ -157,6 +213,20 @@ make build unreality + + Nico/ +
+ Nico +
+
+ + Eugen +
+ Eugen Biegler +
+
Aaron @@ -166,6 +236,27 @@ make build
+ + Fernando +
+ Fernando De Lucchi +
+
+ + Hoàng +
+ Hoàng Đức Hiếu +
+
+ + Michael +
+ Michael G. +
+
Paul @@ -187,6 +278,22 @@ make build Silver Bullet
+ + Stefan +
+ Stefan Majer +
+
+ + lachy2849/ +
+ lachy2849 +
+
thomas/ @@ -194,6 +301,20 @@ make build thomas + + Abraham +
+ Abraham Ingersoll +
+
+ + Artem +
+ Artem Klevtsov +
+
Arthur @@ -201,6 +322,15 @@ make build Arthur Woimbée
+ + Bryan +
+ Bryan Stenson +
+
Felix @@ -208,8 +338,6 @@ make build Felix Kronlage-Dammers
Felix @@ -217,6 +345,57 @@ make build Felix Yan + + JJGadgets/ +
+ JJGadgets +
+
+ + Jamie +
+ Jamie Greeff +
+
+ + Jim +
+ Jim Tittsler +
+
+ + Pierre +
+ Pierre Carru +
+
+ + rcursaru/ +
+ rcursaru +
+
+ + WhiteSource +
+ WhiteSource Renovate +
+
+ + Ryan +
+ Ryan Fowler +
+
Shaanan @@ -224,6 +403,15 @@ make build Shaanan Cohney + + Tanner/ +
+ Tanner +
+
Teteros/ @@ -252,8 +440,6 @@ make build Tjerk Woudsma
Zakhar @@ -261,6 +447,15 @@ make build Zakhar Bessarab + + ZiYuan/ +
+ ZiYuan +
+
derelm/ @@ -275,6 +470,27 @@ make build ignoramous + + lion24/ +
+ lion24 +
+
+ + pernila/ +
+ pernila +
+
+ + Wakeful-Cloud/ +
+ Wakeful-Cloud +
+
zy/ diff --git a/acls.go b/acls.go index 326625b9..63c3955c 100644 --- a/acls.go +++ b/acls.go @@ -5,23 +5,23 @@ import ( "fmt" "io" "os" + "path/filepath" "strconv" "strings" "github.com/rs/zerolog/log" "github.com/tailscale/hujson" + "gopkg.in/yaml.v3" "inet.af/netaddr" "tailscale.com/tailcfg" ) const ( - errEmptyPolicy = Error("empty policy") - errInvalidAction = Error("invalid action") - errInvalidUserSection = Error("invalid user section") - errInvalidGroup = Error("invalid group") - errInvalidTag = Error("invalid tag") - errInvalidNamespace = Error("invalid namespace") - errInvalidPortFormat = Error("invalid port format") + errEmptyPolicy = Error("empty policy") + errInvalidAction = Error("invalid action") + errInvalidGroup = Error("invalid group") + errInvalidTag = Error("invalid tag") + errInvalidPortFormat = Error("invalid port format") ) const ( @@ -54,28 +54,52 @@ func (h *Headscale) LoadACLPolicy(path string) error { return err } - ast, err := hujson.Parse(policyBytes) - if err != nil { - return err - } - ast.Standardize() - policyBytes = ast.Pack() - err = json.Unmarshal(policyBytes, &policy) - if err != nil { - return err + switch filepath.Ext(path) { + case ".yml", ".yaml": + log.Debug(). + Str("path", path). + Bytes("file", policyBytes). + Msg("Loading ACLs from YAML") + + err := yaml.Unmarshal(policyBytes, &policy) + if err != nil { + return err + } + + log.Trace(). + Interface("policy", policy). + Msg("Loaded policy from YAML") + + default: + ast, err := hujson.Parse(policyBytes) + if err != nil { + return err + } + + ast.Standardize() + policyBytes = ast.Pack() + err = json.Unmarshal(policyBytes, &policy) + if err != nil { + return err + } } + if policy.IsZero() { return errEmptyPolicy } h.aclPolicy = &policy + + return h.UpdateACLRules() +} + +func (h *Headscale) UpdateACLRules() error { rules, err := h.generateACLRules() if err != nil { return err } - h.aclRules = rules - log.Trace().Interface("ACL", rules).Msg("ACL rules generated") + h.aclRules = rules return nil } @@ -83,16 +107,23 @@ func (h *Headscale) LoadACLPolicy(path string) error { func (h *Headscale) generateACLRules() ([]tailcfg.FilterRule, error) { rules := []tailcfg.FilterRule{} + if h.aclPolicy == nil { + return nil, errEmptyPolicy + } + + machines, err := h.ListMachines() + if err != nil { + return nil, err + } + for index, acl := range h.aclPolicy.ACLs { if acl.Action != "accept" { return nil, errInvalidAction } - filterRule := tailcfg.FilterRule{} - srcIPs := []string{} for innerIndex, user := range acl.Users { - srcs, err := h.generateACLPolicySrcIP(user) + srcs, err := h.generateACLPolicySrcIP(machines, *h.aclPolicy, user) if err != nil { log.Error(). Msgf("Error parsing ACL %d, User %d", index, innerIndex) @@ -101,11 +132,10 @@ func (h *Headscale) generateACLRules() ([]tailcfg.FilterRule, error) { } srcIPs = append(srcIPs, srcs...) } - filterRule.SrcIPs = srcIPs destPorts := []tailcfg.NetPortRange{} for innerIndex, ports := range acl.Ports { - dests, err := h.generateACLPolicyDestPorts(ports) + dests, err := h.generateACLPolicyDestPorts(machines, *h.aclPolicy, ports) if err != nil { log.Error(). Msgf("Error parsing ACL %d, Port %d", index, innerIndex) @@ -124,11 +154,17 @@ func (h *Headscale) generateACLRules() ([]tailcfg.FilterRule, error) { return rules, nil } -func (h *Headscale) generateACLPolicySrcIP(u string) ([]string, error) { - return h.expandAlias(u) +func (h *Headscale) generateACLPolicySrcIP( + machines []Machine, + aclPolicy ACLPolicy, + u string, +) ([]string, error) { + return expandAlias(machines, aclPolicy, u, h.cfg.OIDC.StripEmaildomain) } func (h *Headscale) generateACLPolicyDestPorts( + machines []Machine, + aclPolicy ACLPolicy, d string, ) ([]tailcfg.NetPortRange, error) { tokens := strings.Split(d, ":") @@ -149,11 +185,16 @@ func (h *Headscale) generateACLPolicyDestPorts( alias = fmt.Sprintf("%s:%s", tokens[0], tokens[1]) } - expanded, err := h.expandAlias(alias) + expanded, err := expandAlias( + machines, + aclPolicy, + alias, + h.cfg.OIDC.StripEmaildomain, + ) if err != nil { return nil, err } - ports, err := h.expandPorts(tokens[len(tokens)-1]) + ports, err := expandPorts(tokens[len(tokens)-1]) if err != nil { return nil, err } @@ -172,21 +213,33 @@ func (h *Headscale) generateACLPolicyDestPorts( return dests, nil } -func (h *Headscale) expandAlias(alias string) ([]string, error) { +// expandalias has an input of either +// - a namespace +// - a group +// - a tag +// and transform these in IPAddresses. +func expandAlias( + machines []Machine, + aclPolicy ACLPolicy, + alias string, + stripEmailDomain bool, +) ([]string, error) { + ips := []string{} if alias == "*" { return []string{"*"}, nil } + log.Debug(). + Str("alias", alias). + Msg("Expanding") + if strings.HasPrefix(alias, "group:") { - if _, ok := h.aclPolicy.Groups[alias]; !ok { - return nil, errInvalidGroup + namespaces, err := expandGroup(aclPolicy, alias, stripEmailDomain) + if err != nil { + return ips, err } - ips := []string{} - for _, n := range h.aclPolicy.Groups[alias] { - nodes, err := h.ListMachinesInNamespace(n) - if err != nil { - return nil, errInvalidNamespace - } + for _, n := range namespaces { + nodes := filterMachinesByNamespace(machines, n) for _, node := range nodes { ips = append(ips, node.IPAddresses.ToStringSlice()...) } @@ -196,35 +249,17 @@ func (h *Headscale) expandAlias(alias string) ([]string, error) { } if strings.HasPrefix(alias, "tag:") { - if _, ok := h.aclPolicy.TagOwners[alias]; !ok { - return nil, errInvalidTag + owners, err := expandTagOwners(aclPolicy, alias, stripEmailDomain) + if err != nil { + return ips, err } - - // This will have HORRIBLE performance. - // We need to change the data model to better store tags - machines := []Machine{} - if err := h.db.Where("registered").Find(&machines).Error; err != nil { - return nil, err - } - ips := []string{} - for _, machine := range machines { - hostinfo := tailcfg.Hostinfo{} - if len(machine.HostInfo) != 0 { - hi, err := machine.HostInfo.MarshalJSON() - if err != nil { - return nil, err - } - err = json.Unmarshal(hi, &hostinfo) - if err != nil { - return nil, err - } - - // FIXME: Check TagOwners allows this - for _, t := range hostinfo.RequestTags { - if alias[4:] == t { + for _, namespace := range owners { + machines := filterMachinesByNamespace(machines, namespace) + for _, machine := range machines { + hi := machine.GetHostInfo() + for _, t := range hi.RequestTags { + if alias == t { ips = append(ips, machine.IPAddresses.ToStringSlice()...) - - break } } } @@ -233,38 +268,75 @@ func (h *Headscale) expandAlias(alias string) ([]string, error) { return ips, nil } - n, err := h.GetNamespace(alias) - if err == nil { - nodes, err := h.ListMachinesInNamespace(n.Name) - if err != nil { - return nil, err - } - ips := []string{} - for _, n := range nodes { - ips = append(ips, n.IPAddresses.ToStringSlice()...) - } + // if alias is a namespace + nodes := filterMachinesByNamespace(machines, alias) + nodes = excludeCorrectlyTaggedNodes(aclPolicy, nodes, alias) + for _, n := range nodes { + ips = append(ips, n.IPAddresses.ToStringSlice()...) + } + if len(ips) > 0 { return ips, nil } - if h, ok := h.aclPolicy.Hosts[alias]; ok { + // if alias is an host + if h, ok := aclPolicy.Hosts[alias]; ok { return []string{h.String()}, nil } + // if alias is an IP ip, err := netaddr.ParseIP(alias) if err == nil { return []string{ip.String()}, nil } + // if alias is an CIDR cidr, err := netaddr.ParseIPPrefix(alias) if err == nil { return []string{cidr.String()}, nil } - return nil, errInvalidUserSection + log.Warn().Msgf("No IPs found with the alias %v", alias) + + return ips, nil } -func (h *Headscale) expandPorts(portsStr string) (*[]tailcfg.PortRange, error) { +// excludeCorrectlyTaggedNodes will remove from the list of input nodes the ones +// that are correctly tagged since they should not be listed as being in the namespace +// we assume in this function that we only have nodes from 1 namespace. +func excludeCorrectlyTaggedNodes( + aclPolicy ACLPolicy, + nodes []Machine, + namespace string, +) []Machine { + out := []Machine{} + tags := []string{} + for tag, ns := range aclPolicy.TagOwners { + if containsString(ns, namespace) { + tags = append(tags, tag) + } + } + // for each machine if tag is in tags list, don't append it. + for _, machine := range nodes { + hi := machine.GetHostInfo() + + found := false + for _, t := range hi.RequestTags { + if containsString(tags, t) { + found = true + + break + } + } + if !found { + out = append(out, machine) + } + } + + return out +} + +func expandPorts(portsStr string) (*[]tailcfg.PortRange, error) { if portsStr == "*" { return &[]tailcfg.PortRange{ {First: portRangeBegin, Last: portRangeEnd}, @@ -306,3 +378,82 @@ func (h *Headscale) expandPorts(portsStr string) (*[]tailcfg.PortRange, error) { return &ports, nil } + +func filterMachinesByNamespace(machines []Machine, namespace string) []Machine { + out := []Machine{} + for _, machine := range machines { + if machine.Namespace.Name == namespace { + out = append(out, machine) + } + } + + return out +} + +// expandTagOwners will return a list of namespace. An owner can be either a namespace or a group +// a group cannot be composed of groups. +func expandTagOwners( + aclPolicy ACLPolicy, + tag string, + stripEmailDomain bool, +) ([]string, error) { + var owners []string + ows, ok := aclPolicy.TagOwners[tag] + if !ok { + return []string{}, fmt.Errorf( + "%w. %v isn't owned by a TagOwner. Please add one first. https://tailscale.com/kb/1018/acls/#tag-owners", + errInvalidTag, + tag, + ) + } + for _, owner := range ows { + if strings.HasPrefix(owner, "group:") { + gs, err := expandGroup(aclPolicy, owner, stripEmailDomain) + if err != nil { + return []string{}, err + } + owners = append(owners, gs...) + } else { + owners = append(owners, owner) + } + } + + return owners, nil +} + +// expandGroup will return the list of namespace inside the group +// after some validation. +func expandGroup( + aclPolicy ACLPolicy, + group string, + stripEmailDomain bool, +) ([]string, error) { + outGroups := []string{} + aclGroups, ok := aclPolicy.Groups[group] + if !ok { + return []string{}, fmt.Errorf( + "group %v isn't registered. %w", + group, + errInvalidGroup, + ) + } + for _, group := range aclGroups { + if strings.HasPrefix(group, "group:") { + return []string{}, fmt.Errorf( + "%w. A group cannot be composed of groups. https://tailscale.com/kb/1018/acls/#groups", + errInvalidGroup, + ) + } + grp, err := NormalizeToFQDNRules(group, stripEmailDomain) + if err != nil { + return []string{}, fmt.Errorf( + "failed to normalize group %q, err: %w", + group, + errInvalidGroup, + ) + } + outGroups = append(outGroups, grp) + } + + return outGroups, nil +} diff --git a/acls_test.go b/acls_test.go index c35f4f84..9dcc40ba 100644 --- a/acls_test.go +++ b/acls_test.go @@ -1,7 +1,13 @@ package headscale import ( + "errors" + "reflect" + "testing" + "gopkg.in/check.v1" + "inet.af/netaddr" + "tailscale.com/tailcfg" ) func (s *Suite) TestWrongPath(c *check.C) { @@ -52,6 +58,255 @@ func (s *Suite) TestBasicRule(c *check.C) { c.Assert(rules, check.NotNil) } +// TODO(kradalby): Make tests values safe, independent and descriptive. +func (s *Suite) TestInvalidAction(c *check.C) { + app.aclPolicy = &ACLPolicy{ + ACLs: []ACL{ + {Action: "invalidAction", Users: []string{"*"}, Ports: []string{"*:*"}}, + }, + } + err := app.UpdateACLRules() + c.Assert(errors.Is(err, errInvalidAction), check.Equals, true) +} + +func (s *Suite) TestInvalidGroupInGroup(c *check.C) { + // this ACL is wrong because the group in users sections doesn't exist + app.aclPolicy = &ACLPolicy{ + Groups: Groups{ + "group:test": []string{"foo"}, + "group:error": []string{"foo", "group:test"}, + }, + ACLs: []ACL{ + {Action: "accept", Users: []string{"group:error"}, Ports: []string{"*:*"}}, + }, + } + err := app.UpdateACLRules() + c.Assert(errors.Is(err, errInvalidGroup), check.Equals, true) +} + +func (s *Suite) TestInvalidTagOwners(c *check.C) { + // this ACL is wrong because no tagOwners own the requested tag for the server + app.aclPolicy = &ACLPolicy{ + ACLs: []ACL{ + {Action: "accept", Users: []string{"tag:foo"}, Ports: []string{"*:*"}}, + }, + } + err := app.UpdateACLRules() + c.Assert(errors.Is(err, errInvalidTag), check.Equals, true) +} + +// this test should validate that we can expand a group in a TagOWner section and +// match properly the IP's of the related hosts. The owner is valid and the tag is also valid. +// the tag is matched in the Users section. +func (s *Suite) TestValidExpandTagOwnersInUsers(c *check.C) { + namespace, err := app.CreateNamespace("user1") + c.Assert(err, check.IsNil) + + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) + c.Assert(err, check.IsNil) + + _, err = app.GetMachine("user1", "testmachine") + c.Assert(err, check.NotNil) + hostInfo := tailcfg.Hostinfo{ + OS: "centos", + Hostname: "testmachine", + RequestTags: []string{"tag:test"}, + } + + machine := Machine{ + ID: 0, + MachineKey: "foo", + NodeKey: "bar", + DiscoKey: "faa", + Name: "testmachine", + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")}, + NamespaceID: namespace.ID, + RegisterMethod: RegisterMethodAuthKey, + AuthKeyID: uint(pak.ID), + HostInfo: HostInfo(hostInfo), + } + app.db.Save(&machine) + + app.aclPolicy = &ACLPolicy{ + Groups: Groups{"group:test": []string{"user1", "user2"}}, + TagOwners: TagOwners{"tag:test": []string{"user3", "group:test"}}, + ACLs: []ACL{ + {Action: "accept", Users: []string{"tag:test"}, Ports: []string{"*:*"}}, + }, + } + err = app.UpdateACLRules() + c.Assert(err, check.IsNil) + c.Assert(app.aclRules, check.HasLen, 1) + c.Assert(app.aclRules[0].SrcIPs, check.HasLen, 1) + c.Assert(app.aclRules[0].SrcIPs[0], check.Equals, "100.64.0.1") +} + +// this test should validate that we can expand a group in a TagOWner section and +// match properly the IP's of the related hosts. The owner is valid and the tag is also valid. +// the tag is matched in the Ports section. +func (s *Suite) TestValidExpandTagOwnersInPorts(c *check.C) { + namespace, err := app.CreateNamespace("user1") + c.Assert(err, check.IsNil) + + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) + c.Assert(err, check.IsNil) + + _, err = app.GetMachine("user1", "testmachine") + c.Assert(err, check.NotNil) + hostInfo := tailcfg.Hostinfo{ + OS: "centos", + Hostname: "testmachine", + RequestTags: []string{"tag:test"}, + } + + machine := Machine{ + ID: 1, + MachineKey: "12345", + NodeKey: "bar", + DiscoKey: "faa", + Name: "testmachine", + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")}, + NamespaceID: namespace.ID, + RegisterMethod: RegisterMethodAuthKey, + AuthKeyID: uint(pak.ID), + HostInfo: HostInfo(hostInfo), + } + app.db.Save(&machine) + + app.aclPolicy = &ACLPolicy{ + Groups: Groups{"group:test": []string{"user1", "user2"}}, + TagOwners: TagOwners{"tag:test": []string{"user3", "group:test"}}, + ACLs: []ACL{ + {Action: "accept", Users: []string{"*"}, Ports: []string{"tag:test:*"}}, + }, + } + err = app.UpdateACLRules() + c.Assert(err, check.IsNil) + c.Assert(app.aclRules, check.HasLen, 1) + c.Assert(app.aclRules[0].DstPorts, check.HasLen, 1) + c.Assert(app.aclRules[0].DstPorts[0].IP, check.Equals, "100.64.0.1") +} + +// need a test with: +// tag on a host that isn't owned by a tag owners. So the namespace +// of the host should be valid. +func (s *Suite) TestInvalidTagValidNamespace(c *check.C) { + namespace, err := app.CreateNamespace("user1") + c.Assert(err, check.IsNil) + + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) + c.Assert(err, check.IsNil) + + _, err = app.GetMachine("user1", "testmachine") + c.Assert(err, check.NotNil) + hostInfo := tailcfg.Hostinfo{ + OS: "centos", + Hostname: "testmachine", + RequestTags: []string{"tag:foo"}, + } + + machine := Machine{ + ID: 1, + MachineKey: "12345", + NodeKey: "bar", + DiscoKey: "faa", + Name: "testmachine", + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")}, + NamespaceID: namespace.ID, + RegisterMethod: RegisterMethodAuthKey, + AuthKeyID: uint(pak.ID), + HostInfo: HostInfo(hostInfo), + } + app.db.Save(&machine) + + app.aclPolicy = &ACLPolicy{ + TagOwners: TagOwners{"tag:test": []string{"user1"}}, + ACLs: []ACL{ + {Action: "accept", Users: []string{"user1"}, Ports: []string{"*:*"}}, + }, + } + err = app.UpdateACLRules() + c.Assert(err, check.IsNil) + c.Assert(app.aclRules, check.HasLen, 1) + c.Assert(app.aclRules[0].SrcIPs, check.HasLen, 1) + c.Assert(app.aclRules[0].SrcIPs[0], check.Equals, "100.64.0.1") +} + +// tag on a host is owned by a tag owner, the tag is valid. +// an ACL rule is matching the tag to a namespace. It should not be valid since the +// host should be tied to the tag now. +func (s *Suite) TestValidTagInvalidNamespace(c *check.C) { + namespace, err := app.CreateNamespace("user1") + c.Assert(err, check.IsNil) + + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) + c.Assert(err, check.IsNil) + + _, err = app.GetMachine("user1", "webserver") + c.Assert(err, check.NotNil) + hostInfo := tailcfg.Hostinfo{ + OS: "centos", + Hostname: "webserver", + RequestTags: []string{"tag:webapp"}, + } + + machine := Machine{ + ID: 1, + MachineKey: "12345", + NodeKey: "bar", + DiscoKey: "faa", + Name: "webserver", + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")}, + NamespaceID: namespace.ID, + RegisterMethod: RegisterMethodAuthKey, + AuthKeyID: uint(pak.ID), + HostInfo: HostInfo(hostInfo), + } + app.db.Save(&machine) + _, err = app.GetMachine("user1", "user") + hostInfo2 := tailcfg.Hostinfo{ + OS: "debian", + Hostname: "Hostname", + } + c.Assert(err, check.NotNil) + machine = Machine{ + ID: 2, + MachineKey: "56789", + NodeKey: "bar2", + DiscoKey: "faab", + Name: "user", + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")}, + NamespaceID: namespace.ID, + RegisterMethod: RegisterMethodAuthKey, + AuthKeyID: uint(pak.ID), + HostInfo: HostInfo(hostInfo2), + } + app.db.Save(&machine) + + app.aclPolicy = &ACLPolicy{ + TagOwners: TagOwners{"tag:webapp": []string{"user1"}}, + ACLs: []ACL{ + { + Action: "accept", + Users: []string{"user1"}, + Ports: []string{"tag:webapp:80,443"}, + }, + }, + } + err = app.UpdateACLRules() + c.Assert(err, check.IsNil) + c.Assert(app.aclRules, check.HasLen, 1) + c.Assert(app.aclRules[0].SrcIPs, check.HasLen, 1) + c.Assert(app.aclRules[0].SrcIPs[0], check.Equals, "100.64.0.2") + c.Assert(app.aclRules[0].DstPorts, check.HasLen, 2) + c.Assert(app.aclRules[0].DstPorts[0].Ports.First, check.Equals, uint16(80)) + c.Assert(app.aclRules[0].DstPorts[0].Ports.Last, check.Equals, uint16(80)) + c.Assert(app.aclRules[0].DstPorts[0].IP, check.Equals, "100.64.0.1") + c.Assert(app.aclRules[0].DstPorts[1].Ports.First, check.Equals, uint16(443)) + c.Assert(app.aclRules[0].DstPorts[1].Ports.Last, check.Equals, uint16(443)) + c.Assert(app.aclRules[0].DstPorts[1].IP, check.Equals, "100.64.0.1") +} + func (s *Suite) TestPortRange(c *check.C) { err := app.LoadACLPolicy("./tests/acls/acl_policy_basic_range.hujson") c.Assert(err, check.IsNil) @@ -82,6 +337,22 @@ func (s *Suite) TestPortWildcard(c *check.C) { c.Assert(rules[0].SrcIPs[0], check.Equals, "*") } +func (s *Suite) TestPortWildcardYAML(c *check.C) { + err := app.LoadACLPolicy("./tests/acls/acl_policy_basic_wildcards.yaml") + c.Assert(err, check.IsNil) + + rules, err := app.generateACLRules() + c.Assert(err, check.IsNil) + c.Assert(rules, check.NotNil) + + c.Assert(rules, check.HasLen, 1) + c.Assert(rules[0].DstPorts, check.HasLen, 1) + c.Assert(rules[0].DstPorts[0].Ports.First, check.Equals, uint16(0)) + c.Assert(rules[0].DstPorts[0].Ports.Last, check.Equals, uint16(65535)) + c.Assert(rules[0].SrcIPs, check.HasLen, 1) + c.Assert(rules[0].SrcIPs[0], check.Equals, "*") +} + func (s *Suite) TestPortNamespace(c *check.C) { namespace, err := app.CreateNamespace("testnamespace") c.Assert(err, check.IsNil) @@ -94,12 +365,11 @@ func (s *Suite) TestPortNamespace(c *check.C) { ips, _ := app.getAvailableIPs() machine := Machine{ ID: 0, - MachineKey: "foo", + MachineKey: "12345", NodeKey: "bar", DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: ips, AuthKeyID: uint(pak.ID), @@ -142,7 +412,6 @@ func (s *Suite) TestPortGroup(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: ips, AuthKeyID: uint(pak.ID), @@ -165,3 +434,795 @@ func (s *Suite) TestPortGroup(c *check.C) { c.Assert(len(ips), check.Equals, 1) c.Assert(rules[0].SrcIPs[0], check.Equals, ips[0].String()) } + +func Test_expandGroup(t *testing.T) { + type args struct { + aclPolicy ACLPolicy + group string + stripEmailDomain bool + } + tests := []struct { + name string + args args + want []string + wantErr bool + }{ + { + name: "simple test", + args: args{ + aclPolicy: ACLPolicy{ + Groups: Groups{ + "group:test": []string{"user1", "user2", "user3"}, + "group:foo": []string{"user2", "user3"}, + }, + }, + group: "group:test", + stripEmailDomain: true, + }, + want: []string{"user1", "user2", "user3"}, + wantErr: false, + }, + { + name: "InexistantGroup", + args: args{ + aclPolicy: ACLPolicy{ + Groups: Groups{ + "group:test": []string{"user1", "user2", "user3"}, + "group:foo": []string{"user2", "user3"}, + }, + }, + group: "group:undefined", + stripEmailDomain: true, + }, + want: []string{}, + wantErr: true, + }, + { + name: "Expand emails in group", + args: args{ + aclPolicy: ACLPolicy{ + Groups: Groups{ + "group:admin": []string{ + "joe.bar@gmail.com", + "john.doe@yahoo.fr", + }, + }, + }, + group: "group:admin", + stripEmailDomain: true, + }, + want: []string{"joe.bar", "john.doe"}, + wantErr: false, + }, + { + name: "Expand emails in group", + args: args{ + aclPolicy: ACLPolicy{ + Groups: Groups{ + "group:admin": []string{ + "joe.bar@gmail.com", + "john.doe@yahoo.fr", + }, + }, + }, + group: "group:admin", + stripEmailDomain: false, + }, + want: []string{"joe.bar.gmail.com", "john.doe.yahoo.fr"}, + wantErr: false, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := expandGroup( + test.args.aclPolicy, + test.args.group, + test.args.stripEmailDomain, + ) + if (err != nil) != test.wantErr { + t.Errorf("expandGroup() error = %v, wantErr %v", err, test.wantErr) + + return + } + if !reflect.DeepEqual(got, test.want) { + t.Errorf("expandGroup() = %v, want %v", got, test.want) + } + }) + } +} + +func Test_expandTagOwners(t *testing.T) { + type args struct { + aclPolicy ACLPolicy + tag string + stripEmailDomain bool + } + tests := []struct { + name string + args args + want []string + wantErr bool + }{ + { + name: "simple tag expansion", + args: args{ + aclPolicy: ACLPolicy{ + TagOwners: TagOwners{"tag:test": []string{"user1"}}, + }, + tag: "tag:test", + stripEmailDomain: true, + }, + want: []string{"user1"}, + wantErr: false, + }, + { + name: "expand with tag and group", + args: args{ + aclPolicy: ACLPolicy{ + Groups: Groups{"group:foo": []string{"user1", "user2"}}, + TagOwners: TagOwners{"tag:test": []string{"group:foo"}}, + }, + tag: "tag:test", + stripEmailDomain: true, + }, + want: []string{"user1", "user2"}, + wantErr: false, + }, + { + name: "expand with namespace and group", + args: args{ + aclPolicy: ACLPolicy{ + Groups: Groups{"group:foo": []string{"user1", "user2"}}, + TagOwners: TagOwners{"tag:test": []string{"group:foo", "user3"}}, + }, + tag: "tag:test", + stripEmailDomain: true, + }, + want: []string{"user1", "user2", "user3"}, + wantErr: false, + }, + { + name: "invalid tag", + args: args{ + aclPolicy: ACLPolicy{ + TagOwners: TagOwners{"tag:foo": []string{"group:foo", "user1"}}, + }, + tag: "tag:test", + stripEmailDomain: true, + }, + want: []string{}, + wantErr: true, + }, + { + name: "invalid group", + args: args{ + aclPolicy: ACLPolicy{ + Groups: Groups{"group:bar": []string{"user1", "user2"}}, + TagOwners: TagOwners{"tag:test": []string{"group:foo", "user2"}}, + }, + tag: "tag:test", + stripEmailDomain: true, + }, + want: []string{}, + wantErr: true, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := expandTagOwners( + test.args.aclPolicy, + test.args.tag, + test.args.stripEmailDomain, + ) + if (err != nil) != test.wantErr { + t.Errorf("expandTagOwners() error = %v, wantErr %v", err, test.wantErr) + + return + } + if !reflect.DeepEqual(got, test.want) { + t.Errorf("expandTagOwners() = %v, want %v", got, test.want) + } + }) + } +} + +func Test_expandPorts(t *testing.T) { + type args struct { + portsStr string + } + tests := []struct { + name string + args args + want *[]tailcfg.PortRange + wantErr bool + }{ + { + name: "wildcard", + args: args{portsStr: "*"}, + want: &[]tailcfg.PortRange{ + {First: portRangeBegin, Last: portRangeEnd}, + }, + wantErr: false, + }, + { + name: "two ports", + args: args{portsStr: "80,443"}, + want: &[]tailcfg.PortRange{ + {First: 80, Last: 80}, + {First: 443, Last: 443}, + }, + wantErr: false, + }, + { + name: "a range and a port", + args: args{portsStr: "80-1024,443"}, + want: &[]tailcfg.PortRange{ + {First: 80, Last: 1024}, + {First: 443, Last: 443}, + }, + wantErr: false, + }, + { + name: "out of bounds", + args: args{portsStr: "854038"}, + want: nil, + wantErr: true, + }, + { + name: "wrong port", + args: args{portsStr: "85a38"}, + want: nil, + wantErr: true, + }, + { + name: "wrong port in first", + args: args{portsStr: "a-80"}, + want: nil, + wantErr: true, + }, + { + name: "wrong port in last", + args: args{portsStr: "80-85a38"}, + want: nil, + wantErr: true, + }, + { + name: "wrong port format", + args: args{portsStr: "80-85a38-3"}, + want: nil, + wantErr: true, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := expandPorts(test.args.portsStr) + if (err != nil) != test.wantErr { + t.Errorf("expandPorts() error = %v, wantErr %v", err, test.wantErr) + + return + } + if !reflect.DeepEqual(got, test.want) { + t.Errorf("expandPorts() = %v, want %v", got, test.want) + } + }) + } +} + +func Test_listMachinesInNamespace(t *testing.T) { + type args struct { + machines []Machine + namespace string + } + tests := []struct { + name string + args args + want []Machine + }{ + { + name: "1 machine in namespace", + args: args{ + machines: []Machine{ + {Namespace: Namespace{Name: "joe"}}, + }, + namespace: "joe", + }, + want: []Machine{ + {Namespace: Namespace{Name: "joe"}}, + }, + }, + { + name: "3 machines, 2 in namespace", + args: args{ + machines: []Machine{ + {ID: 1, Namespace: Namespace{Name: "joe"}}, + {ID: 2, Namespace: Namespace{Name: "marc"}}, + {ID: 3, Namespace: Namespace{Name: "marc"}}, + }, + namespace: "marc", + }, + want: []Machine{ + {ID: 2, Namespace: Namespace{Name: "marc"}}, + {ID: 3, Namespace: Namespace{Name: "marc"}}, + }, + }, + { + name: "5 machines, 0 in namespace", + args: args{ + machines: []Machine{ + {ID: 1, Namespace: Namespace{Name: "joe"}}, + {ID: 2, Namespace: Namespace{Name: "marc"}}, + {ID: 3, Namespace: Namespace{Name: "marc"}}, + {ID: 4, Namespace: Namespace{Name: "marc"}}, + {ID: 5, Namespace: Namespace{Name: "marc"}}, + }, + namespace: "mickael", + }, + want: []Machine{}, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if got := filterMachinesByNamespace(test.args.machines, test.args.namespace); !reflect.DeepEqual( + got, + test.want, + ) { + t.Errorf("listMachinesInNamespace() = %v, want %v", got, test.want) + } + }) + } +} + +// nolint +func Test_expandAlias(t *testing.T) { + type args struct { + machines []Machine + aclPolicy ACLPolicy + alias string + stripEmailDomain bool + } + tests := []struct { + name string + args args + want []string + wantErr bool + }{ + { + name: "wildcard", + args: args{ + alias: "*", + machines: []Machine{ + {IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")}}, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.78.84.227"), + }, + }, + }, + aclPolicy: ACLPolicy{}, + stripEmailDomain: true, + }, + want: []string{"*"}, + wantErr: false, + }, + { + name: "simple group", + args: args{ + alias: "group:accountant", + machines: []Machine{ + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "marc"}, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.4"), + }, + Namespace: Namespace{Name: "mickael"}, + }, + }, + aclPolicy: ACLPolicy{ + Groups: Groups{"group:accountant": []string{"joe", "marc"}}, + }, + stripEmailDomain: true, + }, + want: []string{"100.64.0.1", "100.64.0.2", "100.64.0.3"}, + wantErr: false, + }, + { + name: "wrong group", + args: args{ + alias: "group:hr", + machines: []Machine{ + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "marc"}, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.4"), + }, + Namespace: Namespace{Name: "mickael"}, + }, + }, + aclPolicy: ACLPolicy{ + Groups: Groups{"group:accountant": []string{"joe", "marc"}}, + }, + stripEmailDomain: true, + }, + want: []string{}, + wantErr: true, + }, + { + name: "simple ipaddress", + args: args{ + alias: "10.0.0.3", + machines: []Machine{}, + aclPolicy: ACLPolicy{}, + stripEmailDomain: true, + }, + want: []string{"10.0.0.3"}, + wantErr: false, + }, + { + name: "private network", + args: args{ + alias: "homeNetwork", + machines: []Machine{}, + aclPolicy: ACLPolicy{ + Hosts: Hosts{ + "homeNetwork": netaddr.MustParseIPPrefix("192.168.1.0/24"), + }, + }, + stripEmailDomain: true, + }, + want: []string{"192.168.1.0/24"}, + wantErr: false, + }, + { + name: "simple host", + args: args{ + alias: "10.0.0.1", + machines: []Machine{}, + aclPolicy: ACLPolicy{}, + stripEmailDomain: true, + }, + want: []string{"10.0.0.1"}, + wantErr: false, + }, + { + name: "simple CIDR", + args: args{ + alias: "10.0.0.0/16", + machines: []Machine{}, + aclPolicy: ACLPolicy{}, + stripEmailDomain: true, + }, + want: []string{"10.0.0.0/16"}, + wantErr: false, + }, + { + name: "simple tag", + args: args{ + alias: "tag:hr-webserver", + machines: []Machine{ + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + HostInfo: HostInfo{ + OS: "centos", + Hostname: "foo", + RequestTags: []string{"tag:hr-webserver"}, + }, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "joe"}, + HostInfo: HostInfo{ + OS: "centos", + Hostname: "foo", + RequestTags: []string{"tag:hr-webserver"}, + }, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "marc"}, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.4"), + }, + Namespace: Namespace{Name: "joe"}, + }, + }, + aclPolicy: ACLPolicy{ + TagOwners: TagOwners{"tag:hr-webserver": []string{"joe"}}, + }, + stripEmailDomain: true, + }, + want: []string{"100.64.0.1", "100.64.0.2"}, + wantErr: false, + }, + { + name: "No tag defined", + args: args{ + alias: "tag:hr-webserver", + machines: []Machine{ + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "marc"}, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.4"), + }, + Namespace: Namespace{Name: "mickael"}, + }, + }, + aclPolicy: ACLPolicy{ + Groups: Groups{"group:accountant": []string{"joe", "marc"}}, + TagOwners: TagOwners{ + "tag:accountant-webserver": []string{"group:accountant"}, + }, + }, + stripEmailDomain: true, + }, + want: []string{}, + wantErr: true, + }, + { + name: "list host in namespace without correctly tagged servers", + args: args{ + alias: "joe", + machines: []Machine{ + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + HostInfo: HostInfo{ + OS: "centos", + Hostname: "foo", + RequestTags: []string{"tag:accountant-webserver"}, + }, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "joe"}, + HostInfo: HostInfo{ + OS: "centos", + Hostname: "foo", + RequestTags: []string{"tag:accountant-webserver"}, + }, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "marc"}, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.4"), + }, + Namespace: Namespace{Name: "joe"}, + }, + }, + aclPolicy: ACLPolicy{ + TagOwners: TagOwners{"tag:accountant-webserver": []string{"joe"}}, + }, + stripEmailDomain: true, + }, + want: []string{"100.64.0.4"}, + wantErr: false, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := expandAlias( + test.args.machines, + test.args.aclPolicy, + test.args.alias, + test.args.stripEmailDomain, + ) + if (err != nil) != test.wantErr { + t.Errorf("expandAlias() error = %v, wantErr %v", err, test.wantErr) + + return + } + if !reflect.DeepEqual(got, test.want) { + t.Errorf("expandAlias() = %v, want %v", got, test.want) + } + }) + } +} + +func Test_excludeCorrectlyTaggedNodes(t *testing.T) { + type args struct { + aclPolicy ACLPolicy + nodes []Machine + namespace string + } + tests := []struct { + name string + args args + want []Machine + wantErr bool + }{ + { + name: "exclude nodes with valid tags", + args: args{ + aclPolicy: ACLPolicy{ + TagOwners: TagOwners{"tag:accountant-webserver": []string{"joe"}}, + }, + nodes: []Machine{ + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + HostInfo: HostInfo{ + OS: "centos", + Hostname: "foo", + RequestTags: []string{"tag:accountant-webserver"}, + }, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "joe"}, + HostInfo: HostInfo{ + OS: "centos", + Hostname: "foo", + RequestTags: []string{"tag:accountant-webserver"}, + }, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.4"), + }, + Namespace: Namespace{Name: "joe"}, + }, + }, + namespace: "joe", + }, + want: []Machine{ + { + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.4")}, + Namespace: Namespace{Name: "joe"}, + }, + }, + }, + { + name: "all nodes have invalid tags, don't exclude them", + args: args{ + aclPolicy: ACLPolicy{ + TagOwners: TagOwners{"tag:accountant-webserver": []string{"joe"}}, + }, + nodes: []Machine{ + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + HostInfo: HostInfo{ + OS: "centos", + Hostname: "hr-web1", + RequestTags: []string{"tag:hr-webserver"}, + }, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "joe"}, + HostInfo: HostInfo{ + OS: "centos", + Hostname: "hr-web2", + RequestTags: []string{"tag:hr-webserver"}, + }, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.4"), + }, + Namespace: Namespace{Name: "joe"}, + }, + }, + namespace: "joe", + }, + want: []Machine{ + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + HostInfo: HostInfo{ + OS: "centos", + Hostname: "hr-web1", + RequestTags: []string{"tag:hr-webserver"}, + }, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "joe"}, + HostInfo: HostInfo{ + OS: "centos", + Hostname: "hr-web2", + RequestTags: []string{"tag:hr-webserver"}, + }, + }, + { + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.4"), + }, + Namespace: Namespace{Name: "joe"}, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := excludeCorrectlyTaggedNodes( + test.args.aclPolicy, + test.args.nodes, + test.args.namespace, + ) + if !reflect.DeepEqual(got, test.want) { + t.Errorf("excludeCorrectlyTaggedNodes() = %v, want %v", got, test.want) + } + }) + } +} diff --git a/acls_types.go b/acls_types.go index 08e650ff..fb869821 100644 --- a/acls_types.go +++ b/acls_types.go @@ -5,23 +5,24 @@ import ( "strings" "github.com/tailscale/hujson" + "gopkg.in/yaml.v3" "inet.af/netaddr" ) // ACLPolicy represents a Tailscale ACL Policy. type ACLPolicy struct { - Groups Groups `json:"Groups"` - Hosts Hosts `json:"Hosts"` - TagOwners TagOwners `json:"TagOwners"` - ACLs []ACL `json:"ACLs"` - Tests []ACLTest `json:"Tests"` + Groups Groups `json:"Groups" yaml:"Groups"` + Hosts Hosts `json:"Hosts" yaml:"Hosts"` + TagOwners TagOwners `json:"TagOwners" yaml:"TagOwners"` + ACLs []ACL `json:"ACLs" yaml:"ACLs"` + Tests []ACLTest `json:"Tests" yaml:"Tests"` } // ACL is a basic rule for the ACL Policy. type ACL struct { - Action string `json:"Action"` - Users []string `json:"Users"` - Ports []string `json:"Ports"` + Action string `json:"Action" yaml:"Action"` + Users []string `json:"Users" yaml:"Users"` + Ports []string `json:"Ports" yaml:"Ports"` } // Groups references a series of alias in the ACL rules. @@ -35,9 +36,9 @@ type TagOwners map[string][]string // ACLTest is not implemented, but should be use to check if a certain rule is allowed. type ACLTest struct { - User string `json:"User"` - Allow []string `json:"Allow"` - Deny []string `json:"Deny,omitempty"` + User string `json:"User" yaml:"User"` + Allow []string `json:"Allow" yaml:"Allow"` + Deny []string `json:"Deny,omitempty" yaml:"Deny,omitempty"` } // UnmarshalJSON allows to parse the Hosts directly into netaddr objects. @@ -69,6 +70,27 @@ func (hosts *Hosts) UnmarshalJSON(data []byte) error { return nil } +// UnmarshalYAML allows to parse the Hosts directly into netaddr objects. +func (hosts *Hosts) UnmarshalYAML(data []byte) error { + newHosts := Hosts{} + hostIPPrefixMap := make(map[string]string) + + err := yaml.Unmarshal(data, &hostIPPrefixMap) + if err != nil { + return err + } + for host, prefixStr := range hostIPPrefixMap { + prefix, err := netaddr.ParseIPPrefix(prefixStr) + if err != nil { + return err + } + newHosts[host] = prefix + } + *hosts = newHosts + + return nil +} + // IsZero is perhaps a bit naive here. func (policy ACLPolicy) IsZero() bool { if len(policy.Groups) == 0 && len(policy.Hosts) == 0 && len(policy.ACLs) == 0 { diff --git a/api.go b/api.go index 020ded01..1023e6fc 100644 --- a/api.go +++ b/api.go @@ -22,7 +22,7 @@ import ( const ( reservedResponseHeaderSize = 4 - RegisterMethodAuthKey = "authKey" + RegisterMethodAuthKey = "authkey" RegisterMethodOIDC = "oidc" RegisterMethodCLI = "cli" ErrRegisterMethodCLIDoesNotSupportExpire = Error( @@ -45,22 +45,21 @@ type registerWebAPITemplateConfig struct { } var registerWebAPITemplate = template.Must( - template.New("registerweb").Parse(` + template.New("registerweb").Parse(` + + + Registration - Headscale + -

headscale

-

- Run the command below in the headscale server to add this machine to your network: -

- -

- - headscale -n NAMESPACE nodes register --key {{.Key}} - -

- +

headscale

+

Machine registration

+

+ Run the command below in the headscale server to add this machine to your network: +

+
headscale -n NAMESPACE nodes register --key {{.Key}}
- `), -) + +`)) // RegisterWebAPI shows a simple message in the browser to point to the CLI // Listens in /register. @@ -125,25 +124,63 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) { machine, err := h.GetMachineByMachineKey(machineKey) if errors.Is(err, gorm.ErrRecordNotFound) { log.Info().Str("machine", req.Hostinfo.Hostname).Msg("New machine") - newMachine := Machine{ - Expiry: &time.Time{}, - MachineKey: MachinePublicKeyStripPrefix(machineKey), - Name: req.Hostinfo.Hostname, - } - if err := h.db.Create(&newMachine).Error; err != nil { - log.Error(). - Caller(). - Err(err). - Msg("Could not create row") - machineRegistrations.WithLabelValues("unknown", "web", "error", machine.Namespace.Name). - Inc() + + machineKeyStr := MachinePublicKeyStripPrefix(machineKey) + + // If the machine has AuthKey set, handle registration via PreAuthKeys + if req.Auth.AuthKey != "" { + h.handleAuthKey(ctx, machineKey, req) return } - machine = &newMachine + hname, err := NormalizeToFQDNRules( + req.Hostinfo.Hostname, + h.cfg.OIDC.StripEmaildomain, + ) + if err != nil { + log.Error(). + Caller(). + Str("func", "RegistrationHandler"). + Str("hostinfo.name", req.Hostinfo.Hostname). + Err(err) + + return + } + + // The machine did not have a key to authenticate, which means + // that we rely on a method that calls back some how (OpenID or CLI) + // We create the machine and then keep it around until a callback + // happens + newMachine := Machine{ + MachineKey: machineKeyStr, + Name: hname, + NodeKey: NodePublicKeyStripPrefix(req.NodeKey), + LastSeen: &now, + Expiry: &time.Time{}, + } + + if !req.Expiry.IsZero() { + log.Trace(). + Caller(). + Str("machine", req.Hostinfo.Hostname). + Time("expiry", req.Expiry). + Msg("Non-zero expiry time requested") + newMachine.Expiry = &req.Expiry + } + + h.registrationCache.Set( + machineKeyStr, + newMachine, + registerCacheExpiration, + ) + + h.handleMachineRegistrationNew(ctx, machineKey, req) + + return } - if machine.Registered { + // The machine is already registered, so we need to pass through reauth or key update. + if machine != nil { // If the NodeKey stored in headscale is the same as the key presented in a registration // request, then we have a node that is either: // - Trying to log out (sending a expiry in the past) @@ -180,15 +217,6 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) { return } - - // If the machine has AuthKey set, handle registration via PreAuthKeys - if req.Auth.AuthKey != "" { - h.handleAuthKey(ctx, machineKey, req, *machine) - - return - } - - h.handleMachineRegistrationNew(ctx, machineKey, req, *machine) } func (h *Headscale) getMapResponse( @@ -261,7 +289,16 @@ func (h *Headscale) getMapResponse( var respBody []byte if req.Compress == "zstd" { - src, _ := json.Marshal(resp) + src, err := json.Marshal(resp) + if err != nil { + log.Error(). + Caller(). + Str("func", "getMapResponse"). + Err(err). + Msg("Failed to marshal response for the client") + + return nil, err + } encoder, _ := zstd.NewWriter(nil) srcCompressed := encoder.EncodeAll(src, nil) @@ -290,7 +327,16 @@ func (h *Headscale) getMapKeepAliveResponse( var respBody []byte var err error if mapRequest.Compress == "zstd" { - src, _ := json.Marshal(mapResponse) + src, err := json.Marshal(mapResponse) + if err != nil { + log.Error(). + Caller(). + Str("func", "getMapKeepAliveResponse"). + Err(err). + Msg("Failed to marshal keepalive response for the client") + + return nil, err + } encoder, _ := zstd.NewWriter(nil) srcCompressed := encoder.EncodeAll(src, nil) respBody = h.privateKey.SealTo(machineKey, srcCompressed) @@ -384,7 +430,7 @@ func (h *Headscale) handleMachineExpired( Msg("Machine registration has expired. Sending a authurl to register") if registerRequest.Auth.AuthKey != "" { - h.handleAuthKey(ctx, machineKey, registerRequest, machine) + h.handleAuthKey(ctx, machineKey, registerRequest) return } @@ -447,13 +493,12 @@ func (h *Headscale) handleMachineRegistrationNew( ctx *gin.Context, machineKey key.MachinePublic, registerRequest tailcfg.RegisterRequest, - machine Machine, ) { resp := tailcfg.RegisterResponse{} // The machine registration is new, redirect the client to the registration URL log.Debug(). - Str("machine", machine.Name). + Str("machine", registerRequest.Hostinfo.Hostname). Msg("The node is sending us a new NodeKey, sending auth url") if h.cfg.OIDC.Issuer != "" { resp.AuthURL = fmt.Sprintf( @@ -466,24 +511,6 @@ func (h *Headscale) handleMachineRegistrationNew( strings.TrimSuffix(h.cfg.ServerURL, "/"), MachinePublicKeyStripPrefix(machineKey)) } - if !registerRequest.Expiry.IsZero() { - log.Trace(). - Caller(). - Str("machine", machine.Name). - Time("expiry", registerRequest.Expiry). - Msg("Non-zero expiry time requested, adding to cache") - h.requestedExpiryCache.Set( - machineKey.String(), - registerRequest.Expiry, - requestedExpiryCacheExpiration, - ) - } - - machine.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey) - - // save the NodeKey - h.db.Save(&machine) - respBody, err := encode(resp, &machineKey, h.privateKey) if err != nil { log.Error(). @@ -502,19 +529,21 @@ func (h *Headscale) handleAuthKey( ctx *gin.Context, machineKey key.MachinePublic, registerRequest tailcfg.RegisterRequest, - machine Machine, ) { + machineKeyStr := MachinePublicKeyStripPrefix(machineKey) + log.Debug(). Str("func", "handleAuthKey"). Str("machine", registerRequest.Hostinfo.Hostname). Msgf("Processing auth key for %s", registerRequest.Hostinfo.Hostname) resp := tailcfg.RegisterResponse{} + pak, err := h.checkKeyValidity(registerRequest.Auth.AuthKey) if err != nil { log.Error(). Caller(). Str("func", "handleAuthKey"). - Str("machine", machine.Name). + Str("machine", registerRequest.Hostinfo.Hostname). Err(err). Msg("Failed authentication via AuthKey") resp.MachineAuthorized = false @@ -523,71 +552,66 @@ func (h *Headscale) handleAuthKey( log.Error(). Caller(). Str("func", "handleAuthKey"). - Str("machine", machine.Name). + Str("machine", registerRequest.Hostinfo.Hostname). Err(err). Msg("Cannot encode message") ctx.String(http.StatusInternalServerError, "") - machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name). + machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). Inc() return } + ctx.Data(http.StatusUnauthorized, "application/json; charset=utf-8", respBody) log.Error(). Caller(). Str("func", "handleAuthKey"). - Str("machine", machine.Name). + Str("machine", registerRequest.Hostinfo.Hostname). Msg("Failed authentication via AuthKey") - machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name). + machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). Inc() return } - if machine.isRegistered() { - log.Trace(). - Caller(). - Str("machine", machine.Name). - Msg("machine already registered, reauthenticating") + log.Debug(). + Str("func", "handleAuthKey"). + Str("machine", registerRequest.Hostinfo.Hostname). + Msg("Authentication key was valid, proceeding to acquire IP addresses") - h.RefreshMachine(&machine, registerRequest.Expiry) - } else { - log.Debug(). - Str("func", "handleAuthKey"). - Str("machine", machine.Name). - Msg("Authentication key was valid, proceeding to acquire IP addresses") - ips, err := h.getAvailableIPs() - if err != nil { - log.Error(). - Caller(). - Str("func", "handleAuthKey"). - Str("machine", machine.Name). - Msg("Failed to find an available IP address") - machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name). - Inc() + nodeKey := NodePublicKeyStripPrefix(registerRequest.NodeKey) + now := time.Now().UTC() - return - } - log.Info(). - Str("func", "handleAuthKey"). - Str("machine", machine.Name). - Str("ips", strings.Join(ips.ToStringSlice(), ",")). - Msgf("Assigning %s to %s", strings.Join(ips.ToStringSlice(), ","), machine.Name) - - machine.Expiry = ®isterRequest.Expiry - machine.AuthKeyID = uint(pak.ID) - machine.IPAddresses = ips - machine.NamespaceID = pak.NamespaceID - - machine.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey) - // we update it just in case - machine.Registered = true - machine.RegisterMethod = RegisterMethodAuthKey - h.db.Save(&machine) + machineToRegister := Machine{ + Name: registerRequest.Hostinfo.Hostname, + NamespaceID: pak.Namespace.ID, + MachineKey: machineKeyStr, + RegisterMethod: RegisterMethodAuthKey, + Expiry: ®isterRequest.Expiry, + NodeKey: nodeKey, + LastSeen: &now, + AuthKeyID: uint(pak.ID), } - pak.Used = true - h.db.Save(&pak) + machine, err := h.RegisterMachine( + machineToRegister, + ) + if err != nil { + log.Error(). + Caller(). + Err(err). + Msg("could not register machine") + machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). + Inc() + ctx.String( + http.StatusInternalServerError, + "could not register machine", + ) + + return + } + + h.UsePreAuthKey(pak) resp.MachineAuthorized = true resp.User = *pak.Namespace.toUser() @@ -596,21 +620,21 @@ func (h *Headscale) handleAuthKey( log.Error(). Caller(). Str("func", "handleAuthKey"). - Str("machine", machine.Name). + Str("machine", registerRequest.Hostinfo.Hostname). Err(err). Msg("Cannot encode message") - machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name). + machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). Inc() ctx.String(http.StatusInternalServerError, "Extremely sad!") return } - machineRegistrations.WithLabelValues("new", "authkey", "success", machine.Namespace.Name). + machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "success", pak.Namespace.Name). Inc() ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody) log.Info(). Str("func", "handleAuthKey"). - Str("machine", machine.Name). + Str("machine", registerRequest.Hostinfo.Hostname). Str("ips", strings.Join(machine.IPAddresses.ToStringSlice(), ", ")). Msg("Successfully authenticated via AuthKey") } diff --git a/app.go b/app.go index ac350ecf..f1426bbb 100644 --- a/app.go +++ b/app.go @@ -55,19 +55,24 @@ const ( HTTPReadTimeout = 30 * time.Second privateKeyFileMode = 0o600 - requestedExpiryCacheExpiration = time.Minute * 5 - requestedExpiryCacheCleanupInterval = time.Minute * 10 + registerCacheExpiration = time.Minute * 15 + registerCacheCleanup = time.Minute * 20 errUnsupportedDatabase = Error("unsupported DB") errUnsupportedLetsEncryptChallengeType = Error( "unknown value for Lets Encrypt challenge type", ) + + DisabledClientAuth = "disabled" + RelaxedClientAuth = "relaxed" + EnforcedClientAuth = "enforced" ) // Config contains the initial Headscale configuration. type Config struct { ServerURL string Addr string + MetricsAddr string GRPCAddr string GRPCAllowInsecure bool EphemeralNodeInactivityTimeout time.Duration @@ -90,8 +95,9 @@ type Config struct { TLSLetsEncryptCacheDir string TLSLetsEncryptChallengeType string - TLSCertPath string - TLSKeyPath string + TLSCertPath string + TLSKeyPath string + TLSClientAuthMode tls.ClientAuthType ACMEURL string ACMEEmail string @@ -107,17 +113,23 @@ type Config struct { } type OIDCConfig struct { - Issuer string - ClientID string - ClientSecret string - MatchMap map[string]string + Issuer string + ClientID string + ClientSecret string + StripEmaildomain bool } type DERPConfig struct { - URLs []url.URL - Paths []string - AutoUpdate bool - UpdateFrequency time.Duration + ServerEnabled bool + ServerRegionID int + ServerRegionCode string + ServerRegionName string + STUNEnabled bool + STUNAddr string + URLs []url.URL + Paths []string + AutoUpdate bool + UpdateFrequency time.Duration } type CLIConfig struct { @@ -136,21 +148,43 @@ type Headscale struct { dbDebug bool privateKey *key.MachinePrivate - DERPMap *tailcfg.DERPMap + DERPMap *tailcfg.DERPMap + DERPServer *DERPServer aclPolicy *ACLPolicy aclRules []tailcfg.FilterRule lastStateChange sync.Map - oidcProvider *oidc.Provider - oauth2Config *oauth2.Config - oidcStateCache *cache.Cache + oidcProvider *oidc.Provider + oauth2Config *oauth2.Config - requestedExpiryCache *cache.Cache + registrationCache *cache.Cache + + ipAllocationMutex sync.Mutex +} + +// Look up the TLS constant relative to user-supplied TLS client +// authentication mode. If an unknown mode is supplied, the default +// value, tls.RequireAnyClientCert, is returned. The returned boolean +// indicates if the supplied mode was valid. +func LookupTLSClientAuthMode(mode string) (tls.ClientAuthType, bool) { + switch mode { + case DisabledClientAuth: + // Client cert is _not_ required. + return tls.NoClientCert, true + case RelaxedClientAuth: + // Client cert required, but _not verified_. + return tls.RequireAnyClientCert, true + case EnforcedClientAuth: + // Client cert is _required and verified_. + return tls.RequireAndVerifyClientCert, true + default: + // Return the default when an unknown value is supplied. + return tls.RequireAnyClientCert, false + } } -// NewHeadscale returns the Headscale app. func NewHeadscale(cfg Config) (*Headscale, error) { privKey, err := readOrCreatePrivateKey(cfg.PrivateKeyPath) if err != nil { @@ -174,18 +208,18 @@ func NewHeadscale(cfg Config) (*Headscale, error) { return nil, errUnsupportedDatabase } - requestedExpiryCache := cache.New( - requestedExpiryCacheExpiration, - requestedExpiryCacheCleanupInterval, + registrationCache := cache.New( + registerCacheExpiration, + registerCacheCleanup, ) app := Headscale{ - cfg: cfg, - dbType: cfg.DBtype, - dbString: dbString, - privateKey: privKey, - aclRules: tailcfg.FilterAllowAll, // default allowall - requestedExpiryCache: requestedExpiryCache, + cfg: cfg, + dbType: cfg.DBtype, + dbString: dbString, + privateKey: privKey, + aclRules: tailcfg.FilterAllowAll, // default allowall + registrationCache: registrationCache, } err = app.initDB() @@ -211,6 +245,14 @@ func NewHeadscale(cfg Config) (*Headscale, error) { } } + if cfg.DERP.ServerEnabled { + embeddedDERPServer, err := app.NewDERPServer() + if err != nil { + return nil, err + } + app.DERPServer = embeddedDERPServer + } + return &app, nil } @@ -406,11 +448,17 @@ func (h *Headscale) ensureUnixSocketIsAbsent() error { return os.Remove(h.cfg.UnixSocket) } -func (h *Headscale) createRouter(grpcMux *runtime.ServeMux) *gin.Engine { - router := gin.Default() +func (h *Headscale) createPrometheusRouter() *gin.Engine { + promRouter := gin.Default() prometheus := ginprometheus.NewPrometheus("gin") - prometheus.Use(router) + prometheus.Use(promRouter) + + return promRouter +} + +func (h *Headscale) createRouter(grpcMux *runtime.ServeMux) *gin.Engine { + router := gin.Default() router.GET( "/health", @@ -422,11 +470,19 @@ func (h *Headscale) createRouter(grpcMux *runtime.ServeMux) *gin.Engine { router.POST("/machine/:id", h.RegistrationHandler) router.GET("/oidc/register/:mkey", h.RegisterOIDC) router.GET("/oidc/callback", h.OIDCCallback) - router.GET("/apple", h.AppleMobileConfig) + router.GET("/apple", h.AppleConfigMessage) router.GET("/apple/:platform", h.ApplePlatformConfig) + router.GET("/windows", h.WindowsConfigMessage) + router.GET("/windows/tailscale.reg", h.WindowsRegConfig) router.GET("/swagger", SwaggerUI) router.GET("/swagger/v1/openapiv2.json", SwaggerAPIv1) + if h.cfg.DERP.ServerEnabled { + router.Any("/derp", h.DERPHandler) + router.Any("/derp/probe", h.DERPProbeHandler) + router.Any("/bootstrap-dns", h.DERPBootstrapDNSHandler) + } + api := router.Group("/api") api.Use(h.httpAuthenticationMiddleware) { @@ -445,6 +501,13 @@ func (h *Headscale) Serve() error { // Fetch an initial DERP Map before we start serving h.DERPMap = GetDERPMap(h.cfg.DERP) + if h.cfg.DERP.ServerEnabled { + h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region + if h.cfg.DERP.STUNEnabled { + go h.ServeSTUN() + } + } + if h.cfg.DERP.AutoUpdate { derpMapCancelChannel := make(chan struct{}) defer func() { derpMapCancelChannel <- struct{}{} }() @@ -622,6 +685,27 @@ func (h *Headscale) Serve() error { log.Info(). Msgf("listening and serving HTTP on: %s", h.cfg.Addr) + promRouter := h.createPrometheusRouter() + + promHTTPServer := &http.Server{ + Addr: h.cfg.MetricsAddr, + Handler: promRouter, + ReadTimeout: HTTPReadTimeout, + WriteTimeout: 0, + } + + var promHTTPListener net.Listener + promHTTPListener, err = net.Listen("tcp", h.cfg.MetricsAddr) + + if err != nil { + return fmt.Errorf("failed to bind to TCP address: %w", err) + } + + errorGroup.Go(func() error { return promHTTPServer.Serve(promHTTPListener) }) + + log.Info(). + Msgf("listening and serving metrics on: %s", h.cfg.MetricsAddr) + return errorGroup.Wait() } @@ -676,12 +760,18 @@ func (h *Headscale) getTLSSettings() (*tls.Config, error) { if !strings.HasPrefix(h.cfg.ServerURL, "https://") { log.Warn().Msg("Listening with TLS but ServerURL does not start with https://") } + + log.Info().Msg(fmt.Sprintf( + "Client authentication (mTLS) is \"%s\". See the docs to learn about configuring this setting.", + h.cfg.TLSClientAuthMode)) + tlsConfig := &tls.Config{ - ClientAuth: tls.RequireAnyClientCert, + ClientAuth: h.cfg.TLSClientAuthMode, NextProtos: []string{"http/1.1"}, Certificates: make([]tls.Certificate, 1), MinVersion: tls.VersionTLS12, } + tlsConfig.Certificates[0], err = tls.LoadX509KeyPair(h.cfg.TLSCertPath, h.cfg.TLSKeyPath) return tlsConfig, err diff --git a/app_test.go b/app_test.go index 02fdce8b..96036a1d 100644 --- a/app_test.go +++ b/app_test.go @@ -5,7 +5,6 @@ import ( "os" "testing" - "github.com/patrickmn/go-cache" "gopkg.in/check.v1" "inet.af/netaddr" ) @@ -50,10 +49,6 @@ func (s *Suite) ResetDB(c *check.C) { cfg: cfg, dbType: "sqlite3", dbString: tmpDir + "/headscale_test.db", - requestedExpiryCache: cache.New( - requestedExpiryCacheExpiration, - requestedExpiryCacheCleanupInterval, - ), } err = app.initDB() if err != nil { @@ -65,3 +60,20 @@ func (s *Suite) ResetDB(c *check.C) { } app.db = db } + +// Enusre an error is returned when an invalid auth mode +// is supplied. +func (s *Suite) TestInvalidClientAuthMode(c *check.C) { + _, isValid := LookupTLSClientAuthMode("invalid") + c.Assert(isValid, check.Equals, false) +} + +// Ensure that all client auth modes return a nil error. +func (s *Suite) TestAuthModes(c *check.C) { + modes := []string{"disabled", "relaxed", "enforced"} + + for _, v := range modes { + _, isValid := LookupTLSClientAuthMode(v) + c.Assert(isValid, check.Equals, true) + } +} diff --git a/cli_test.go b/cli_test.go deleted file mode 100644 index 71f2bea5..00000000 --- a/cli_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package headscale - -import ( - "time" - - "gopkg.in/check.v1" - "inet.af/netaddr" -) - -func (s *Suite) TestRegisterMachine(c *check.C) { - namespace, err := app.CreateNamespace("test") - c.Assert(err, check.IsNil) - - now := time.Now().UTC() - - machine := Machine{ - ID: 0, - MachineKey: "8ce002a935f8c394e55e78fbbb410576575ff8ec5cfa2e627e4b807f1be15b0e", - NodeKey: "bar", - DiscoKey: "faa", - Name: "testmachine", - NamespaceID: namespace.ID, - IPAddresses: []netaddr.IP{netaddr.MustParseIP("10.0.0.1")}, - Expiry: &now, - } - err = app.db.Save(&machine).Error - c.Assert(err, check.IsNil) - - _, err = app.GetMachine(namespace.Name, machine.Name) - c.Assert(err, check.IsNil) - - machineAfterRegistering, err := app.RegisterMachine( - machine.MachineKey, - namespace.Name, - ) - c.Assert(err, check.IsNil) - c.Assert(machineAfterRegistering.Registered, check.Equals, true) - - _, err = machineAfterRegistering.GetHostInfo() - c.Assert(err, check.IsNil) -} diff --git a/cmd/headscale/cli/api_key.go b/cmd/headscale/cli/api_key.go index 975149ff..06099aa1 100644 --- a/cmd/headscale/cli/api_key.go +++ b/cmd/headscale/cli/api_key.go @@ -36,13 +36,15 @@ func init() { } var apiKeysCmd = &cobra.Command{ - Use: "apikeys", - Short: "Handle the Api keys in Headscale", + Use: "apikeys", + Short: "Handle the Api keys in Headscale", + Aliases: []string{"apikey", "api"}, } var listAPIKeys = &cobra.Command{ - Use: "list", - Short: "List the Api keys for headscale", + Use: "list", + Short: "List the Api keys for headscale", + Aliases: []string{"ls", "show"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") @@ -107,6 +109,7 @@ var createAPIKeyCmd = &cobra.Command{ Creates a new Api key, the Api key is only visible on creation and cannot be retrieved again. If you loose a key, create a new one and revoke (expire) the old one.`, + Aliases: []string{"c", "new"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") @@ -144,7 +147,7 @@ If you loose a key, create a new one and revoke (expire) the old one.`, var expireAPIKeyCmd = &cobra.Command{ Use: "expire", Short: "Expire an ApiKey", - Aliases: []string{"revoke"}, + Aliases: []string{"revoke", "exp", "e"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") diff --git a/cmd/headscale/cli/generate.go b/cmd/headscale/cli/generate.go index 24844146..35906411 100644 --- a/cmd/headscale/cli/generate.go +++ b/cmd/headscale/cli/generate.go @@ -13,8 +13,9 @@ func init() { } var generateCmd = &cobra.Command{ - Use: "generate", - Short: "Generate commands", + Use: "generate", + Short: "Generate commands", + Aliases: []string{"gen"}, } var generatePrivateKeyCmd = &cobra.Command{ diff --git a/cmd/headscale/cli/namespaces.go b/cmd/headscale/cli/namespaces.go index 361e9be3..729e213e 100644 --- a/cmd/headscale/cli/namespaces.go +++ b/cmd/headscale/cli/namespaces.go @@ -25,13 +25,15 @@ const ( ) var namespaceCmd = &cobra.Command{ - Use: "namespaces", - Short: "Manage the namespaces of Headscale", + Use: "namespaces", + Short: "Manage the namespaces of Headscale", + Aliases: []string{"namespace", "ns", "user", "users"}, } var createNamespaceCmd = &cobra.Command{ - Use: "create NAME", - Short: "Creates a new namespace", + Use: "create NAME", + Short: "Creates a new namespace", + Aliases: []string{"c", "new"}, Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return errMissingParameter @@ -72,8 +74,9 @@ var createNamespaceCmd = &cobra.Command{ } var destroyNamespaceCmd = &cobra.Command{ - Use: "destroy NAME", - Short: "Destroys a namespace", + Use: "destroy NAME", + Short: "Destroys a namespace", + Aliases: []string{"delete"}, Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return errMissingParameter @@ -144,8 +147,9 @@ var destroyNamespaceCmd = &cobra.Command{ } var listNamespacesCmd = &cobra.Command{ - Use: "list", - Short: "List all the namespaces", + Use: "list", + Short: "List all the namespaces", + Aliases: []string{"ls", "show"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") @@ -197,8 +201,9 @@ var listNamespacesCmd = &cobra.Command{ } var renameNamespaceCmd = &cobra.Command{ - Use: "rename OLD_NAME NEW_NAME", - Short: "Renames a namespace", + Use: "rename OLD_NAME NEW_NAME", + Short: "Renames a namespace", + Aliases: []string{"mv"}, Args: func(cmd *cobra.Command, args []string) error { expectedArguments := 2 if len(args) < expectedArguments { diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go index d6b86ee1..0abe87b7 100644 --- a/cmd/headscale/cli/nodes.go +++ b/cmd/headscale/cli/nodes.go @@ -46,35 +46,12 @@ func init() { log.Fatalf(err.Error()) } nodeCmd.AddCommand(deleteNodeCmd) - - shareMachineCmd.Flags().StringP("namespace", "n", "", "Namespace") - err = shareMachineCmd.MarkFlagRequired("namespace") - if err != nil { - log.Fatalf(err.Error()) - } - shareMachineCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)") - err = shareMachineCmd.MarkFlagRequired("identifier") - if err != nil { - log.Fatalf(err.Error()) - } - nodeCmd.AddCommand(shareMachineCmd) - - unshareMachineCmd.Flags().StringP("namespace", "n", "", "Namespace") - err = unshareMachineCmd.MarkFlagRequired("namespace") - if err != nil { - log.Fatalf(err.Error()) - } - unshareMachineCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)") - err = unshareMachineCmd.MarkFlagRequired("identifier") - if err != nil { - log.Fatalf(err.Error()) - } - nodeCmd.AddCommand(unshareMachineCmd) } var nodeCmd = &cobra.Command{ - Use: "nodes", - Short: "Manage the nodes of Headscale", + Use: "nodes", + Short: "Manage the nodes of Headscale", + Aliases: []string{"node", "machine", "machines"}, } var registerNodeCmd = &cobra.Command{ @@ -128,8 +105,9 @@ var registerNodeCmd = &cobra.Command{ } var listNodesCmd = &cobra.Command{ - Use: "list", - Short: "List nodes", + Use: "list", + Short: "List nodes", + Aliases: []string{"ls", "show"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") namespace, err := cmd.Flags().GetString("namespace") @@ -188,7 +166,7 @@ var expireNodeCmd = &cobra.Command{ Use: "expire", Short: "Expire (log out) a machine in your network", Long: "Expiring a node will keep the node in the database and force it to reauthenticate.", - Aliases: []string{"logout"}, + Aliases: []string{"logout", "exp", "e"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") @@ -230,8 +208,9 @@ var expireNodeCmd = &cobra.Command{ } var deleteNodeCmd = &cobra.Command{ - Use: "delete", - Short: "Delete a node", + Use: "delete", + Short: "Delete a node", + Aliases: []string{"del"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") @@ -317,139 +296,6 @@ var deleteNodeCmd = &cobra.Command{ }, } -func sharingWorker( - cmd *cobra.Command, -) (string, *v1.Machine, *v1.Namespace, error) { - output, _ := cmd.Flags().GetString("output") - namespaceStr, err := cmd.Flags().GetString("namespace") - if err != nil { - ErrorOutput(err, fmt.Sprintf("Error getting namespace: %s", err), output) - - return "", nil, nil, err - } - - ctx, client, conn, cancel := getHeadscaleCLIClient() - defer cancel() - defer conn.Close() - - identifier, err := cmd.Flags().GetUint64("identifier") - if err != nil { - ErrorOutput(err, fmt.Sprintf("Error converting ID to integer: %s", err), output) - - return "", nil, nil, err - } - - machineRequest := &v1.GetMachineRequest{ - MachineId: identifier, - } - - machineResponse, err := client.GetMachine(ctx, machineRequest) - if err != nil { - ErrorOutput( - err, - fmt.Sprintf("Error getting node node: %s", status.Convert(err).Message()), - output, - ) - - return "", nil, nil, err - } - - namespaceRequest := &v1.GetNamespaceRequest{ - Name: namespaceStr, - } - - namespaceResponse, err := client.GetNamespace(ctx, namespaceRequest) - if err != nil { - ErrorOutput( - err, - fmt.Sprintf("Error getting node node: %s", status.Convert(err).Message()), - output, - ) - - return "", nil, nil, err - } - - return output, machineResponse.GetMachine(), namespaceResponse.GetNamespace(), nil -} - -var shareMachineCmd = &cobra.Command{ - Use: "share", - Short: "Shares a node from the current namespace to the specified one", - Run: func(cmd *cobra.Command, args []string) { - output, machine, namespace, err := sharingWorker(cmd) - if err != nil { - ErrorOutput( - err, - fmt.Sprintf("Failed to fetch namespace or machine: %s", err), - output, - ) - - return - } - - ctx, client, conn, cancel := getHeadscaleCLIClient() - defer cancel() - defer conn.Close() - - request := &v1.ShareMachineRequest{ - MachineId: machine.Id, - Namespace: namespace.Name, - } - - response, err := client.ShareMachine(ctx, request) - if err != nil { - ErrorOutput( - err, - fmt.Sprintf("Error sharing node: %s", status.Convert(err).Message()), - output, - ) - - return - } - - SuccessOutput(response.Machine, "Node shared", output) - }, -} - -var unshareMachineCmd = &cobra.Command{ - Use: "unshare", - Short: "Unshares a node from the specified namespace", - Run: func(cmd *cobra.Command, args []string) { - output, machine, namespace, err := sharingWorker(cmd) - if err != nil { - ErrorOutput( - err, - fmt.Sprintf("Failed to fetch namespace or machine: %s", err), - output, - ) - - return - } - - ctx, client, conn, cancel := getHeadscaleCLIClient() - defer cancel() - defer conn.Close() - - request := &v1.UnshareMachineRequest{ - MachineId: machine.Id, - Namespace: namespace.Name, - } - - response, err := client.UnshareMachine(ctx, request) - if err != nil { - ErrorOutput( - err, - fmt.Sprintf("Error unsharing node: %s", status.Convert(err).Message()), - output, - ) - - return - } - - SuccessOutput(response.Machine, "Node unshared", output) - }, -} - func nodesToPtables( currentNamespace string, machines []*v1.Machine, diff --git a/cmd/headscale/cli/preauthkeys.go b/cmd/headscale/cli/preauthkeys.go index 580184f1..950cbcc5 100644 --- a/cmd/headscale/cli/preauthkeys.go +++ b/cmd/headscale/cli/preauthkeys.go @@ -35,13 +35,15 @@ func init() { } var preauthkeysCmd = &cobra.Command{ - Use: "preauthkeys", - Short: "Handle the preauthkeys in Headscale", + Use: "preauthkeys", + Short: "Handle the preauthkeys in Headscale", + Aliases: []string{"preauthkey", "authkey", "pre"}, } var listPreAuthKeys = &cobra.Command{ - Use: "list", - Short: "List the preauthkeys for this namespace", + Use: "list", + Short: "List the preauthkeys for this namespace", + Aliases: []string{"ls", "show"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") @@ -118,8 +120,9 @@ var listPreAuthKeys = &cobra.Command{ } var createPreAuthKeyCmd = &cobra.Command{ - Use: "create", - Short: "Creates a new preauthkey in the specified namespace", + Use: "create", + Short: "Creates a new preauthkey in the specified namespace", + Aliases: []string{"c", "new"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") @@ -172,8 +175,9 @@ var createPreAuthKeyCmd = &cobra.Command{ } var expirePreAuthKeyCmd = &cobra.Command{ - Use: "expire KEY", - Short: "Expire a preauthkey", + Use: "expire KEY", + Short: "Expire a preauthkey", + Aliases: []string{"revoke", "exp", "e"}, Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return errMissingParameter diff --git a/cmd/headscale/cli/routes.go b/cmd/headscale/cli/routes.go index ced1a0bf..dc060fba 100644 --- a/cmd/headscale/cli/routes.go +++ b/cmd/headscale/cli/routes.go @@ -35,13 +35,15 @@ func init() { } var routesCmd = &cobra.Command{ - Use: "routes", - Short: "Manage the routes of Headscale", + Use: "routes", + Short: "Manage the routes of Headscale", + Aliases: []string{"r", "route"}, } var listRoutesCmd = &cobra.Command{ - Use: "list", - Short: "List routes advertised and enabled by a given node", + Use: "list", + Short: "List routes advertised and enabled by a given node", + Aliases: []string{"ls", "show"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") diff --git a/cmd/headscale/cli/server.go b/cmd/headscale/cli/server.go index 6d9ad194..c19580b9 100644 --- a/cmd/headscale/cli/server.go +++ b/cmd/headscale/cli/server.go @@ -1,8 +1,7 @@ package cli import ( - "log" - + "github.com/rs/zerolog/log" "github.com/spf13/cobra" ) @@ -19,12 +18,12 @@ var serveCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { h, err := getHeadscaleApp() if err != nil { - log.Fatalf("Error initializing: %s", err) + log.Fatal().Caller().Err(err).Msg("Error initializing") } err = h.Serve() if err != nil { - log.Fatalf("Error initializing: %s", err) + log.Fatal().Caller().Err(err).Msg("Error starting server") } }, } diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index 85dcae71..dc7a4e9f 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -10,7 +10,6 @@ import ( "net/url" "os" "path/filepath" - "regexp" "strconv" "strings" "time" @@ -50,6 +49,7 @@ func LoadConfig(path string) error { viper.SetDefault("tls_letsencrypt_cache_dir", "/var/www/.cache") viper.SetDefault("tls_letsencrypt_challenge_type", "HTTP-01") + viper.SetDefault("tls_client_auth_mode", "relaxed") viper.SetDefault("log_level", "info") @@ -64,6 +64,8 @@ func LoadConfig(path string) error { viper.SetDefault("cli.timeout", "5s") viper.SetDefault("cli.insecure", false) + viper.SetDefault("oidc.strip_email_domain", true) + if err := viper.ReadInConfig(); err != nil { return fmt.Errorf("fatal error reading config file: %w", err) } @@ -92,6 +94,20 @@ func LoadConfig(path string) error { !strings.HasPrefix(viper.GetString("server_url"), "https://") { errorText += "Fatal config error: server_url must start with https:// or http://\n" } + + _, authModeValid := headscale.LookupTLSClientAuthMode( + viper.GetString("tls_client_auth_mode"), + ) + + if !authModeValid { + errorText += fmt.Sprintf( + "Invalid tls_client_auth_mode supplied: %s. Accepted values: %s, %s, %s.", + viper.GetString("tls_client_auth_mode"), + headscale.DisabledClientAuth, + headscale.RelaxedClientAuth, + headscale.EnforcedClientAuth) + } + if errorText != "" { //nolint return errors.New(strings.TrimSuffix(errorText, "\n")) @@ -101,6 +117,13 @@ func LoadConfig(path string) error { } func GetDERPConfig() headscale.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") + stunEnabled := viper.GetBool("derp.server.stun.enabled") + stunAddr := viper.GetString("derp.server.stun.listen_addr") + urlStrs := viper.GetStringSlice("derp.urls") urls := make([]url.URL, len(urlStrs)) @@ -122,10 +145,16 @@ func GetDERPConfig() headscale.DERPConfig { updateFrequency := viper.GetDuration("derp.update_frequency") return headscale.DERPConfig{ - URLs: urls, - Paths: paths, - AutoUpdate: autoUpdate, - UpdateFrequency: updateFrequency, + ServerEnabled: serverEnabled, + ServerRegionID: serverRegionID, + ServerRegionCode: serverRegionCode, + ServerRegionName: serverRegionName, + STUNEnabled: stunEnabled, + STUNAddr: stunAddr, + URLs: urls, + Paths: paths, + AutoUpdate: autoUpdate, + UpdateFrequency: updateFrequency, } } @@ -281,9 +310,14 @@ func getHeadscaleConfig() headscale.Config { Msgf("'ip_prefixes' not configured, falling back to default: %v", prefixes) } + tlsClientAuthMode, _ := headscale.LookupTLSClientAuthMode( + viper.GetString("tls_client_auth_mode"), + ) + return headscale.Config{ 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"), @@ -312,8 +346,9 @@ func getHeadscaleConfig() headscale.Config { ), TLSLetsEncryptChallengeType: viper.GetString("tls_letsencrypt_challenge_type"), - TLSCertPath: absPath(viper.GetString("tls_cert_path")), - TLSKeyPath: absPath(viper.GetString("tls_key_path")), + TLSCertPath: absPath(viper.GetString("tls_cert_path")), + TLSKeyPath: absPath(viper.GetString("tls_key_path")), + TLSClientAuthMode: tlsClientAuthMode, DNSConfig: dnsConfig, @@ -324,9 +359,10 @@ func getHeadscaleConfig() headscale.Config { UnixSocketPermission: GetFileMode("unix_socket_permission"), OIDC: headscale.OIDCConfig{ - Issuer: viper.GetString("oidc.issuer"), - ClientID: viper.GetString("oidc.client_id"), - ClientSecret: viper.GetString("oidc.client_secret"), + Issuer: viper.GetString("oidc.issuer"), + ClientID: viper.GetString("oidc.client_id"), + ClientSecret: viper.GetString("oidc.client_secret"), + StripEmaildomain: viper.GetBool("oidc.strip_email_domain"), }, CLI: headscale.CLIConfig{ @@ -356,8 +392,6 @@ func getHeadscaleApp() (*headscale.Headscale, error) { cfg := getHeadscaleConfig() - cfg.OIDC.MatchMap = loadOIDCMatchMap() - app, err := headscale.NewHeadscale(cfg) if err != nil { return nil, err @@ -514,18 +548,6 @@ func (tokenAuth) RequireTransportSecurity() bool { return true } -// loadOIDCMatchMap is a wrapper around viper to verifies that the keys in -// the match map is valid regex strings. -func loadOIDCMatchMap() map[string]string { - strMap := viper.GetStringMapString("oidc.domain_map") - - for oidcMatcher := range strMap { - _ = regexp.MustCompile(oidcMatcher) - } - - return strMap -} - func GetFileMode(key string) fs.FileMode { modeStr := viper.GetString(key) diff --git a/cmd/headscale/headscale_test.go b/cmd/headscale/headscale_test.go index 5ab46e00..c971220b 100644 --- a/cmd/headscale/headscale_test.go +++ b/cmd/headscale/headscale_test.go @@ -55,6 +55,7 @@ func (*Suite) TestConfigLoading(c *check.C) { // Test that config file was interpreted correctly c.Assert(viper.GetString("server_url"), check.Equals, "http://127.0.0.1:8080") c.Assert(viper.GetString("listen_addr"), check.Equals, "0.0.0.0:8080") + c.Assert(viper.GetString("metrics_listen_addr"), check.Equals, "127.0.0.1:9090") c.Assert(viper.GetString("db_type"), check.Equals, "sqlite3") c.Assert(viper.GetString("db_path"), check.Equals, "/var/lib/headscale/db.sqlite") c.Assert(viper.GetString("tls_letsencrypt_hostname"), check.Equals, "") diff --git a/config-example.yaml b/config-example.yaml index ba0c653f..2075e69a 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -16,6 +16,12 @@ server_url: http://127.0.0.1:8080 # listen_addr: 0.0.0.0:8080 +# Address to listen to /metrics, you may want +# to keep this endpoint private to your internal +# network +# +metrics_listen_addr: 127.0.0.1:9090 + # Address to listen for gRPC. # gRPC is used for controlling a headscale server # remotely with the CLI @@ -49,6 +55,26 @@ ip_prefixes: # headscale needs a list of DERP servers that can be presented # to the clients. derp: + server: + # If enabled, runs the embedded DERP server and merges it into the rest of the DERP config + # The Headscale server_url defined above MUST be using https, DERP requires TLS to be in place + enabled: false + + # Region ID to use for the embedded DERP server. + # The local DERP prevails if the region ID collides with other region ID coming from + # the regular DERP config. + region_id: 999 + + # Region code and name are displayed in the Tailscale UI to identify a DERP region + region_code: "headscale" + region_name: "Headscale Embedded DERP" + + # If enabled, also listens in UDP at the configured address for STUN connections to help on NAT traversal + # For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/ + stun: + enabled: false + listen_addr: "0.0.0.0:3478" + # List of externally available DERP maps encoded in JSON urls: - https://controlplane.tailscale.com/derpmap/default @@ -105,12 +131,19 @@ acme_email: "" # Domain name to request a TLS certificate for: tls_letsencrypt_hostname: "" +# Client (Tailscale/Browser) authentication mode (mTLS) +# Acceptable values: +# - disabled: client authentication disabled +# - relaxed: client certificate is required but not verified +# - enforced: client certificate is required and verified +tls_client_auth_mode: relaxed + # Path to store certificates and metadata needed by # letsencrypt tls_letsencrypt_cache_dir: /var/lib/headscale/cache # Type of ACME challenge to use, currently supported types: -# HTTP-01 or TLS_ALPN-01 +# HTTP-01 or TLS-ALPN-01 # See [docs/tls.md](docs/tls.md) for more information tls_letsencrypt_challenge_type: HTTP-01 # When HTTP-01 challenge is chosen, letsencrypt must set up a @@ -125,7 +158,8 @@ tls_key_path: "" log_level: info # Path to a file containg ACL policies. -# Recommended path: /etc/headscale/acl.hujson +# ACLs can be defined as YAML or HUJSON. +# https://tailscale.com/kb/1018/acls/ acl_policy_path: "" ## DNS @@ -180,7 +214,9 @@ unix_socket_permission: "0770" # client_id: "your-oidc-client-id" # client_secret: "your-oidc-client-secret" # -# # Domain map is used to map incomming users (by their email) to -# # a namespace. The key can be a string, or regex. -# domain_map: -# ".*": default-namespace +# If `strip_email_domain` is set to `true`, the domain part of the username email address will be removed. +# This will transform `first-name.last-name@example.com` to the namespace `first-name.last-name` +# If `strip_email_domain` is set to `false` the domain part will NOT be removed resulting to the following +# namespace: `first-name.last-name.example.com` +# +# strip_email_domain: true diff --git a/db.go b/db.go index 1b53dc88..9130d90e 100644 --- a/db.go +++ b/db.go @@ -1,12 +1,19 @@ package headscale import ( + "database/sql/driver" + "encoding/json" "errors" + "fmt" + "time" + "github.com/glebarez/sqlite" + "github.com/rs/zerolog/log" "gorm.io/driver/postgres" - "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" + "inet.af/netaddr" + "tailscale.com/tailcfg" ) const ( @@ -33,6 +40,38 @@ func (h *Headscale) initDB() error { _ = db.Migrator().RenameColumn(&Machine{}, "ip_address", "ip_addresses") + // If the Machine table has a column for registered, + // find all occourences of "false" and drop them. Then + // remove the column. + if db.Migrator().HasColumn(&Machine{}, "registered") { + log.Info(). + Msg(`Database has legacy "registered" column in machine, removing...`) + + machines := Machines{} + if err := h.db.Not("registered").Find(&machines).Error; err != nil { + log.Error().Err(err).Msg("Error accessing db") + } + + for _, machine := range machines { + log.Info(). + Str("machine", machine.Name). + Str("machine_key", machine.MachineKey). + Msg("Deleting unregistered machine") + if err := h.db.Delete(&Machine{}, machine.ID).Error; err != nil { + log.Error(). + Err(err). + Str("machine", machine.Name). + Str("machine_key", machine.MachineKey). + Msg("Error deleting unregistered machine") + } + } + + err := db.Migrator().DropColumn(&Machine{}, "registered") + if err != nil { + log.Error().Err(err).Msg("Error dropping registered column") + } + } + err = db.AutoMigrate(&Machine{}) if err != nil { return err @@ -53,10 +92,7 @@ func (h *Headscale) initDB() error { return err } - err = db.AutoMigrate(&SharedMachine{}) - if err != nil { - return err - } + _ = db.Migrator().DropTable("shared_machines") err = db.AutoMigrate(&APIKey{}) if err != nil { @@ -81,10 +117,24 @@ func (h *Headscale) openDB() (*gorm.DB, error) { switch h.dbType { case Sqlite: - db, err = gorm.Open(sqlite.Open(h.dbString), &gorm.Config{ - DisableForeignKeyConstraintWhenMigrating: true, - Logger: log, - }) + db, err = gorm.Open( + sqlite.Open(h.dbString+"?_synchronous=1&_journal_mode=WAL"), + &gorm.Config{ + DisableForeignKeyConstraintWhenMigrating: true, + Logger: log, + }, + ) + + db.Exec("PRAGMA foreign_keys=ON") + + // The pure Go SQLite library does not handle locking in + // the same way as the C based one and we cant use the gorm + // connection pool as of 2022/02/23. + sqlDB, _ := db.DB() + sqlDB.SetMaxIdleConns(1) + sqlDB.SetMaxOpenConns(1) + sqlDB.SetConnMaxIdleTime(time.Hour) + case Postgres: db, err = gorm.Open(postgres.Open(h.dbString), &gorm.Config{ DisableForeignKeyConstraintWhenMigrating: true, @@ -129,3 +179,74 @@ func (h *Headscale) setValue(key string, value string) error { return nil } + +// This is a "wrapper" type around tailscales +// Hostinfo to allow us to add database "serialization" +// methods. This allows us to use a typed values throughout +// the code and not have to marshal/unmarshal and error +// check all over the code. +type HostInfo tailcfg.Hostinfo + +func (hi *HostInfo) Scan(destination interface{}) error { + switch value := destination.(type) { + case []byte: + return json.Unmarshal(value, hi) + + case string: + return json.Unmarshal([]byte(value), hi) + + default: + return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination) + } +} + +// Value return json value, implement driver.Valuer interface. +func (hi HostInfo) Value() (driver.Value, error) { + bytes, err := json.Marshal(hi) + + return string(bytes), err +} + +type IPPrefixes []netaddr.IPPrefix + +func (i *IPPrefixes) Scan(destination interface{}) error { + switch value := destination.(type) { + case []byte: + return json.Unmarshal(value, i) + + case string: + return json.Unmarshal([]byte(value), i) + + default: + return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination) + } +} + +// Value return json value, implement driver.Valuer interface. +func (i IPPrefixes) Value() (driver.Value, error) { + bytes, err := json.Marshal(i) + + return string(bytes), err +} + +type StringList []string + +func (i *StringList) Scan(destination interface{}) error { + switch value := destination.(type) { + case []byte: + return json.Unmarshal(value, i) + + case string: + return json.Unmarshal([]byte(value), i) + + default: + return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination) + } +} + +// Value return json value, implement driver.Valuer interface. +func (i StringList) Value() (driver.Value, error) { + bytes, err := json.Marshal(i) + + return string(bytes), err +} diff --git a/derp.go b/derp.go index 63e448db..7a9b2367 100644 --- a/derp.go +++ b/derp.go @@ -148,6 +148,7 @@ func (h *Headscale) scheduledDERPMapUpdateWorker(cancelChan <-chan struct{}) { case <-ticker.C: log.Info().Msg("Fetching DERPMap updates") h.DERPMap = GetDERPMap(h.cfg.DERP) + h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region namespaces, err := h.ListNamespaces() if err != nil { diff --git a/derp_server.go b/derp_server.go new file mode 100644 index 00000000..11e3eb14 --- /dev/null +++ b/derp_server.go @@ -0,0 +1,233 @@ +package headscale + +import ( + "context" + "fmt" + "net" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "github.com/gin-gonic/gin" + "github.com/rs/zerolog/log" + "tailscale.com/derp" + "tailscale.com/net/stun" + "tailscale.com/tailcfg" + "tailscale.com/types/key" +) + +// fastStartHeader is the header (with value "1") that signals to the HTTP +// server that the DERP HTTP client does not want the HTTP 101 response +// headers and it will begin writing & reading the DERP protocol immediately +// following its HTTP request. +const fastStartHeader = "Derp-Fast-Start" + +type DERPServer struct { + tailscaleDERP *derp.Server + region tailcfg.DERPRegion +} + +func (h *Headscale) NewDERPServer() (*DERPServer, error) { + server := derp.NewServer(key.NodePrivate(*h.privateKey), log.Info().Msgf) + region, err := h.generateRegionLocalDERP() + if err != nil { + return nil, err + } + + return &DERPServer{server, region}, nil +} + +func (h *Headscale) generateRegionLocalDERP() (tailcfg.DERPRegion, error) { + serverURL, err := url.Parse(h.cfg.ServerURL) + if err != nil { + return tailcfg.DERPRegion{}, err + } + var host string + var port int + host, portStr, err := net.SplitHostPort(serverURL.Host) + if err != nil { + if serverURL.Scheme == "https" { + host = serverURL.Host + port = 443 + } else { + host = serverURL.Host + port = 80 + } + } else { + port, err = strconv.Atoi(portStr) + if err != nil { + return tailcfg.DERPRegion{}, err + } + } + + localDERPregion := tailcfg.DERPRegion{ + RegionID: h.cfg.DERP.ServerRegionID, + RegionCode: h.cfg.DERP.ServerRegionCode, + RegionName: h.cfg.DERP.ServerRegionName, + Avoid: false, + Nodes: []*tailcfg.DERPNode{ + { + Name: fmt.Sprintf("%d", h.cfg.DERP.ServerRegionID), + RegionID: h.cfg.DERP.ServerRegionID, + HostName: host, + DERPPort: port, + }, + }, + } + + if h.cfg.DERP.STUNEnabled { + _, portStr, err := net.SplitHostPort(h.cfg.DERP.STUNAddr) + if err != nil { + return tailcfg.DERPRegion{}, err + } + port, err := strconv.Atoi(portStr) + if err != nil { + return tailcfg.DERPRegion{}, err + } + localDERPregion.Nodes[0].STUNPort = port + } + + return localDERPregion, nil +} + +func (h *Headscale) DERPHandler(ctx *gin.Context) { + log.Trace().Caller().Msgf("/derp request from %v", ctx.ClientIP()) + up := strings.ToLower(ctx.Request.Header.Get("Upgrade")) + if up != "websocket" && up != "derp" { + if up != "" { + log.Warn().Caller().Msgf("Weird websockets connection upgrade: %q", up) + } + ctx.String(http.StatusUpgradeRequired, "DERP requires connection upgrade") + + return + } + + fastStart := ctx.Request.Header.Get(fastStartHeader) == "1" + + hijacker, ok := ctx.Writer.(http.Hijacker) + if !ok { + log.Error().Caller().Msg("DERP requires Hijacker interface from Gin") + ctx.String(http.StatusInternalServerError, "HTTP does not support general TCP support") + + return + } + + netConn, conn, err := hijacker.Hijack() + if err != nil { + log.Error().Caller().Err(err).Msgf("Hijack failed") + ctx.String(http.StatusInternalServerError, "HTTP does not support general TCP support") + + return + } + + if !fastStart { + pubKey := h.privateKey.Public() + pubKeyStr := pubKey.UntypedHexString() // nolint + fmt.Fprintf(conn, "HTTP/1.1 101 Switching Protocols\r\n"+ + "Upgrade: DERP\r\n"+ + "Connection: Upgrade\r\n"+ + "Derp-Version: %v\r\n"+ + "Derp-Public-Key: %s\r\n\r\n", + derp.ProtocolVersion, + pubKeyStr) + } + + h.DERPServer.tailscaleDERP.Accept(netConn, conn, netConn.RemoteAddr().String()) +} + +// DERPProbeHandler is the endpoint that js/wasm clients hit to measure +// DERP latency, since they can't do UDP STUN queries. +func (h *Headscale) DERPProbeHandler(ctx *gin.Context) { + switch ctx.Request.Method { + case "HEAD", "GET": + ctx.Writer.Header().Set("Access-Control-Allow-Origin", "*") + default: + ctx.String(http.StatusMethodNotAllowed, "bogus probe method") + } +} + +// DERPBootstrapDNSHandler implements the /bootsrap-dns endpoint +// Described in https://github.com/tailscale/tailscale/issues/1405, +// this endpoint provides a way to help a client when it fails to start up +// because its DNS are broken. +// The initial implementation is here https://github.com/tailscale/tailscale/pull/1406 +// They have a cache, but not clear if that is really necessary at Headscale, uh, scale. +// An example implementation is found here https://derp.tailscale.com/bootstrap-dns +func (h *Headscale) DERPBootstrapDNSHandler(ctx *gin.Context) { + dnsEntries := make(map[string][]net.IP) + + resolvCtx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + var r net.Resolver + for _, region := range h.DERPMap.Regions { + for _, node := range region.Nodes { // we don't care if we override some nodes + addrs, err := r.LookupIP(resolvCtx, "ip", node.HostName) + if err != nil { + log.Trace().Caller().Err(err).Msgf("bootstrap DNS lookup failed %q", node.HostName) + + continue + } + dnsEntries[node.HostName] = addrs + } + } + ctx.JSON(http.StatusOK, dnsEntries) +} + +// ServeSTUN starts a STUN server on the configured addr. +func (h *Headscale) ServeSTUN() { + packetConn, err := net.ListenPacket("udp", h.cfg.DERP.STUNAddr) + if err != nil { + log.Fatal().Msgf("failed to open STUN listener: %v", err) + } + log.Info().Msgf("STUN server started at %s", packetConn.LocalAddr()) + + udpConn, ok := packetConn.(*net.UDPConn) + if !ok { + log.Fatal().Msg("STUN listener is not a UDP listener") + } + serverSTUNListener(context.Background(), udpConn) +} + +func serverSTUNListener(ctx context.Context, packetConn *net.UDPConn) { + var buf [64 << 10]byte + var ( + bytesRead int + udpAddr *net.UDPAddr + err error + ) + for { + bytesRead, udpAddr, err = packetConn.ReadFromUDP(buf[:]) + if err != nil { + if ctx.Err() != nil { + return + } + log.Error().Caller().Err(err).Msgf("STUN ReadFrom") + time.Sleep(time.Second) + + continue + } + log.Trace().Caller().Msgf("STUN request from %v", udpAddr) + pkt := buf[:bytesRead] + if !stun.Is(pkt) { + log.Trace().Caller().Msgf("UDP packet is not STUN") + + continue + } + txid, err := stun.ParseBindingRequest(pkt) + if err != nil { + log.Trace().Caller().Err(err).Msgf("STUN parse error") + + continue + } + + res := stun.Response(txid, udpAddr.IP, uint16(udpAddr.Port)) + _, err = packetConn.WriteTo(res, udpAddr) + if err != nil { + log.Trace().Caller().Err(err).Msgf("Issue writing to UDP") + + continue + } + } +} diff --git a/dns.go b/dns.go index 8ecd993c..9a91cff0 100644 --- a/dns.go +++ b/dns.go @@ -163,7 +163,11 @@ func getMapResponseDNSConfig( dnsConfig = dnsConfigOrig.Clone() dnsConfig.Domains = append( dnsConfig.Domains, - fmt.Sprintf("%s.%s", machine.Namespace.Name, baseDomain), + fmt.Sprintf( + "%s.%s", + machine.Namespace.Name, + baseDomain, + ), ) namespaceSet := set.New(set.ThreadSafe) @@ -171,8 +175,14 @@ func getMapResponseDNSConfig( for _, p := range peers { namespaceSet.Add(p.Namespace) } - for _, namespace := range namespaceSet.List() { - dnsRoute := fmt.Sprintf("%s.%s", namespace.(Namespace).Name, baseDomain) + for _, ns := range namespaceSet.List() { + namespace, ok := ns.(Namespace) + if !ok { + dnsConfig = dnsConfigOrig + + continue + } + dnsRoute := fmt.Sprintf("%v.%v", namespace.Name, baseDomain) dnsConfig.Routes[dnsRoute] = nil } } else { diff --git a/dns_test.go b/dns_test.go index 80ee83bb..5fd30d3b 100644 --- a/dns_test.go +++ b/dns_test.go @@ -164,7 +164,6 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { Name: "test_get_shared_nodes_1", NamespaceID: namespaceShared1.ID, Namespace: *namespaceShared1, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")}, AuthKeyID: uint(preAuthKeyInShared1.ID), @@ -182,7 +181,6 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { Name: "test_get_shared_nodes_2", NamespaceID: namespaceShared2.ID, Namespace: *namespaceShared2, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")}, AuthKeyID: uint(preAuthKeyInShared2.ID), @@ -200,7 +198,6 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { Name: "test_get_shared_nodes_3", NamespaceID: namespaceShared3.ID, Namespace: *namespaceShared3, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")}, AuthKeyID: uint(preAuthKeyInShared3.ID), @@ -218,16 +215,12 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { Name: "test_get_shared_nodes_4", NamespaceID: namespaceShared1.ID, Namespace: *namespaceShared1, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")}, AuthKeyID: uint(PreAuthKey2InShared1.ID), } app.db.Save(machine2InShared1) - err = app.AddSharedMachineToNamespace(machineInShared2, namespaceShared1) - c.Assert(err, check.IsNil) - baseDomain := "foobar.headscale.net" dnsConfigOrig := tailcfg.DNSConfig{ Routes: make(map[string][]dnstype.Resolver), @@ -245,7 +238,8 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { peersOfMachineInShared1, ) c.Assert(dnsConfig, check.NotNil) - c.Assert(len(dnsConfig.Routes), check.Equals, 2) + + c.Assert(len(dnsConfig.Routes), check.Equals, 3) domainRouteShared1 := fmt.Sprintf("%s.%s", namespaceShared1.Name, baseDomain) _, ok := dnsConfig.Routes[domainRouteShared1] @@ -257,7 +251,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { domainRouteShared3 := fmt.Sprintf("%s.%s", namespaceShared3.Name, baseDomain) _, ok = dnsConfig.Routes[domainRouteShared3] - c.Assert(ok, check.Equals, false) + c.Assert(ok, check.Equals, true) } func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { @@ -313,7 +307,6 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { Name: "test_get_shared_nodes_1", NamespaceID: namespaceShared1.ID, Namespace: *namespaceShared1, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")}, AuthKeyID: uint(preAuthKeyInShared1.ID), @@ -331,7 +324,6 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { Name: "test_get_shared_nodes_2", NamespaceID: namespaceShared2.ID, Namespace: *namespaceShared2, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")}, AuthKeyID: uint(preAuthKeyInShared2.ID), @@ -349,7 +341,6 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { Name: "test_get_shared_nodes_3", NamespaceID: namespaceShared3.ID, Namespace: *namespaceShared3, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")}, AuthKeyID: uint(preAuthKeyInShared3.ID), @@ -367,16 +358,12 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { Name: "test_get_shared_nodes_4", NamespaceID: namespaceShared1.ID, Namespace: *namespaceShared1, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")}, AuthKeyID: uint(preAuthKey2InShared1.ID), } app.db.Save(machine2InShared1) - err = app.AddSharedMachineToNamespace(machineInShared2, namespaceShared1) - c.Assert(err, check.IsNil) - baseDomain := "foobar.headscale.net" dnsConfigOrig := tailcfg.DNSConfig{ Routes: make(map[string][]dnstype.Resolver), diff --git a/docs/README.md b/docs/README.md index 1ac15221..7a3080e3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,7 +10,7 @@ please ask on [Discord](https://discord.gg/XcQxk2VHjx) instead of opening an Iss ### How-to - [Running headscale on Linux](running-headscale-linux.md) -- [Control headscale remotly](remote-cli.md) +- [Control headscale remotely](remote-cli.md) - [Using a Windows client with headscale](windows-client.md) ### References @@ -39,6 +39,14 @@ use namespaces (which are the equivalent to user/logins in Tailscale.com). Please check https://tailscale.com/kb/1018/acls/, and `./tests/acls/` in this repo for working examples. +When using ACL's the Namespace borders are no longer applied. All machines +whichever the Namespace have the ability to communicate with other hosts as +long as the ACL's permits this exchange. + +The [ACLs](acls.md) document should help understand a fictional case of setting +up ACLs in a small company. All concepts presented in this document could be +applied outside of business oriented usage. + ### Apple devices An endpoint with information on how to connect your Apple devices (currently macOS only) is available at `/apple` on your running instance. diff --git a/docs/acls.md b/docs/acls.md new file mode 100644 index 00000000..63e7c6b8 --- /dev/null +++ b/docs/acls.md @@ -0,0 +1,141 @@ +# ACLs use case example + +Let's build an example use case for a small business (It may be the place where +ACL's are the most useful). + +We have a small company with a boss, an admin, two developers and an intern. + +The boss should have access to all servers but not to the users hosts. Admin +should also have access to all hosts except that their permissions should be +limited to maintaining the hosts (for example purposes). The developers can do +anything they want on dev hosts, but only watch on productions hosts. Intern +can only interact with the development servers. + +Each user have at least a device connected to the network and we have some +servers. + +- database.prod +- database.dev +- app-server1.prod +- app-server1.dev +- billing.internal + +## Setup of the network + +Let's create the namespaces. Each user should have his own namespace. The users +here are represented as namespaces. + +```bash +headscale namespaces create boss +headscale namespaces create admin1 +headscale namespaces create dev1 +headscale namespaces create dev2 +headscale namespaces create intern1 +``` + +We don't need to create namespaces for the servers because the servers will be +tagged. When registering the servers we will need to add the flag +`--advertised-tags=tag:,tag:`, and the user (namespace) that is +registering the server should be allowed to do it. Since anyone can add tags to +a server they can register, the check of the tags is done on headscale server +and only valid tags are applied. A tag is valid if the namespace that is +registering it is allowed to do it. + +Here are the ACL's to implement the same permissions as above: + +```json +{ + // groups are collections of users having a common scope. A user can be in multiple groups + // groups cannot be composed of groups + "groups": { + "group:boss": ["boss"], + "group:dev": ["dev1", "dev2"], + "group:admin": ["admin1"], + "group:intern": ["intern1"] + }, + // tagOwners in tailscale is an association between a TAG and the people allowed to set this TAG on a server. + // This is documented [here](https://tailscale.com/kb/1068/acl-tags#defining-a-tag) + // and explained [here](https://tailscale.com/blog/rbac-like-it-was-meant-to-be/) + "tagOwners": { + // the administrators can add servers in production + "tag:prod-databases": ["group:admin"], + "tag:prod-app-servers": ["group:admin"], + + // the boss can tag any server as internal + "tag:internal": ["group:boss"], + + // dev can add servers for dev purposes as well as admins + "tag:dev-databases": ["group:admin", "group:dev"], + "tag:dev-app-servers": ["group:admin", "group:dev"] + + // interns cannot add servers + }, + "acls": [ + // boss have access to all servers + { + "action": "accept", + "users": ["group:boss"], + "ports": [ + "tag:prod-databases:*", + "tag:prod-app-servers:*", + "tag:internal:*", + "tag:dev-databases:*", + "tag:dev-app-servers:*" + ] + }, + + // admin have only access to administrative ports of the servers + { + "action": "accept", + "users": ["group:admin"], + "ports": [ + "tag:prod-databases:22", + "tag:prod-app-servers:22", + "tag:internal:22", + "tag:dev-databases:22", + "tag:dev-app-servers:22" + ] + }, + + // developers have access to databases servers and application servers on all ports + // they can only view the applications servers in prod and have no access to databases servers in production + { + "action": "accept", + "users": ["group:dev"], + "ports": [ + "tag:dev-databases:*", + "tag:dev-app-servers:*", + "tag:prod-app-servers:80,443" + ] + }, + + // servers should be able to talk to database. Database should not be able to initiate connections to + // applications servers + { + "action": "accept", + "users": ["tag:dev-app-servers"], + "ports": ["tag:dev-databases:5432"] + }, + { + "action": "accept", + "users": ["tag:prod-app-servers"], + "ports": ["tag:prod-databases:5432"] + }, + + // interns have access to dev-app-servers only in reading mode + { + "action": "accept", + "users": ["group:intern"], + "ports": ["tag:dev-app-servers:80,443"] + }, + + // We still have to allow internal namespaces communications since nothing guarantees that each user have + // their own namespaces. + { "action": "accept", "users": ["boss"], "ports": ["boss:*"] }, + { "action": "accept", "users": ["dev1"], "ports": ["dev1:*"] }, + { "action": "accept", "users": ["dev2"], "ports": ["dev2:*"] }, + { "action": "accept", "users": ["admin1"], "ports": ["admin1:*"] }, + { "action": "accept", "users": ["intern1"], "ports": ["intern1:*"] } + ] +} +``` diff --git a/docs/examples/kustomize/base/configmap.yaml b/docs/examples/kustomize/base/configmap.yaml index 2e25e5f9..0ac2d563 100644 --- a/docs/examples/kustomize/base/configmap.yaml +++ b/docs/examples/kustomize/base/configmap.yaml @@ -5,4 +5,5 @@ metadata: data: server_url: $(PUBLIC_PROTO)://$(PUBLIC_HOSTNAME) listen_addr: "0.0.0.0:8080" + metrics_listen_addr: "127.0.0.1:9090" ephemeral_node_inactivity_timeout: "30m" diff --git a/docs/examples/kustomize/postgres/deployment.yaml b/docs/examples/kustomize/postgres/deployment.yaml index 75e64446..1dd88b41 100644 --- a/docs/examples/kustomize/postgres/deployment.yaml +++ b/docs/examples/kustomize/postgres/deployment.yaml @@ -25,6 +25,11 @@ spec: configMapKeyRef: name: headscale-config key: listen_addr + - name: METRICS_LISTEN_ADDR + valueFrom: + configMapKeyRef: + name: headscale-config + key: metrics_listen_addr - name: DERP_MAP_PATH value: /vol/config/derp.yaml - name: EPHEMERAL_NODE_INACTIVITY_TIMEOUT diff --git a/docs/examples/kustomize/sqlite/statefulset.yaml b/docs/examples/kustomize/sqlite/statefulset.yaml index 050bf766..2321d39d 100644 --- a/docs/examples/kustomize/sqlite/statefulset.yaml +++ b/docs/examples/kustomize/sqlite/statefulset.yaml @@ -26,6 +26,11 @@ spec: configMapKeyRef: name: headscale-config key: listen_addr + - name: METRICS_LISTEN_ADDR + valueFrom: + configMapKeyRef: + name: headscale-config + key: metrics_listen_addr - name: DERP_MAP_PATH value: /vol/config/derp.yaml - name: EPHEMERAL_NODE_INACTIVITY_TIMEOUT diff --git a/docs/glossary.md b/docs/glossary.md index aa3e2e35..c38060d5 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -1,3 +1,6 @@ # Glossary -- Namespace: Collection of Tailscale nodes that can see each other. In Tailscale.com this is called Tailnet. +| Term | Description | +| --------- | --------------------------------------------------------------------------------------------------------------------- | +| Machine | A machine is a single entity connected to `headscale`, typically an installation of Tailscale. Also known as **Node** | +| Namespace | A namespace is a logical grouping of machines "owned" by the same entity, in Tailscale, this is typically a User | diff --git a/docs/tls.md b/docs/tls.md index 557cdf01..c319359a 100644 --- a/docs/tls.md +++ b/docs/tls.md @@ -29,3 +29,17 @@ headscale can also be configured to expose its web service via TLS. To configure tls_cert_path: "" tls_key_path: "" ``` + +### Configuring Mutual TLS Authentication (mTLS) + +mTLS is a method by which an HTTPS server authenticates clients, e.g. Tailscale, using TLS certificates. This can be configured by applying one of the following values to the `tls_client_auth_mode` setting in the configuration file. + +| Value | Behavior | +| ------------------- | ---------------------------------------------------------- | +| `disabled` | Disable mTLS. | +| `relaxed` (default) | A client certificate is required, but it is not verified. | +| `enforced` | Requires clients to supply a certificate that is verified. | + +```yaml +tls_client_auth_mode: "" +``` diff --git a/gen/go/headscale/v1/apikey.pb.go b/gen/go/headscale/v1/apikey.pb.go index ace8b18c..9e6fddb7 100644 --- a/gen/go/headscale/v1/apikey.pb.go +++ b/gen/go/headscale/v1/apikey.pb.go @@ -7,12 +7,11 @@ package v1 import ( - reflect "reflect" - sync "sync" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" ) const ( diff --git a/gen/go/headscale/v1/device.pb.go b/gen/go/headscale/v1/device.pb.go index 58792512..26607019 100644 --- a/gen/go/headscale/v1/device.pb.go +++ b/gen/go/headscale/v1/device.pb.go @@ -7,12 +7,11 @@ package v1 import ( - reflect "reflect" - sync "sync" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" ) const ( diff --git a/gen/go/headscale/v1/headscale.pb.go b/gen/go/headscale/v1/headscale.pb.go index 7799082d..5c700113 100644 --- a/gen/go/headscale/v1/headscale.pb.go +++ b/gen/go/headscale/v1/headscale.pb.go @@ -7,11 +7,10 @@ package v1 import ( - reflect "reflect" - _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" ) const ( @@ -37,7 +36,7 @@ var file_headscale_v1_headscale_proto_rawDesc = []byte{ 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, - 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xcb, 0x15, 0x0a, 0x10, 0x48, 0x65, + 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xa3, 0x13, 0x0a, 0x10, 0x48, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x77, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, @@ -152,68 +151,50 @@ var file_headscale_v1_headscale_proto_rawDesc = []byte{ 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, - 0x12, 0x8d, 0x01, 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, - 0x65, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x30, - 0x22, 0x2e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, - 0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x73, - 0x68, 0x61, 0x72, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, - 0x12, 0x95, 0x01, 0x0a, 0x0e, 0x55, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, - 0x69, 0x6e, 0x65, 0x12, 0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x4d, - 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x38, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x22, 0x30, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, - 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, - 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2f, 0x7b, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x12, 0x8b, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, - 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x68, - 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, - 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x25, 0x12, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, - 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x97, 0x01, 0x0a, 0x13, 0x45, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x28, - 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, - 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x22, 0x23, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61, - 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, - 0x12, 0x70, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, - 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x22, - 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x3a, - 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x0c, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, - 0x65, 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, - 0x79, 0x2f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0b, 0x4c, - 0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x20, 0x2e, 0x68, 0x65, 0x61, - 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, - 0x69, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x68, - 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, - 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, - 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, - 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x8b, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x65, 0x61, + 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, + 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, + 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x97, + 0x01, 0x0a, 0x13, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x28, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, + 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68, + 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x29, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x25, 0x22, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, + 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, + 0x7d, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x70, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, + 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, + 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x22, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, + 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x0c, 0x45, 0x78, + 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, + 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, + 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, + 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, + 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x2f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, + 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, + 0x79, 0x73, 0x12, 0x20, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, + 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x42, + 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75, + 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var file_headscale_v1_headscale_proto_goTypes = []interface{}{ @@ -231,34 +212,30 @@ var file_headscale_v1_headscale_proto_goTypes = []interface{}{ (*DeleteMachineRequest)(nil), // 11: headscale.v1.DeleteMachineRequest (*ExpireMachineRequest)(nil), // 12: headscale.v1.ExpireMachineRequest (*ListMachinesRequest)(nil), // 13: headscale.v1.ListMachinesRequest - (*ShareMachineRequest)(nil), // 14: headscale.v1.ShareMachineRequest - (*UnshareMachineRequest)(nil), // 15: headscale.v1.UnshareMachineRequest - (*GetMachineRouteRequest)(nil), // 16: headscale.v1.GetMachineRouteRequest - (*EnableMachineRoutesRequest)(nil), // 17: headscale.v1.EnableMachineRoutesRequest - (*CreateApiKeyRequest)(nil), // 18: headscale.v1.CreateApiKeyRequest - (*ExpireApiKeyRequest)(nil), // 19: headscale.v1.ExpireApiKeyRequest - (*ListApiKeysRequest)(nil), // 20: headscale.v1.ListApiKeysRequest - (*GetNamespaceResponse)(nil), // 21: headscale.v1.GetNamespaceResponse - (*CreateNamespaceResponse)(nil), // 22: headscale.v1.CreateNamespaceResponse - (*RenameNamespaceResponse)(nil), // 23: headscale.v1.RenameNamespaceResponse - (*DeleteNamespaceResponse)(nil), // 24: headscale.v1.DeleteNamespaceResponse - (*ListNamespacesResponse)(nil), // 25: headscale.v1.ListNamespacesResponse - (*CreatePreAuthKeyResponse)(nil), // 26: headscale.v1.CreatePreAuthKeyResponse - (*ExpirePreAuthKeyResponse)(nil), // 27: headscale.v1.ExpirePreAuthKeyResponse - (*ListPreAuthKeysResponse)(nil), // 28: headscale.v1.ListPreAuthKeysResponse - (*DebugCreateMachineResponse)(nil), // 29: headscale.v1.DebugCreateMachineResponse - (*GetMachineResponse)(nil), // 30: headscale.v1.GetMachineResponse - (*RegisterMachineResponse)(nil), // 31: headscale.v1.RegisterMachineResponse - (*DeleteMachineResponse)(nil), // 32: headscale.v1.DeleteMachineResponse - (*ExpireMachineResponse)(nil), // 33: headscale.v1.ExpireMachineResponse - (*ListMachinesResponse)(nil), // 34: headscale.v1.ListMachinesResponse - (*ShareMachineResponse)(nil), // 35: headscale.v1.ShareMachineResponse - (*UnshareMachineResponse)(nil), // 36: headscale.v1.UnshareMachineResponse - (*GetMachineRouteResponse)(nil), // 37: headscale.v1.GetMachineRouteResponse - (*EnableMachineRoutesResponse)(nil), // 38: headscale.v1.EnableMachineRoutesResponse - (*CreateApiKeyResponse)(nil), // 39: headscale.v1.CreateApiKeyResponse - (*ExpireApiKeyResponse)(nil), // 40: headscale.v1.ExpireApiKeyResponse - (*ListApiKeysResponse)(nil), // 41: headscale.v1.ListApiKeysResponse + (*GetMachineRouteRequest)(nil), // 14: headscale.v1.GetMachineRouteRequest + (*EnableMachineRoutesRequest)(nil), // 15: headscale.v1.EnableMachineRoutesRequest + (*CreateApiKeyRequest)(nil), // 16: headscale.v1.CreateApiKeyRequest + (*ExpireApiKeyRequest)(nil), // 17: headscale.v1.ExpireApiKeyRequest + (*ListApiKeysRequest)(nil), // 18: headscale.v1.ListApiKeysRequest + (*GetNamespaceResponse)(nil), // 19: headscale.v1.GetNamespaceResponse + (*CreateNamespaceResponse)(nil), // 20: headscale.v1.CreateNamespaceResponse + (*RenameNamespaceResponse)(nil), // 21: headscale.v1.RenameNamespaceResponse + (*DeleteNamespaceResponse)(nil), // 22: headscale.v1.DeleteNamespaceResponse + (*ListNamespacesResponse)(nil), // 23: headscale.v1.ListNamespacesResponse + (*CreatePreAuthKeyResponse)(nil), // 24: headscale.v1.CreatePreAuthKeyResponse + (*ExpirePreAuthKeyResponse)(nil), // 25: headscale.v1.ExpirePreAuthKeyResponse + (*ListPreAuthKeysResponse)(nil), // 26: headscale.v1.ListPreAuthKeysResponse + (*DebugCreateMachineResponse)(nil), // 27: headscale.v1.DebugCreateMachineResponse + (*GetMachineResponse)(nil), // 28: headscale.v1.GetMachineResponse + (*RegisterMachineResponse)(nil), // 29: headscale.v1.RegisterMachineResponse + (*DeleteMachineResponse)(nil), // 30: headscale.v1.DeleteMachineResponse + (*ExpireMachineResponse)(nil), // 31: headscale.v1.ExpireMachineResponse + (*ListMachinesResponse)(nil), // 32: headscale.v1.ListMachinesResponse + (*GetMachineRouteResponse)(nil), // 33: headscale.v1.GetMachineRouteResponse + (*EnableMachineRoutesResponse)(nil), // 34: headscale.v1.EnableMachineRoutesResponse + (*CreateApiKeyResponse)(nil), // 35: headscale.v1.CreateApiKeyResponse + (*ExpireApiKeyResponse)(nil), // 36: headscale.v1.ExpireApiKeyResponse + (*ListApiKeysResponse)(nil), // 37: headscale.v1.ListApiKeysResponse } var file_headscale_v1_headscale_proto_depIdxs = []int32{ 0, // 0: headscale.v1.HeadscaleService.GetNamespace:input_type -> headscale.v1.GetNamespaceRequest @@ -275,36 +252,32 @@ var file_headscale_v1_headscale_proto_depIdxs = []int32{ 11, // 11: headscale.v1.HeadscaleService.DeleteMachine:input_type -> headscale.v1.DeleteMachineRequest 12, // 12: headscale.v1.HeadscaleService.ExpireMachine:input_type -> headscale.v1.ExpireMachineRequest 13, // 13: headscale.v1.HeadscaleService.ListMachines:input_type -> headscale.v1.ListMachinesRequest - 14, // 14: headscale.v1.HeadscaleService.ShareMachine:input_type -> headscale.v1.ShareMachineRequest - 15, // 15: headscale.v1.HeadscaleService.UnshareMachine:input_type -> headscale.v1.UnshareMachineRequest - 16, // 16: headscale.v1.HeadscaleService.GetMachineRoute:input_type -> headscale.v1.GetMachineRouteRequest - 17, // 17: headscale.v1.HeadscaleService.EnableMachineRoutes:input_type -> headscale.v1.EnableMachineRoutesRequest - 18, // 18: headscale.v1.HeadscaleService.CreateApiKey:input_type -> headscale.v1.CreateApiKeyRequest - 19, // 19: headscale.v1.HeadscaleService.ExpireApiKey:input_type -> headscale.v1.ExpireApiKeyRequest - 20, // 20: headscale.v1.HeadscaleService.ListApiKeys:input_type -> headscale.v1.ListApiKeysRequest - 21, // 21: headscale.v1.HeadscaleService.GetNamespace:output_type -> headscale.v1.GetNamespaceResponse - 22, // 22: headscale.v1.HeadscaleService.CreateNamespace:output_type -> headscale.v1.CreateNamespaceResponse - 23, // 23: headscale.v1.HeadscaleService.RenameNamespace:output_type -> headscale.v1.RenameNamespaceResponse - 24, // 24: headscale.v1.HeadscaleService.DeleteNamespace:output_type -> headscale.v1.DeleteNamespaceResponse - 25, // 25: headscale.v1.HeadscaleService.ListNamespaces:output_type -> headscale.v1.ListNamespacesResponse - 26, // 26: headscale.v1.HeadscaleService.CreatePreAuthKey:output_type -> headscale.v1.CreatePreAuthKeyResponse - 27, // 27: headscale.v1.HeadscaleService.ExpirePreAuthKey:output_type -> headscale.v1.ExpirePreAuthKeyResponse - 28, // 28: headscale.v1.HeadscaleService.ListPreAuthKeys:output_type -> headscale.v1.ListPreAuthKeysResponse - 29, // 29: headscale.v1.HeadscaleService.DebugCreateMachine:output_type -> headscale.v1.DebugCreateMachineResponse - 30, // 30: headscale.v1.HeadscaleService.GetMachine:output_type -> headscale.v1.GetMachineResponse - 31, // 31: headscale.v1.HeadscaleService.RegisterMachine:output_type -> headscale.v1.RegisterMachineResponse - 32, // 32: headscale.v1.HeadscaleService.DeleteMachine:output_type -> headscale.v1.DeleteMachineResponse - 33, // 33: headscale.v1.HeadscaleService.ExpireMachine:output_type -> headscale.v1.ExpireMachineResponse - 34, // 34: headscale.v1.HeadscaleService.ListMachines:output_type -> headscale.v1.ListMachinesResponse - 35, // 35: headscale.v1.HeadscaleService.ShareMachine:output_type -> headscale.v1.ShareMachineResponse - 36, // 36: headscale.v1.HeadscaleService.UnshareMachine:output_type -> headscale.v1.UnshareMachineResponse - 37, // 37: headscale.v1.HeadscaleService.GetMachineRoute:output_type -> headscale.v1.GetMachineRouteResponse - 38, // 38: headscale.v1.HeadscaleService.EnableMachineRoutes:output_type -> headscale.v1.EnableMachineRoutesResponse - 39, // 39: headscale.v1.HeadscaleService.CreateApiKey:output_type -> headscale.v1.CreateApiKeyResponse - 40, // 40: headscale.v1.HeadscaleService.ExpireApiKey:output_type -> headscale.v1.ExpireApiKeyResponse - 41, // 41: headscale.v1.HeadscaleService.ListApiKeys:output_type -> headscale.v1.ListApiKeysResponse - 21, // [21:42] is the sub-list for method output_type - 0, // [0:21] is the sub-list for method input_type + 14, // 14: headscale.v1.HeadscaleService.GetMachineRoute:input_type -> headscale.v1.GetMachineRouteRequest + 15, // 15: headscale.v1.HeadscaleService.EnableMachineRoutes:input_type -> headscale.v1.EnableMachineRoutesRequest + 16, // 16: headscale.v1.HeadscaleService.CreateApiKey:input_type -> headscale.v1.CreateApiKeyRequest + 17, // 17: headscale.v1.HeadscaleService.ExpireApiKey:input_type -> headscale.v1.ExpireApiKeyRequest + 18, // 18: headscale.v1.HeadscaleService.ListApiKeys:input_type -> headscale.v1.ListApiKeysRequest + 19, // 19: headscale.v1.HeadscaleService.GetNamespace:output_type -> headscale.v1.GetNamespaceResponse + 20, // 20: headscale.v1.HeadscaleService.CreateNamespace:output_type -> headscale.v1.CreateNamespaceResponse + 21, // 21: headscale.v1.HeadscaleService.RenameNamespace:output_type -> headscale.v1.RenameNamespaceResponse + 22, // 22: headscale.v1.HeadscaleService.DeleteNamespace:output_type -> headscale.v1.DeleteNamespaceResponse + 23, // 23: headscale.v1.HeadscaleService.ListNamespaces:output_type -> headscale.v1.ListNamespacesResponse + 24, // 24: headscale.v1.HeadscaleService.CreatePreAuthKey:output_type -> headscale.v1.CreatePreAuthKeyResponse + 25, // 25: headscale.v1.HeadscaleService.ExpirePreAuthKey:output_type -> headscale.v1.ExpirePreAuthKeyResponse + 26, // 26: headscale.v1.HeadscaleService.ListPreAuthKeys:output_type -> headscale.v1.ListPreAuthKeysResponse + 27, // 27: headscale.v1.HeadscaleService.DebugCreateMachine:output_type -> headscale.v1.DebugCreateMachineResponse + 28, // 28: headscale.v1.HeadscaleService.GetMachine:output_type -> headscale.v1.GetMachineResponse + 29, // 29: headscale.v1.HeadscaleService.RegisterMachine:output_type -> headscale.v1.RegisterMachineResponse + 30, // 30: headscale.v1.HeadscaleService.DeleteMachine:output_type -> headscale.v1.DeleteMachineResponse + 31, // 31: headscale.v1.HeadscaleService.ExpireMachine:output_type -> headscale.v1.ExpireMachineResponse + 32, // 32: headscale.v1.HeadscaleService.ListMachines:output_type -> headscale.v1.ListMachinesResponse + 33, // 33: headscale.v1.HeadscaleService.GetMachineRoute:output_type -> headscale.v1.GetMachineRouteResponse + 34, // 34: headscale.v1.HeadscaleService.EnableMachineRoutes:output_type -> headscale.v1.EnableMachineRoutesResponse + 35, // 35: headscale.v1.HeadscaleService.CreateApiKey:output_type -> headscale.v1.CreateApiKeyResponse + 36, // 36: headscale.v1.HeadscaleService.ExpireApiKey:output_type -> headscale.v1.ExpireApiKeyResponse + 37, // 37: headscale.v1.HeadscaleService.ListApiKeys:output_type -> headscale.v1.ListApiKeysResponse + 19, // [19:38] is the sub-list for method output_type + 0, // [0:19] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name diff --git a/gen/go/headscale/v1/headscale.pb.gw.go b/gen/go/headscale/v1/headscale.pb.gw.go index b245b895..0938e147 100644 --- a/gen/go/headscale/v1/headscale.pb.gw.go +++ b/gen/go/headscale/v1/headscale.pb.gw.go @@ -625,150 +625,6 @@ func local_request_HeadscaleService_ListMachines_0(ctx context.Context, marshale } -func request_HeadscaleService_ShareMachine_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq ShareMachineRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["machine_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "machine_id") - } - - protoReq.MachineId, err = runtime.Uint64(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "machine_id", err) - } - - val, ok = pathParams["namespace"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace") - } - - protoReq.Namespace, err = runtime.String(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace", err) - } - - msg, err := client.ShareMachine(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_HeadscaleService_ShareMachine_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq ShareMachineRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["machine_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "machine_id") - } - - protoReq.MachineId, err = runtime.Uint64(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "machine_id", err) - } - - val, ok = pathParams["namespace"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace") - } - - protoReq.Namespace, err = runtime.String(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace", err) - } - - msg, err := server.ShareMachine(ctx, &protoReq) - return msg, metadata, err - -} - -func request_HeadscaleService_UnshareMachine_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq UnshareMachineRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["machine_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "machine_id") - } - - protoReq.MachineId, err = runtime.Uint64(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "machine_id", err) - } - - val, ok = pathParams["namespace"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace") - } - - protoReq.Namespace, err = runtime.String(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace", err) - } - - msg, err := client.UnshareMachine(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_HeadscaleService_UnshareMachine_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq UnshareMachineRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["machine_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "machine_id") - } - - protoReq.MachineId, err = runtime.Uint64(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "machine_id", err) - } - - val, ok = pathParams["namespace"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace") - } - - protoReq.Namespace, err = runtime.String(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace", err) - } - - msg, err := server.UnshareMachine(ctx, &protoReq) - return msg, metadata, err - -} - func request_HeadscaleService_GetMachineRoute_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq GetMachineRouteRequest var metadata runtime.ServerMetadata @@ -1305,52 +1161,6 @@ func RegisterHeadscaleServiceHandlerServer(ctx context.Context, mux *runtime.Ser }) - mux.Handle("POST", pattern_HeadscaleService_ShareMachine_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/headscale.v1.HeadscaleService/ShareMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}/share/{namespace}")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_HeadscaleService_ShareMachine_0(rctx, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_HeadscaleService_ShareMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_HeadscaleService_UnshareMachine_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/headscale.v1.HeadscaleService/UnshareMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}/unshare/{namespace}")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_HeadscaleService_UnshareMachine_0(rctx, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_HeadscaleService_UnshareMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - mux.Handle("GET", pattern_HeadscaleService_GetMachineRoute_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1787,46 +1597,6 @@ func RegisterHeadscaleServiceHandlerClient(ctx context.Context, mux *runtime.Ser }) - mux.Handle("POST", pattern_HeadscaleService_ShareMachine_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/headscale.v1.HeadscaleService/ShareMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}/share/{namespace}")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_HeadscaleService_ShareMachine_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_HeadscaleService_ShareMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_HeadscaleService_UnshareMachine_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/headscale.v1.HeadscaleService/UnshareMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}/unshare/{namespace}")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_HeadscaleService_UnshareMachine_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_HeadscaleService_UnshareMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - mux.Handle("GET", pattern_HeadscaleService_GetMachineRoute_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1959,10 +1729,6 @@ var ( pattern_HeadscaleService_ListMachines_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "machine"}, "")) - pattern_HeadscaleService_ShareMachine_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"api", "v1", "machine", "machine_id", "share", "namespace"}, "")) - - pattern_HeadscaleService_UnshareMachine_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"api", "v1", "machine", "machine_id", "unshare", "namespace"}, "")) - pattern_HeadscaleService_GetMachineRoute_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "machine", "machine_id", "routes"}, "")) pattern_HeadscaleService_EnableMachineRoutes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "machine", "machine_id", "routes"}, "")) @@ -2003,10 +1769,6 @@ var ( forward_HeadscaleService_ListMachines_0 = runtime.ForwardResponseMessage - forward_HeadscaleService_ShareMachine_0 = runtime.ForwardResponseMessage - - forward_HeadscaleService_UnshareMachine_0 = runtime.ForwardResponseMessage - forward_HeadscaleService_GetMachineRoute_0 = runtime.ForwardResponseMessage forward_HeadscaleService_EnableMachineRoutes_0 = runtime.ForwardResponseMessage diff --git a/gen/go/headscale/v1/headscale_grpc.pb.go b/gen/go/headscale/v1/headscale_grpc.pb.go index c75a36c5..e4edf3f0 100644 --- a/gen/go/headscale/v1/headscale_grpc.pb.go +++ b/gen/go/headscale/v1/headscale_grpc.pb.go @@ -1,10 +1,13 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc (unknown) +// source: headscale/v1/headscale.proto package v1 import ( context "context" - grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -36,8 +39,6 @@ type HeadscaleServiceClient interface { DeleteMachine(ctx context.Context, in *DeleteMachineRequest, opts ...grpc.CallOption) (*DeleteMachineResponse, error) ExpireMachine(ctx context.Context, in *ExpireMachineRequest, opts ...grpc.CallOption) (*ExpireMachineResponse, error) ListMachines(ctx context.Context, in *ListMachinesRequest, opts ...grpc.CallOption) (*ListMachinesResponse, error) - ShareMachine(ctx context.Context, in *ShareMachineRequest, opts ...grpc.CallOption) (*ShareMachineResponse, error) - UnshareMachine(ctx context.Context, in *UnshareMachineRequest, opts ...grpc.CallOption) (*UnshareMachineResponse, error) // --- Route start --- GetMachineRoute(ctx context.Context, in *GetMachineRouteRequest, opts ...grpc.CallOption) (*GetMachineRouteResponse, error) EnableMachineRoutes(ctx context.Context, in *EnableMachineRoutesRequest, opts ...grpc.CallOption) (*EnableMachineRoutesResponse, error) @@ -181,24 +182,6 @@ func (c *headscaleServiceClient) ListMachines(ctx context.Context, in *ListMachi return out, nil } -func (c *headscaleServiceClient) ShareMachine(ctx context.Context, in *ShareMachineRequest, opts ...grpc.CallOption) (*ShareMachineResponse, error) { - out := new(ShareMachineResponse) - err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ShareMachine", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *headscaleServiceClient) UnshareMachine(ctx context.Context, in *UnshareMachineRequest, opts ...grpc.CallOption) (*UnshareMachineResponse, error) { - out := new(UnshareMachineResponse) - err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/UnshareMachine", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *headscaleServiceClient) GetMachineRoute(ctx context.Context, in *GetMachineRouteRequest, opts ...grpc.CallOption) (*GetMachineRouteResponse, error) { out := new(GetMachineRouteResponse) err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/GetMachineRoute", in, out, opts...) @@ -265,8 +248,6 @@ type HeadscaleServiceServer interface { DeleteMachine(context.Context, *DeleteMachineRequest) (*DeleteMachineResponse, error) ExpireMachine(context.Context, *ExpireMachineRequest) (*ExpireMachineResponse, error) ListMachines(context.Context, *ListMachinesRequest) (*ListMachinesResponse, error) - ShareMachine(context.Context, *ShareMachineRequest) (*ShareMachineResponse, error) - UnshareMachine(context.Context, *UnshareMachineRequest) (*UnshareMachineResponse, error) // --- Route start --- GetMachineRoute(context.Context, *GetMachineRouteRequest) (*GetMachineRouteResponse, error) EnableMachineRoutes(context.Context, *EnableMachineRoutesRequest) (*EnableMachineRoutesResponse, error) @@ -323,12 +304,6 @@ func (UnimplementedHeadscaleServiceServer) ExpireMachine(context.Context, *Expir func (UnimplementedHeadscaleServiceServer) ListMachines(context.Context, *ListMachinesRequest) (*ListMachinesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListMachines not implemented") } -func (UnimplementedHeadscaleServiceServer) ShareMachine(context.Context, *ShareMachineRequest) (*ShareMachineResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ShareMachine not implemented") -} -func (UnimplementedHeadscaleServiceServer) UnshareMachine(context.Context, *UnshareMachineRequest) (*UnshareMachineResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method UnshareMachine not implemented") -} func (UnimplementedHeadscaleServiceServer) GetMachineRoute(context.Context, *GetMachineRouteRequest) (*GetMachineRouteResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetMachineRoute not implemented") } @@ -609,42 +584,6 @@ func _HeadscaleService_ListMachines_Handler(srv interface{}, ctx context.Context return interceptor(ctx, in, info, handler) } -func _HeadscaleService_ShareMachine_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ShareMachineRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(HeadscaleServiceServer).ShareMachine(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/headscale.v1.HeadscaleService/ShareMachine", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(HeadscaleServiceServer).ShareMachine(ctx, req.(*ShareMachineRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _HeadscaleService_UnshareMachine_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UnshareMachineRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(HeadscaleServiceServer).UnshareMachine(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/headscale.v1.HeadscaleService/UnshareMachine", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(HeadscaleServiceServer).UnshareMachine(ctx, req.(*UnshareMachineRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _HeadscaleService_GetMachineRoute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetMachineRouteRequest) if err := dec(in); err != nil { @@ -798,14 +737,6 @@ var HeadscaleService_ServiceDesc = grpc.ServiceDesc{ MethodName: "ListMachines", Handler: _HeadscaleService_ListMachines_Handler, }, - { - MethodName: "ShareMachine", - Handler: _HeadscaleService_ShareMachine_Handler, - }, - { - MethodName: "UnshareMachine", - Handler: _HeadscaleService_UnshareMachine_Handler, - }, { MethodName: "GetMachineRoute", Handler: _HeadscaleService_GetMachineRoute_Handler, diff --git a/gen/go/headscale/v1/machine.pb.go b/gen/go/headscale/v1/machine.pb.go index 5f12c6e5..b25db290 100644 --- a/gen/go/headscale/v1/machine.pb.go +++ b/gen/go/headscale/v1/machine.pb.go @@ -7,12 +7,11 @@ package v1 import ( - reflect "reflect" - sync "sync" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" ) const ( @@ -86,13 +85,12 @@ type Machine struct { IpAddresses []string `protobuf:"bytes,5,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ip_addresses,omitempty"` Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` Namespace *Namespace `protobuf:"bytes,7,opt,name=namespace,proto3" json:"namespace,omitempty"` - Registered bool `protobuf:"varint,8,opt,name=registered,proto3" json:"registered,omitempty"` - RegisterMethod RegisterMethod `protobuf:"varint,9,opt,name=register_method,json=registerMethod,proto3,enum=headscale.v1.RegisterMethod" json:"register_method,omitempty"` - LastSeen *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` - LastSuccessfulUpdate *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=last_successful_update,json=lastSuccessfulUpdate,proto3" json:"last_successful_update,omitempty"` - Expiry *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=expiry,proto3" json:"expiry,omitempty"` - PreAuthKey *PreAuthKey `protobuf:"bytes,13,opt,name=pre_auth_key,json=preAuthKey,proto3" json:"pre_auth_key,omitempty"` - CreatedAt *timestamppb.Timestamp `protobuf:"bytes,14,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + LastSeen *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` + LastSuccessfulUpdate *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=last_successful_update,json=lastSuccessfulUpdate,proto3" json:"last_successful_update,omitempty"` + Expiry *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=expiry,proto3" json:"expiry,omitempty"` + PreAuthKey *PreAuthKey `protobuf:"bytes,11,opt,name=pre_auth_key,json=preAuthKey,proto3" json:"pre_auth_key,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + RegisterMethod RegisterMethod `protobuf:"varint,13,opt,name=register_method,json=registerMethod,proto3,enum=headscale.v1.RegisterMethod" json:"register_method,omitempty"` } func (x *Machine) Reset() { @@ -176,20 +174,6 @@ func (x *Machine) GetNamespace() *Namespace { return nil } -func (x *Machine) GetRegistered() bool { - if x != nil { - return x.Registered - } - return false -} - -func (x *Machine) GetRegisterMethod() RegisterMethod { - if x != nil { - return x.RegisterMethod - } - return RegisterMethod_REGISTER_METHOD_UNSPECIFIED -} - func (x *Machine) GetLastSeen() *timestamppb.Timestamp { if x != nil { return x.LastSeen @@ -225,6 +209,13 @@ func (x *Machine) GetCreatedAt() *timestamppb.Timestamp { return nil } +func (x *Machine) GetRegisterMethod() RegisterMethod { + if x != nil { + return x.RegisterMethod + } + return RegisterMethod_REGISTER_METHOD_UNSPECIFIED +} + type RegisterMachineRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -694,210 +685,6 @@ func (x *ListMachinesResponse) GetMachines() []*Machine { return nil } -type ShareMachineRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - MachineId uint64 `protobuf:"varint,1,opt,name=machine_id,json=machineId,proto3" json:"machine_id,omitempty"` - Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` -} - -func (x *ShareMachineRequest) Reset() { - *x = ShareMachineRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_machine_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ShareMachineRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ShareMachineRequest) ProtoMessage() {} - -func (x *ShareMachineRequest) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_machine_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ShareMachineRequest.ProtoReflect.Descriptor instead. -func (*ShareMachineRequest) Descriptor() ([]byte, []int) { - return file_headscale_v1_machine_proto_rawDescGZIP(), []int{11} -} - -func (x *ShareMachineRequest) GetMachineId() uint64 { - if x != nil { - return x.MachineId - } - return 0 -} - -func (x *ShareMachineRequest) GetNamespace() string { - if x != nil { - return x.Namespace - } - return "" -} - -type ShareMachineResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Machine *Machine `protobuf:"bytes,1,opt,name=machine,proto3" json:"machine,omitempty"` -} - -func (x *ShareMachineResponse) Reset() { - *x = ShareMachineResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_machine_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ShareMachineResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ShareMachineResponse) ProtoMessage() {} - -func (x *ShareMachineResponse) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_machine_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ShareMachineResponse.ProtoReflect.Descriptor instead. -func (*ShareMachineResponse) Descriptor() ([]byte, []int) { - return file_headscale_v1_machine_proto_rawDescGZIP(), []int{12} -} - -func (x *ShareMachineResponse) GetMachine() *Machine { - if x != nil { - return x.Machine - } - return nil -} - -type UnshareMachineRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - MachineId uint64 `protobuf:"varint,1,opt,name=machine_id,json=machineId,proto3" json:"machine_id,omitempty"` - Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` -} - -func (x *UnshareMachineRequest) Reset() { - *x = UnshareMachineRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_machine_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UnshareMachineRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UnshareMachineRequest) ProtoMessage() {} - -func (x *UnshareMachineRequest) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_machine_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UnshareMachineRequest.ProtoReflect.Descriptor instead. -func (*UnshareMachineRequest) Descriptor() ([]byte, []int) { - return file_headscale_v1_machine_proto_rawDescGZIP(), []int{13} -} - -func (x *UnshareMachineRequest) GetMachineId() uint64 { - if x != nil { - return x.MachineId - } - return 0 -} - -func (x *UnshareMachineRequest) GetNamespace() string { - if x != nil { - return x.Namespace - } - return "" -} - -type UnshareMachineResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Machine *Machine `protobuf:"bytes,1,opt,name=machine,proto3" json:"machine,omitempty"` -} - -func (x *UnshareMachineResponse) Reset() { - *x = UnshareMachineResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_machine_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UnshareMachineResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UnshareMachineResponse) ProtoMessage() {} - -func (x *UnshareMachineResponse) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_machine_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UnshareMachineResponse.ProtoReflect.Descriptor instead. -func (*UnshareMachineResponse) Descriptor() ([]byte, []int) { - return file_headscale_v1_machine_proto_rawDescGZIP(), []int{14} -} - -func (x *UnshareMachineResponse) GetMachine() *Machine { - if x != nil { - return x.Machine - } - return nil -} - type DebugCreateMachineRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -912,7 +699,7 @@ type DebugCreateMachineRequest struct { func (x *DebugCreateMachineRequest) Reset() { *x = DebugCreateMachineRequest{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_machine_proto_msgTypes[15] + mi := &file_headscale_v1_machine_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -925,7 +712,7 @@ func (x *DebugCreateMachineRequest) String() string { func (*DebugCreateMachineRequest) ProtoMessage() {} func (x *DebugCreateMachineRequest) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_machine_proto_msgTypes[15] + mi := &file_headscale_v1_machine_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -938,7 +725,7 @@ func (x *DebugCreateMachineRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DebugCreateMachineRequest.ProtoReflect.Descriptor instead. func (*DebugCreateMachineRequest) Descriptor() ([]byte, []int) { - return file_headscale_v1_machine_proto_rawDescGZIP(), []int{15} + return file_headscale_v1_machine_proto_rawDescGZIP(), []int{11} } func (x *DebugCreateMachineRequest) GetNamespace() string { @@ -980,7 +767,7 @@ type DebugCreateMachineResponse struct { func (x *DebugCreateMachineResponse) Reset() { *x = DebugCreateMachineResponse{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_machine_proto_msgTypes[16] + mi := &file_headscale_v1_machine_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -993,7 +780,7 @@ func (x *DebugCreateMachineResponse) String() string { func (*DebugCreateMachineResponse) ProtoMessage() {} func (x *DebugCreateMachineResponse) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_machine_proto_msgTypes[16] + mi := &file_headscale_v1_machine_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1006,7 +793,7 @@ func (x *DebugCreateMachineResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DebugCreateMachineResponse.ProtoReflect.Descriptor instead. func (*DebugCreateMachineResponse) Descriptor() ([]byte, []int) { - return file_headscale_v1_machine_proto_rawDescGZIP(), []int{16} + return file_headscale_v1_machine_proto_rawDescGZIP(), []int{12} } func (x *DebugCreateMachineResponse) GetMachine() *Machine { @@ -1027,7 +814,7 @@ var file_headscale_v1_machine_proto_rawDesc = []byte{ 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x65, 0x61, 0x75, 0x74, 0x68, 0x6b, - 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfd, 0x04, 0x0a, 0x07, 0x4d, 0x61, 0x63, + 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdd, 0x04, 0x0a, 0x07, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, @@ -1041,33 +828,31 @@ var file_headscale_v1_machine_proto_rawDesc = []byte{ 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, 0x45, 0x0a, 0x0f, 0x72, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x52, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x50, 0x0a, 0x16, 0x6c, - 0x61, 0x73, 0x74, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x5f, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x75, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, - 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, - 0x79, 0x12, 0x3a, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, - 0x79, 0x52, 0x0a, 0x70, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x39, 0x0a, - 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x48, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69, + 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x09, 0x6c, + 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, + 0x53, 0x65, 0x65, 0x6e, 0x12, 0x50, 0x0a, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x3a, 0x0a, 0x0c, 0x70, 0x72, + 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x18, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x65, 0x41, + 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, + 0x74, 0x12, 0x45, 0x0a, 0x0f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x68, 0x65, 0x61, + 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x22, 0x48, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, @@ -1105,51 +890,31 @@ var file_headscale_v1_machine_proto_rawDesc = []byte{ 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, - 0x52, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x52, 0x0a, 0x13, 0x53, 0x68, - 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, - 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x47, - 0x0a, 0x14, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x07, - 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x54, 0x0a, 0x15, 0x55, 0x6e, 0x73, 0x68, 0x61, - 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x12, - 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x49, 0x0a, - 0x16, 0x55, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, - 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, - 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x77, 0x0a, 0x19, 0x44, 0x65, 0x62, 0x75, - 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, - 0x73, 0x22, 0x4d, 0x0a, 0x1a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, - 0x2a, 0x82, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, - 0x68, 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, - 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, - 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x5f, 0x4b, 0x45, 0x59, - 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, - 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x43, 0x4c, 0x49, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x52, - 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x4f, - 0x49, 0x44, 0x43, 0x10, 0x03, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, - 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x77, 0x0a, 0x19, 0x44, 0x65, + 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x73, 0x22, 0x4d, 0x0a, 0x1a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, + 0x6e, 0x65, 0x2a, 0x82, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, + 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, + 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x5f, 0x4b, + 0x45, 0x59, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, + 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x43, 0x4c, 0x49, 0x10, 0x02, 0x12, 0x18, 0x0a, + 0x14, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, + 0x5f, 0x4f, 0x49, 0x44, 0x43, 0x10, 0x03, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, + 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, + 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1165,7 +930,7 @@ func file_headscale_v1_machine_proto_rawDescGZIP() []byte { } var file_headscale_v1_machine_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_headscale_v1_machine_proto_msgTypes = make([]protoimpl.MessageInfo, 17) +var file_headscale_v1_machine_proto_msgTypes = make([]protoimpl.MessageInfo, 13) var file_headscale_v1_machine_proto_goTypes = []interface{}{ (RegisterMethod)(0), // 0: headscale.v1.RegisterMethod (*Machine)(nil), // 1: headscale.v1.Machine @@ -1179,36 +944,30 @@ var file_headscale_v1_machine_proto_goTypes = []interface{}{ (*ExpireMachineResponse)(nil), // 9: headscale.v1.ExpireMachineResponse (*ListMachinesRequest)(nil), // 10: headscale.v1.ListMachinesRequest (*ListMachinesResponse)(nil), // 11: headscale.v1.ListMachinesResponse - (*ShareMachineRequest)(nil), // 12: headscale.v1.ShareMachineRequest - (*ShareMachineResponse)(nil), // 13: headscale.v1.ShareMachineResponse - (*UnshareMachineRequest)(nil), // 14: headscale.v1.UnshareMachineRequest - (*UnshareMachineResponse)(nil), // 15: headscale.v1.UnshareMachineResponse - (*DebugCreateMachineRequest)(nil), // 16: headscale.v1.DebugCreateMachineRequest - (*DebugCreateMachineResponse)(nil), // 17: headscale.v1.DebugCreateMachineResponse - (*Namespace)(nil), // 18: headscale.v1.Namespace - (*timestamppb.Timestamp)(nil), // 19: google.protobuf.Timestamp - (*PreAuthKey)(nil), // 20: headscale.v1.PreAuthKey + (*DebugCreateMachineRequest)(nil), // 12: headscale.v1.DebugCreateMachineRequest + (*DebugCreateMachineResponse)(nil), // 13: headscale.v1.DebugCreateMachineResponse + (*Namespace)(nil), // 14: headscale.v1.Namespace + (*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp + (*PreAuthKey)(nil), // 16: headscale.v1.PreAuthKey } var file_headscale_v1_machine_proto_depIdxs = []int32{ - 18, // 0: headscale.v1.Machine.namespace:type_name -> headscale.v1.Namespace - 0, // 1: headscale.v1.Machine.register_method:type_name -> headscale.v1.RegisterMethod - 19, // 2: headscale.v1.Machine.last_seen:type_name -> google.protobuf.Timestamp - 19, // 3: headscale.v1.Machine.last_successful_update:type_name -> google.protobuf.Timestamp - 19, // 4: headscale.v1.Machine.expiry:type_name -> google.protobuf.Timestamp - 20, // 5: headscale.v1.Machine.pre_auth_key:type_name -> headscale.v1.PreAuthKey - 19, // 6: headscale.v1.Machine.created_at:type_name -> google.protobuf.Timestamp + 14, // 0: headscale.v1.Machine.namespace:type_name -> headscale.v1.Namespace + 15, // 1: headscale.v1.Machine.last_seen:type_name -> google.protobuf.Timestamp + 15, // 2: headscale.v1.Machine.last_successful_update:type_name -> google.protobuf.Timestamp + 15, // 3: headscale.v1.Machine.expiry:type_name -> google.protobuf.Timestamp + 16, // 4: headscale.v1.Machine.pre_auth_key:type_name -> headscale.v1.PreAuthKey + 15, // 5: headscale.v1.Machine.created_at:type_name -> google.protobuf.Timestamp + 0, // 6: headscale.v1.Machine.register_method:type_name -> headscale.v1.RegisterMethod 1, // 7: headscale.v1.RegisterMachineResponse.machine:type_name -> headscale.v1.Machine 1, // 8: headscale.v1.GetMachineResponse.machine:type_name -> headscale.v1.Machine 1, // 9: headscale.v1.ExpireMachineResponse.machine:type_name -> headscale.v1.Machine 1, // 10: headscale.v1.ListMachinesResponse.machines:type_name -> headscale.v1.Machine - 1, // 11: headscale.v1.ShareMachineResponse.machine:type_name -> headscale.v1.Machine - 1, // 12: headscale.v1.UnshareMachineResponse.machine:type_name -> headscale.v1.Machine - 1, // 13: headscale.v1.DebugCreateMachineResponse.machine:type_name -> headscale.v1.Machine - 14, // [14:14] is the sub-list for method output_type - 14, // [14:14] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name + 1, // 11: headscale.v1.DebugCreateMachineResponse.machine:type_name -> headscale.v1.Machine + 12, // [12:12] is the sub-list for method output_type + 12, // [12:12] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name } func init() { file_headscale_v1_machine_proto_init() } @@ -1352,54 +1111,6 @@ func file_headscale_v1_machine_proto_init() { } } file_headscale_v1_machine_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ShareMachineRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_headscale_v1_machine_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ShareMachineResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_headscale_v1_machine_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UnshareMachineRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_headscale_v1_machine_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UnshareMachineResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_headscale_v1_machine_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DebugCreateMachineRequest); i { case 0: return &v.state @@ -1411,7 +1122,7 @@ func file_headscale_v1_machine_proto_init() { return nil } } - file_headscale_v1_machine_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + file_headscale_v1_machine_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DebugCreateMachineResponse); i { case 0: return &v.state @@ -1430,7 +1141,7 @@ func file_headscale_v1_machine_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_headscale_v1_machine_proto_rawDesc, NumEnums: 1, - NumMessages: 17, + NumMessages: 13, NumExtensions: 0, NumServices: 0, }, diff --git a/gen/go/headscale/v1/namespace.pb.go b/gen/go/headscale/v1/namespace.pb.go index 0e0f8279..f0893653 100644 --- a/gen/go/headscale/v1/namespace.pb.go +++ b/gen/go/headscale/v1/namespace.pb.go @@ -7,12 +7,11 @@ package v1 import ( - reflect "reflect" - sync "sync" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" ) const ( diff --git a/gen/go/headscale/v1/preauthkey.pb.go b/gen/go/headscale/v1/preauthkey.pb.go index 056e0f39..645a0f03 100644 --- a/gen/go/headscale/v1/preauthkey.pb.go +++ b/gen/go/headscale/v1/preauthkey.pb.go @@ -7,12 +7,11 @@ package v1 import ( - reflect "reflect" - sync "sync" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" ) const ( diff --git a/gen/go/headscale/v1/routes.pb.go b/gen/go/headscale/v1/routes.pb.go index 12510f39..5ab4bbdc 100644 --- a/gen/go/headscale/v1/routes.pb.go +++ b/gen/go/headscale/v1/routes.pb.go @@ -7,11 +7,10 @@ package v1 import ( - reflect "reflect" - sync "sync" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) const ( diff --git a/gen/openapiv2/headscale/v1/headscale.swagger.json b/gen/openapiv2/headscale/v1/headscale.swagger.json index d91d0baf..8d808f98 100644 --- a/gen/openapiv2/headscale/v1/headscale.swagger.json +++ b/gen/openapiv2/headscale/v1/headscale.swagger.json @@ -181,6 +181,20 @@ } } }, + "parameters": [ + { + "name": "namespace", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "key", + "in": "query", + "required": false, + "type": "string" + } + ], "tags": [ "HeadscaleService" ] @@ -324,37 +338,6 @@ } } }, - "parameters": [ - { - "name": "machineId", - "in": "path", - "required": true, - "type": "string", - "format": "uint64" - } - ], - "tags": [ - "HeadscaleService" - ] - } - }, - "/api/v1/machine/{machineId}/share/{namespace}": { - "post": { - "operationId": "HeadscaleService_ShareMachine", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/v1ShareMachineResponse" - } - }, - "default": { - "description": "An unexpected error response.", - "schema": { - "$ref": "#/definitions/rpcStatus" - } - } - }, "parameters": [ { "name": "machineId", @@ -364,47 +347,14 @@ "format": "uint64" }, { - "name": "namespace", - "in": "path", - "required": true, - "type": "string" - } - ], - "tags": [ - "HeadscaleService" - ] - } - }, - "/api/v1/machine/{machineId}/unshare/{namespace}": { - "post": { - "operationId": "HeadscaleService_UnshareMachine", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/v1UnshareMachineResponse" - } - }, - "default": { - "description": "An unexpected error response.", - "schema": { - "$ref": "#/definitions/rpcStatus" - } - } - }, - "parameters": [ - { - "name": "machineId", - "in": "path", - "required": true, - "type": "string", - "format": "uint64" - }, - { - "name": "namespace", - "in": "path", - "required": true, - "type": "string" + "name": "routes", + "in": "query", + "required": false, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" } ], "tags": [ @@ -935,12 +885,6 @@ "namespace": { "$ref": "#/definitions/v1Namespace" }, - "registered": { - "type": "boolean" - }, - "registerMethod": { - "$ref": "#/definitions/v1RegisterMethod" - }, "lastSeen": { "type": "string", "format": "date-time" @@ -959,6 +903,9 @@ "createdAt": { "type": "string", "format": "date-time" + }, + "registerMethod": { + "$ref": "#/definitions/v1RegisterMethod" } } }, @@ -1050,22 +997,6 @@ } } } - }, - "v1ShareMachineResponse": { - "type": "object", - "properties": { - "machine": { - "$ref": "#/definitions/v1Machine" - } - } - }, - "v1UnshareMachineResponse": { - "type": "object", - "properties": { - "machine": { - "$ref": "#/definitions/v1Machine" - } - } } } } diff --git a/go.mod b/go.mod index bbba4646..1ec291c3 100644 --- a/go.mod +++ b/go.mod @@ -8,58 +8,60 @@ require ( github.com/efekarakus/termcolor v1.0.1 github.com/fatih/set v0.2.1 github.com/gin-gonic/gin v1.7.7 + github.com/glebarez/sqlite v1.3.5 github.com/gofrs/uuid v4.2.0+incompatible github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.3 github.com/infobloxopen/protoc-gen-gorm v1.1.0 - github.com/klauspost/compress v1.14.2 + github.com/klauspost/compress v1.14.4 github.com/ory/dockertest/v3 v3.8.1 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/philip-bui/grpc-zerolog v1.0.1 github.com/prometheus/client_golang v1.12.1 - github.com/pterm/pterm v0.12.36 + github.com/pterm/pterm v0.12.37 github.com/rs/zerolog v1.26.1 - github.com/soheilhy/cmux v0.1.5 github.com/spf13/cobra v1.3.0 github.com/spf13/viper v1.10.1 github.com/stretchr/testify v1.7.0 github.com/tailscale/hujson v0.0.0-20211215203138-ffd971c5f362 github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e github.com/zsais/go-gin-prometheus v0.1.0 - golang.org/x/crypto v0.0.0-20220210151621-f4118a5b28e2 - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 + golang.org/x/crypto v0.0.0-20220214200702-86341886e292 + golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - google.golang.org/genproto v0.0.0-20220210181026-6fee9acbd336 + google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7 google.golang.org/grpc v1.44.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 google.golang.org/protobuf v1.27.1 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/yaml.v2 v2.4.0 - gorm.io/datatypes v1.0.5 - gorm.io/driver/postgres v1.2.3 - gorm.io/driver/sqlite v1.2.6 - gorm.io/gorm v1.22.5 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b + gorm.io/driver/postgres v1.3.1 + gorm.io/gorm v1.23.1 inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 - tailscale.com v1.20.4 + tailscale.com v1.22.0 ) require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/Microsoft/go-winio v0.5.1 // indirect + github.com/Microsoft/go-winio v0.5.2 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect + github.com/akutz/memconn v0.1.0 // indirect github.com/atomicgo/cursor v0.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029 // indirect github.com/cenkalti/backoff/v4 v4.1.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/containerd/continuity v0.2.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/denisenkom/go-mssqldb v0.12.0 // indirect github.com/docker/cli v20.10.12+incompatible // indirect github.com/docker/docker v20.10.12+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/ghodss/yaml v1.0.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/glebarez/go-sqlite v1.14.8 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/validator/v10 v10.10.0 // indirect @@ -70,6 +72,7 @@ require ( github.com/google/go-github v17.0.0+incompatible // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/gookit/color v1.5.0 // indirect github.com/hashicorp/go-version v1.4.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect @@ -92,13 +95,14 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/lib/pq v1.10.3 // indirect - github.com/magiconair/properties v1.8.5 // indirect + github.com/magiconair/properties v1.8.6 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-sqlite3 v1.14.11 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect + github.com/mitchellh/go-ps v1.0.0 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -112,6 +116,7 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect github.com/sirupsen/logrus v1.8.1 // indirect @@ -120,7 +125,7 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.2.0 // indirect - github.com/ugorji/go/codec v1.2.6 // indirect + github.com/ugorji/go/codec v1.2.7 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect @@ -128,14 +133,17 @@ require ( go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect go4.org/mem v0.0.0-20210711025021-927187094b94 // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // indirect - golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect - golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect + golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect + golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect + golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect google.golang.org/appengine v1.6.7 // indirect gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect - gorm.io/driver/mysql v1.2.3 // indirect + modernc.org/libc v1.14.5 // indirect + modernc.org/mathutil v1.4.1 // indirect + modernc.org/memory v1.0.5 // indirect + modernc.org/sqlite v1.14.7 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 28145899..6d254a25 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,3 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= @@ -40,7 +39,6 @@ cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM7 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU= cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= @@ -56,6 +54,9 @@ contrib.go.opencensus.io/exporter/ocagent v0.7.0/go.mod h1:IshRmMJBhDfFj5Y67nVhM dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AlecAivazis/survey/v2 v2.3.2 h1:TqTB+aDDCLYhf9/bD2TwSO8u8jDSmMUd2SUVO4gCnU8= github.com/AlecAivazis/survey/v2 v2.3.2/go.mod h1:TH2kPCDU3Kqq7pLbnCWwZXDBjnhZtmsCle5EiYDJ2fg= +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= +github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= @@ -63,9 +64,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= -github.com/MarvinJWendt/testza v0.2.1 h1:eitywm1lzygA2KCyn55jFVdOaXj5I9LeOsLNeifd2Kw= github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k= @@ -74,19 +73,17 @@ github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzX github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A= +github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -94,20 +91,14 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/atomicgo/cursor v0.0.1 h1:xdogsqa6YYlLfM+GyClC/Lchf7aiMerFiZQn7soTOoU= github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -116,18 +107,13 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/bufbuild/buf v0.37.0/go.mod h1:lQ1m2HkIaGOFba6w/aC3KYBHhKEOESP3gaAEpS3dAFM= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029 h1:POmUHfxXdeyM8Aomg4tKDcwATCFuW+cYLkj6pwsw9pc= +github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029/go.mod h1:Rpr5n9cGHYdM3S3IK8ROSUUUYjQOu+MSUCZDcJbYWi8= github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo= github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -141,7 +127,6 @@ github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJ github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -155,13 +140,9 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.1.0 h1:UFRRY5JemiAhPZrr/uE0n8fMTLcZsUvySPr1+D7pgr8= -github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= github.com/containerd/continuity v0.2.2 h1:QSqfxcn8c+12slxwu00AtzXrsami0MJb/MQs9lOLHLA= github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -171,11 +152,9 @@ github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbr github.com/coreos/go-oidc/v3 v3.1.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -190,38 +169,27 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D0yGjAk= github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/denisenkom/go-mssqldb v0.10.0 h1:QykgLZBorFE95+gO3u9esLd0BmbvpWp0/waNNZfHBM8= -github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA= +github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.1-0.20200107013213-dc14462fd587+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/docker/cli v20.10.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v20.10.8+incompatible h1:/zO/6y9IOpcehE49yMRTV9ea0nBpb8OeqSskXLNfH1E= -github.com/docker/cli v20.10.8+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/cli v20.10.11+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v20.10.12+incompatible h1:lZlz0uzG+GH+c0plStMUdF/qk3ppmgnswpR5EbqzVGA= github.com/docker/cli v20.10.12+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.8+incompatible h1:RVqD337BgQicVCzYrrlhLDWhq6OAD2PJDUg2LsEUvKM= -github.com/docker/docker v20.10.8+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.12+incompatible h1:CEeNmFM0QZIsJCZKMkZx0ZcahTiewkrgiwfYD+dfl1U= github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/efekarakus/termcolor v1.0.1 h1:YAKFO3bnLrqZGTWyNLcYoSIAQFKVOmbqmDnwsU/znzg= github.com/efekarakus/termcolor v1.0.1/go.mod h1:AitrZNrE4nPO538fRsqf+p0WgLdAsGN5pUNrHEPsEMM= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -240,27 +208,26 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA= github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM= -github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= +github.com/glebarez/go-sqlite v1.14.7/go.mod h1:TKAw5tjyB/ocvVht7Xv4772qRAun5CG/xLCEbkDwNUc= +github.com/glebarez/go-sqlite v1.14.8 h1:30RsIS/olgfOMr7SxiCaYhpq50BTteA/CUKaWVOOHYg= +github.com/glebarez/go-sqlite v1.14.8/go.mod h1:gf9QVsKCYMcu+7nd+ZbDqvXnEXEb22qLcqRUQ9XEI34= +github.com/glebarez/sqlite v1.3.5 h1:R9op5nxb9Z10t4VXQSdAVyqRalLhWdLrlaT/iuvOGHI= +github.com/glebarez/sqlite v1.3.5/go.mod h1:ZffEtp/afVhV+jvIzQi8wlYEIkuGAYshr9OPKM/NmQc= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -274,11 +241,8 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-playground/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPgjj221I1QHci2A= -github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= @@ -286,24 +250,20 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.1.0+incompatible h1:sIa2eCvUTwgjbqXrPLfNwUf9S3i3mpH1O1atV+iL/Wk= -github.com/gofrs/uuid v4.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4= +github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -336,7 +296,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -351,7 +310,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= @@ -381,49 +339,37 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw= github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.3.0/go.mod h1:d2gYTOTUQklu06xp0AJYYmRdTVU1VKrqhkYfYag2L08= github.com/grpc-ecosystem/grpc-gateway/v2 v2.4.0/go.mod h1:IOyTYjcIO0rkmnGBfJTL0NJ11exy/Tc2QEuv7hCXp24= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 h1:rgxjzoDmDXw5q8HONgyHhBas4to0/XWRo/gPpJhsUNQ= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0/go.mod h1:qrJPVzv9YlhsrxJc3P/Q85nr0w1lIRikTl4JlhdDH5w= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.3 h1:I8MsauTJQXZ8df8qJvEln0kYNc3bSapuaSsEsnFdEFU= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.3/go.mod h1:lZdb/YAJUSj9OqrCHs2ihjtoO3+xK3G53wTYXFWRGDo= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -443,8 +389,6 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4= github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= @@ -465,8 +409,6 @@ github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKEN github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -474,13 +416,9 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/infobloxopen/atlas-app-toolkit v0.24.1-0.20210416193901-4c7518b07e08/go.mod h1:9BTHnpff654rY1J8KxSUOLJ+ZUDn2Vi3mmk26gQDo1M= -github.com/infobloxopen/protoc-gen-gorm v1.0.1 h1:IjvQ02gZSll+CjpWjxkLqrpxnvKAGfs5dXRJEpfZx2s= -github.com/infobloxopen/protoc-gen-gorm v1.0.1/go.mod h1:gTu86stnDQXwcNqLG9WNJfl3IPUIhxmGNqJ8z4826uo= github.com/infobloxopen/protoc-gen-gorm v1.1.0 h1:l6JKEkqMTFbtoGIfQmh/aOy7KfljgX4ql772LtG4kas= github.com/infobloxopen/protoc-gen-gorm v1.1.0/go.mod h1:ohzLmmFMWQztw2RBHunfjKSCjTPUW4JvbgU1Mdazwxg= -github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= @@ -488,15 +426,9 @@ github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgO github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= -github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.10.0 h1:4EYhlDVEMsJ30nNj0mmgwIUXoq7e9sMJrVC2ED6QlCU= -github.com/jackc/pgconn v1.10.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= github.com/jackc/pgconn v1.11.0 h1:HiHArx4yFbwl91X3qqIHtUFoiIfLNJXCQRsnzkiwwaQ= github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= @@ -508,51 +440,33 @@ github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5W github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns= github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= -github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= -github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= -github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.8.1 h1:9k0IXtdJXHJbyAWQgbWr1lU+MEhPXZz6RIXxfR5oxXs= -github.com/jackc/pgtype v1.8.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgtype v1.9.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.9.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgtype v1.10.0 h1:ILnBWrRMSXGczYvmkYD6PsYyVFUNLTnIUJHHDLmqk38= github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= -github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= -github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= -github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.13.0 h1:JCjhT5vmhMAf/YwBHLvrBn4OGdIQBiFG6ym8Zmdx570= -github.com/jackc/pgx/v4 v4.13.0/go.mod h1:9P4X524sErlaxj0XSGZk7s+LD0eOyu1ZDUrrpznYDF0= -github.com/jackc/pgx/v4 v4.14.0/go.mod h1:jT3ibf/A0ZVCp89rtCIN0zCJxcE74ypROmHEZYsG/j8= +github.com/jackc/pgx/v4 v4.14.1/go.mod h1:RgDuE4Z34o7XE92RpLsvFiOEfrAUT0Xt2KxvX73W06M= github.com/jackc/pgx/v4 v4.15.0 h1:B7dTkXsdILD3MF987WGGCcg+tvLW6bZJdEcqVFeU//w= github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= @@ -563,17 +477,11 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= -github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas= github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -590,10 +498,9 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.14.2 h1:S0OHlFk/Gbon/yauFJ4FfJJF5V0fc5HbBTJazi28pRw= -github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.14.4 h1:eijASRJcobkVtSt81Olfh7JX43osYLwy5krOJo6YEu4= +github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -621,20 +528,17 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.3.1-0.20200116171513-9eb3fc897d6f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg= github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -644,30 +548,23 @@ github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= -github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU= -github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.11 h1:gt+cp9c0XGqe9S/wAHTL3n/7MqY+siPWgWJgqdsFrzQ= github.com/mattn/go-sqlite3 v1.14.11/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= @@ -678,13 +575,14 @@ github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceT github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= +github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= @@ -699,73 +597,41 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.0.3 h1:1hbqejyQWCJBvtKAfdO0b1FmaEf2z/bxnjqbARass5k= -github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runc v1.1.0 h1:O9+X96OcDjkmmZyfaG996kV7yq8HsoU2h1XRRQcefG8= github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/ory/dockertest/v3 v3.7.0 h1:Bijzonc69Ont3OU0a3TWKJ1Rzlh3TsDXP1JrTAkSmsM= -github.com/ory/dockertest/v3 v3.7.0/go.mod h1:PvCCgnP7AfBZeVrzwiUTjZx/IUXlGLC1zQlUQrLIlUE= github.com/ory/dockertest/v3 v3.8.1 h1:vU/8d1We4qIad2YM0kOwRVtnyue7ExvacPiw1yDm17g= github.com/ory/dockertest/v3 v3.8.1/go.mod h1:wSRQ3wmkz+uSARYMk7kVJFDBGm8x5gSxIhI7NDc+BAQ= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/philip-bui/grpc-zerolog v1.0.1 h1:EMacvLRUd2O1K0eWod27ZP5CY1iTNkhBDLSN+Q4JEvA= github.com/philip-bui/grpc-zerolog v1.0.1/go.mod h1:qXbiq/2X4ZUMMshsqlWyTHOcw7ns+GZmlqZZN05ZHcQ= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= @@ -774,35 +640,27 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= @@ -813,13 +671,13 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= -github.com/pterm/pterm v0.12.30 h1:ZfXzqtOJVKZ2Uhd+L5o6jmbO44PH3Mee4mxq303nh1Y= github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU= github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= -github.com/pterm/pterm v0.12.36 h1:Ui5zZj7xA8lXR0CxWXlKGCQMW1cZVUMOS8jEXs6ur/g= -github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/pterm/pterm v0.12.37 h1:QGOyuaDUmY3yTbP0k6i0uPNqNHA9YofEBQDy0tIyKTA= +github.com/pterm/pterm v0.12.37/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -827,30 +685,23 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 h1:Ha8xCaq6ln1a+R91Km45Oq6lPXj2Mla6CRJYcuV2h1w= -github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE= -github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo= github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc= github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -865,45 +716,30 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.8.1 h1:izYHOT71f9iZ7iq37Uqjael60/vYC6vMtzedudZ0zEk= github.com/spf13/afero v1.8.1/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.0.1-0.20201006035406-b97b5ead31f7/go.mod h1:yk5b0mALVusDL5fMM6Rd1wgnoO5jUPhwsQ6LQAJTidQ= -github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= -github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0= github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/spf13/viper v1.9.0 h1:yR6EXjTp0y0cLN8OZg1CRZmOBdI88UcGkhgyJhu6nZk= -github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk= github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -919,25 +755,20 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tailscale/hujson v0.0.0-20211105212140-3a0adc019d83 h1:f7nwzdAHTUUOJjHZuDvLz9CEAlUM228amCRvwzlPvsA= -github.com/tailscale/hujson v0.0.0-20211105212140-3a0adc019d83/go.mod h1:iTDXJsA6A2wNNjurgic2rk+is6uzU4U2NLm4T+edr6M= github.com/tailscale/hujson v0.0.0-20211215203138-ffd971c5f362 h1:xx7EMpWIKUrMMg+QanclF7bj8QTH/XYdQb/eplkmkgw= github.com/tailscale/hujson v0.0.0-20211215203138-ffd971c5f362/go.mod h1:iTDXJsA6A2wNNjurgic2rk+is6uzU4U2NLm4T+edr6M= github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e h1:IWllFTiDjjLIf2oeKxpIUmtiDV5sn71VgeQgg6vcE7k= github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e/go.mod h1:d7u6HkTYKSv5m6MCKkOQlHwaShTMl3HjqSGW3XtVhXM= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= github.com/twitchtv/twirp v7.1.0+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E= -github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= -github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= @@ -962,16 +793,9 @@ github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxt github.com/zsais/go-gin-prometheus v0.1.0 h1:bkLv1XCdzqVgQ36ScgRi09MA2UC1t3tAB6nsfErsGO4= github.com/zsais/go-gin-prometheus v0.1.0/go.mod h1:Slirjzuz8uM8Cw0jmPNqbneoqcUtY2GGjn2bEd4NRLY= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -1010,27 +834,22 @@ golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaE golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8= -golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20220210151621-f4118a5b28e2 h1:XdAboW3BNMv9ocSCOk/u1MFioZGzCNkiJZ19v9Oe3Ig= -golang.org/x/crypto v0.0.0-20220210151621-f4118a5b28e2/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1071,13 +890,11 @@ golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1111,7 +928,6 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -1121,13 +937,13 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211205041911-012df41ee64c h1:7SfqwP5fxEtl/P02w5IhKc86ziJ+A25yFrkVgoy2FT8= -golang.org/x/net v0.0.0-20211205041911-012df41ee64c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1139,15 +955,15 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1163,11 +979,9 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1181,18 +995,15 @@ golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1218,6 +1029,7 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1244,6 +1056,7 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1252,11 +1065,11 @@ golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1274,12 +1087,12 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1334,6 +1147,7 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -1353,7 +1167,6 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1376,7 +1189,6 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= @@ -1389,7 +1201,6 @@ google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUb google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= @@ -1402,7 +1213,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -1464,25 +1274,19 @@ google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247 h1:ZONpjmFT5e+I/0/xE3XXbG5OIvX2hRYzol04MhKBl2E= -google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220210181026-6fee9acbd336 h1:RK2ysGpQApbI6U7xn+ROT2rrm08lE/t8AcGqG8XI1CY= -google.golang.org/genproto v0.0.0-20220210181026-6fee9acbd336/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7 h1:ntPPoHzFW6Xp09ueznmahONZufyoSakK/piXnr2BU3I= +google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -1506,13 +1310,11 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 h1:TLkBREm4nIsEcexnCjgQd5GQWaHcqMzwQV0TX9pq8S0= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY= @@ -1540,15 +1342,9 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -1556,8 +1352,6 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1571,42 +1365,14 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/datatypes v1.0.2 h1:ChZ5VfWGB23qEr1kZosidvG9CF9HIczwoxLhBS7Ebs4= -gorm.io/datatypes v1.0.2/go.mod h1:1O1JVE4grFGcQTOGQbIBitiXUP6Sv84/KZU7eWeUv1k= -gorm.io/datatypes v1.0.5 h1:3vHCfg4Bz8SDx83zE+ASskF+g/j0kWrcKrY9jFUyAl0= -gorm.io/datatypes v1.0.5/go.mod h1:acG/OHGwod+1KrbwPL1t+aavb7jOBOETeyl5M8K5VQs= -gorm.io/driver/mysql v1.1.2 h1:OofcyE2lga734MxwcCW9uB4mWNXMr50uaGRVwQL2B0M= -gorm.io/driver/mysql v1.1.2/go.mod h1:4P/X9vSc3WTrhTLZ259cpFd6xKNYiSSdSZngkSBGIMM= -gorm.io/driver/mysql v1.2.2/go.mod h1:qsiz+XcAyMrS6QY+X3M9R6b/lKM1imKmcuK9kac5LTo= -gorm.io/driver/mysql v1.2.3 h1:cZqzlOfg5Kf1VIdLC1D9hT6Cy9BgxhExLj/2tIgUe7Y= -gorm.io/driver/mysql v1.2.3/go.mod h1:qsiz+XcAyMrS6QY+X3M9R6b/lKM1imKmcuK9kac5LTo= -gorm.io/driver/postgres v1.1.0/go.mod h1:hXQIwafeRjJvUm+OMxcFWyswJ/vevcpPLlGocwAwuqw= -gorm.io/driver/postgres v1.1.1 h1:tWLmqYCyaoh89fi7DhM6QggujrOnmfo3H98AzgNAAu0= -gorm.io/driver/postgres v1.1.1/go.mod h1:tpe2xN7aCst1NUdYyWQyxPtnHC+Zfp6NEux9PXD1OU0= -gorm.io/driver/postgres v1.2.3 h1:f4t0TmNMy9gh3TU2PX+EppoA6YsgFnyq8Ojtddb42To= -gorm.io/driver/postgres v1.2.3/go.mod h1:pJV6RgYQPG47aM1f0QeOzFH9HxQc8JcmAgjRCgS0wjs= -gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw= -gorm.io/driver/sqlite v1.1.5 h1:JU8G59VyKu1x1RMQgjefQnkZjDe9wHc1kARDZPu5dZs= -gorm.io/driver/sqlite v1.1.5/go.mod h1:NpaYMcVKEh6vLJ47VP6T7Weieu4H1Drs3dGD/K6GrGc= -gorm.io/driver/sqlite v1.2.6 h1:SStaH/b+280M7C8vXeZLz/zo9cLQmIGwwj3cSj7p6l4= -gorm.io/driver/sqlite v1.2.6/go.mod h1:gyoX0vHiiwi0g49tv+x2E7l8ksauLK0U/gShcdUsjWY= -gorm.io/driver/sqlserver v1.0.9 h1:P7Dm/BKqsrOjyhRSnLXvG2g1W/eJUgxdrdBwgJw3tEg= -gorm.io/driver/sqlserver v1.0.9/go.mod h1:iBdxY2CepkTt9Q1r84RbZA1qCai300Qlp8kQf9qE9II= -gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gorm.io/gorm v1.21.9/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= -gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= -gorm.io/gorm v1.21.14/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= -gorm.io/gorm v1.21.15 h1:gAyaDoPw0lCyrSFWhBlahbUA1U4P5RViC1uIqoB+1Rk= -gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= -gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= -gorm.io/gorm v1.22.4/go.mod h1:1aeVC+pe9ZmvKZban/gW4QPra7PRoTEssyc922qCAkk= -gorm.io/gorm v1.22.5 h1:lYREBgc02Be/5lSCTuysZZDb6ffL2qrat6fg9CFbvXU= +gorm.io/driver/postgres v1.3.1 h1:Pyv+gg1Gq1IgsLYytj/S2k7ebII3CzEdpqQkPOdH24g= +gorm.io/driver/postgres v1.3.1/go.mod h1:WwvWOuR9unCLpGWCL6Y3JOeBWvbKi6JLhayiVclSZZU= gorm.io/gorm v1.22.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= -gotest.tools/v3 v3.0.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E= +gorm.io/gorm v1.23.1 h1:aj5IlhDzEPsoIyOPtTRVI+SyaN1u6k613sbt4pwbxG0= +gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1616,14 +1382,137 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 h1:acCzuUSQ79tGsM/O50VRFySfMm19IoMKL+sZztZkCxw= inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6/go.mod h1:y3MGhcFMlh0KZPMuXXow8mpjxxAk3yoDNsp4cQz54i8= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= +modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= +modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= +modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= +modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= +modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= +modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= +modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= +modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= +modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= +modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs= +modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8= +modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE= +modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk= +modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w= +modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE= +modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8= +modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc= +modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU= +modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE= +modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk= +modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI= +modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE= +modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg= +modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74= +modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU= +modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU= +modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc= +modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM= +modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ= +modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84= +modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ= +modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY= +modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w= +modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU= +modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko= +modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA= +modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4= +modernc.org/ccgo/v3 v3.14.0/go.mod h1:hBrkiBlUwvr5vV/ZH9YzXIp982jKE8Ek8tR1ytoAL6Q= +modernc.org/ccgo/v3 v3.15.1/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0= +modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0= +modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8= +modernc.org/ccgo/v3 v3.15.12/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I= +modernc.org/ccgo/v3 v3.15.13/go.mod h1:QHtvdpeODlXjdK3tsbpyK+7U9JV4PQsrPGIbtmc0KfY= +modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/ccorpus v1.11.4/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= +modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= +modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= +modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= +modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= +modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= +modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= +modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= +modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= +modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= +modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY= +modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE= +modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg= +modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM= +modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg= +modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo= +modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8= +modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ= +modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA= +modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM= +modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg= +modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE= +modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM= +modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU= +modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw= +modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M= +modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18= +modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8= +modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= +modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0= +modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI= +modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE= +modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY= +modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ= +modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c= +modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI= +modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ= +modernc.org/libc v1.13.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= +modernc.org/libc v1.13.2/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= +modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= +modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34= +modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ= +modernc.org/libc v1.14.5 h1:DAHvwGoVRDZs5iJXnX9RJrgXSsorupCWmJ2ac964Owk= +modernc.org/libc v1.14.5/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak= +modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= +modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14= +modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.14.5/go.mod h1:YyX5Rx0WbXokitdWl2GJIDy4BrPxBP0PwwhpXOHCDLE= +modernc.org/sqlite v1.14.7 h1:A+6rGjtRQbt9SORXfV+hUyXOP3mDf7J5uz+EES/CNPE= +modernc.org/sqlite v1.14.7/go.mod h1:yiCvMv3HblGmzENNIaNtFhfaNIwcla4u2JQEwJPzfEc= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/tcl v1.10.0/go.mod h1:WzWapmP/7dHVhFoyPpEaNSVTL8xtewhouN/cqSJ5A2s= +modernc.org/tcl v1.11.0/go.mod h1:zsTUpbQ+NxQEjOjCUlImDLPv1sG8Ww0qp66ZvyOxCgw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.2.21/go.mod h1:uXrObx4pGqXWIMliC5MiKuwAyMrltzwpteOFUP1PWCc= +modernc.org/z v1.3.0/go.mod h1:+mvgLH814oDjtATDdT3rs84JnUIpkvAF5B8AVkNlE2g= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= -tailscale.com v1.20.3 h1:C3g2AgmQaOi0YT5dAal9mslugPXMxwj0EXY7YfL2QrA= -tailscale.com v1.20.3/go.mod h1:kjVy3ji2OH5lZhPLIIRacoY3CN4Bo3Yyb2mtoM8nfJ4= -tailscale.com v1.20.4 h1:7cl/Q2Sbo2Jb2dX7zA+Exbbl7DT5UGZ4iGhQ2xj23X0= -tailscale.com v1.20.4/go.mod h1:kjVy3ji2OH5lZhPLIIRacoY3CN4Bo3Yyb2mtoM8nfJ4= +tailscale.com v1.22.0 h1:/a1f6eKEl9vL/wGFP8mkhe7O1zDRGtWa9Ft2rGp5N80= +tailscale.com v1.22.0/go.mod h1:D2zuDnjHT7v4aCt71c4+ytQUUAGpnypW+DoubYLaHjg= diff --git a/grpcv1.go b/grpcv1.go index 9762d22b..647e599d 100644 --- a/grpcv1.go +++ b/grpcv1.go @@ -3,12 +3,10 @@ package headscale import ( "context" - "encoding/json" "time" "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/rs/zerolog/log" - "gorm.io/datatypes" "tailscale.com/tailcfg" ) @@ -159,9 +157,11 @@ func (api headscaleV1APIServer) RegisterMachine( Str("namespace", request.GetNamespace()). Str("machine_key", request.GetKey()). Msg("Registering machine") - machine, err := api.h.RegisterMachine( + + machine, err := api.h.RegisterMachineFromAuthCallback( request.GetKey(), request.GetNamespace(), + RegisterMethodCLI, ) if err != nil { return nil, err @@ -232,15 +232,6 @@ func (api headscaleV1APIServer) ListMachines( return nil, err } - sharedMachines, err := api.h.ListSharedMachinesInNamespace( - request.GetNamespace(), - ) - if err != nil { - return nil, err - } - - machines = append(machines, sharedMachines...) - response := make([]*v1.Machine, len(machines)) for index, machine := range machines { response[index] = machine.toProto() @@ -262,50 +253,6 @@ func (api headscaleV1APIServer) ListMachines( return &v1.ListMachinesResponse{Machines: response}, nil } -func (api headscaleV1APIServer) ShareMachine( - ctx context.Context, - request *v1.ShareMachineRequest, -) (*v1.ShareMachineResponse, error) { - destinationNamespace, err := api.h.GetNamespace(request.GetNamespace()) - if err != nil { - return nil, err - } - - machine, err := api.h.GetMachineByID(request.GetMachineId()) - if err != nil { - return nil, err - } - - err = api.h.AddSharedMachineToNamespace(machine, destinationNamespace) - if err != nil { - return nil, err - } - - return &v1.ShareMachineResponse{Machine: machine.toProto()}, nil -} - -func (api headscaleV1APIServer) UnshareMachine( - ctx context.Context, - request *v1.UnshareMachineRequest, -) (*v1.UnshareMachineResponse, error) { - destinationNamespace, err := api.h.GetNamespace(request.GetNamespace()) - if err != nil { - return nil, err - } - - machine, err := api.h.GetMachineByID(request.GetMachineId()) - if err != nil { - return nil, err - } - - err = api.h.RemoveSharedMachineFromNamespace(machine, destinationNamespace) - if err != nil { - return nil, err - } - - return &v1.UnshareMachineResponse{Machine: machine.toProto()}, nil -} - func (api headscaleV1APIServer) GetMachineRoute( ctx context.Context, request *v1.GetMachineRouteRequest, @@ -315,13 +262,8 @@ func (api headscaleV1APIServer) GetMachineRoute( return nil, err } - routes, err := machine.RoutesToProto() - if err != nil { - return nil, err - } - return &v1.GetMachineRouteResponse{ - Routes: routes, + Routes: machine.RoutesToProto(), }, nil } @@ -339,13 +281,8 @@ func (api headscaleV1APIServer) EnableMachineRoutes( return nil, err } - routes, err := machine.RoutesToProto() - if err != nil { - return nil, err - } - return &v1.EnableMachineRoutesResponse{ - Routes: routes, + Routes: machine.RoutesToProto(), }, nil } @@ -432,13 +369,6 @@ func (api headscaleV1APIServer) DebugCreateMachine( Hostname: "DebugTestMachine", } - log.Trace().Caller().Interface("hostinfo", hostinfo).Msg("") - - hostinfoJson, err := json.Marshal(hostinfo) - if err != nil { - return nil, err - } - newMachine := Machine{ MachineKey: request.GetKey(), Name: request.GetName(), @@ -448,14 +378,14 @@ func (api headscaleV1APIServer) DebugCreateMachine( LastSeen: &time.Time{}, LastSuccessfulUpdate: &time.Time{}, - HostInfo: datatypes.JSON(hostinfoJson), + HostInfo: HostInfo(hostinfo), } - // log.Trace().Caller().Interface("machine", newMachine).Msg("") - - if err := api.h.db.Create(&newMachine).Error; err != nil { - return nil, err - } + api.h.registrationCache.Set( + request.GetKey(), + newMachine, + registerCacheExpiration, + ) return &v1.DebugCreateMachineResponse{Machine: newMachine.toProto()}, nil } diff --git a/integration_cli_test.go b/integration_cli_test.go index 818be911..7ce0758f 100644 --- a/integration_cli_test.go +++ b/integration_cli_test.go @@ -529,7 +529,7 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { namespace, err := s.createNamespace("machine-namespace") assert.Nil(s.T(), err) - sharedNamespace, err := s.createNamespace("shared-namespace") + secondNamespace, err := s.createNamespace("other-namespace") assert.Nil(s.T(), err) // Randomly generated machine keys @@ -589,7 +589,7 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { assert.Len(s.T(), machines, len(machineKeys)) - // Test list all nodes after added shared + // Test list all nodes after added seconds listAllResult, err := ExecuteCommand( &s.headscale, []string{ @@ -621,20 +621,14 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { assert.Equal(s.T(), "machine-4", listAll[3].Name) assert.Equal(s.T(), "machine-5", listAll[4].Name) - assert.True(s.T(), listAll[0].Registered) - assert.True(s.T(), listAll[1].Registered) - assert.True(s.T(), listAll[2].Registered) - assert.True(s.T(), listAll[3].Registered) - assert.True(s.T(), listAll[4].Registered) - - sharedMachineKeys := []string{ + otherNamespaceMachineKeys := []string{ "b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e", "dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584", } - sharedMachines := make([]*v1.Machine, len(sharedMachineKeys)) + otherNamespaceMachines := make([]*v1.Machine, len(otherNamespaceMachineKeys)) assert.Nil(s.T(), err) - for index, machineKey := range sharedMachineKeys { + for index, machineKey := range otherNamespaceMachineKeys { _, err := ExecuteCommand( &s.headscale, []string{ @@ -642,9 +636,9 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { "debug", "create-node", "--name", - fmt.Sprintf("shared-machine-%d", index+1), + fmt.Sprintf("otherNamespace-machine-%d", index+1), "--namespace", - sharedNamespace.Name, + secondNamespace.Name, "--key", machineKey, "--output", @@ -660,7 +654,7 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { "headscale", "nodes", "--namespace", - sharedNamespace.Name, + secondNamespace.Name, "register", "--key", machineKey, @@ -675,13 +669,13 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { err = json.Unmarshal([]byte(machineResult), &machine) assert.Nil(s.T(), err) - sharedMachines[index] = &machine + otherNamespaceMachines[index] = &machine } - assert.Len(s.T(), sharedMachines, len(sharedMachineKeys)) + assert.Len(s.T(), otherNamespaceMachines, len(otherNamespaceMachineKeys)) - // Test list all nodes after added shared - listAllWithSharedResult, err := ExecuteCommand( + // Test list all nodes after added otherNamespace + listAllWithotherNamespaceResult, err := ExecuteCommand( &s.headscale, []string{ "headscale", @@ -694,31 +688,31 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { ) assert.Nil(s.T(), err) - var listAllWithShared []v1.Machine - err = json.Unmarshal([]byte(listAllWithSharedResult), &listAllWithShared) + var listAllWithotherNamespace []v1.Machine + err = json.Unmarshal( + []byte(listAllWithotherNamespaceResult), + &listAllWithotherNamespace, + ) assert.Nil(s.T(), err) - // All nodes, machines + shared - assert.Len(s.T(), listAllWithShared, 7) + // All nodes, machines + otherNamespace + assert.Len(s.T(), listAllWithotherNamespace, 7) - assert.Equal(s.T(), uint64(6), listAllWithShared[5].Id) - assert.Equal(s.T(), uint64(7), listAllWithShared[6].Id) + assert.Equal(s.T(), uint64(6), listAllWithotherNamespace[5].Id) + assert.Equal(s.T(), uint64(7), listAllWithotherNamespace[6].Id) - assert.Equal(s.T(), "shared-machine-1", listAllWithShared[5].Name) - assert.Equal(s.T(), "shared-machine-2", listAllWithShared[6].Name) + assert.Equal(s.T(), "otherNamespace-machine-1", listAllWithotherNamespace[5].Name) + assert.Equal(s.T(), "otherNamespace-machine-2", listAllWithotherNamespace[6].Name) - assert.True(s.T(), listAllWithShared[5].Registered) - assert.True(s.T(), listAllWithShared[6].Registered) - - // Test list all nodes after added shared - listOnlySharedMachineNamespaceResult, err := ExecuteCommand( + // Test list all nodes after added otherNamespace + listOnlyotherNamespaceMachineNamespaceResult, err := ExecuteCommand( &s.headscale, []string{ "headscale", "nodes", "list", "--namespace", - sharedNamespace.Name, + secondNamespace.Name, "--output", "json", }, @@ -726,23 +720,28 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { ) assert.Nil(s.T(), err) - var listOnlySharedMachineNamespace []v1.Machine + var listOnlyotherNamespaceMachineNamespace []v1.Machine err = json.Unmarshal( - []byte(listOnlySharedMachineNamespaceResult), - &listOnlySharedMachineNamespace, + []byte(listOnlyotherNamespaceMachineNamespaceResult), + &listOnlyotherNamespaceMachineNamespace, ) assert.Nil(s.T(), err) - assert.Len(s.T(), listOnlySharedMachineNamespace, 2) + assert.Len(s.T(), listOnlyotherNamespaceMachineNamespace, 2) - assert.Equal(s.T(), uint64(6), listOnlySharedMachineNamespace[0].Id) - assert.Equal(s.T(), uint64(7), listOnlySharedMachineNamespace[1].Id) + assert.Equal(s.T(), uint64(6), listOnlyotherNamespaceMachineNamespace[0].Id) + assert.Equal(s.T(), uint64(7), listOnlyotherNamespaceMachineNamespace[1].Id) - assert.Equal(s.T(), "shared-machine-1", listOnlySharedMachineNamespace[0].Name) - assert.Equal(s.T(), "shared-machine-2", listOnlySharedMachineNamespace[1].Name) - - assert.True(s.T(), listOnlySharedMachineNamespace[0].Registered) - assert.True(s.T(), listOnlySharedMachineNamespace[1].Registered) + assert.Equal( + s.T(), + "otherNamespace-machine-1", + listOnlyotherNamespaceMachineNamespace[0].Name, + ) + assert.Equal( + s.T(), + "otherNamespace-machine-2", + listOnlyotherNamespaceMachineNamespace[1].Name, + ) // Delete a machines _, err = ExecuteCommand( @@ -786,120 +785,6 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { assert.Nil(s.T(), err) assert.Len(s.T(), listOnlyMachineNamespaceAfterDelete, 4) - - // test: share node - - shareMachineResult, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "share", - "--namespace", - namespace.Name, - "--identifier", - "7", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var shareMachine v1.Machine - err = json.Unmarshal([]byte(shareMachineResult), &shareMachine) - assert.Nil(s.T(), err) - - assert.Equal(s.T(), uint64(7), shareMachine.Id) - - assert.Equal(s.T(), "shared-machine-2", shareMachine.Name) - - assert.True(s.T(), shareMachine.Registered) - - // Test: list main namespace after machine has been shared - listOnlyMachineNamespaceAfterShareResult, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "list", - "--namespace", - namespace.Name, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listOnlyMachineNamespaceAfterShare []v1.Machine - err = json.Unmarshal( - []byte(listOnlyMachineNamespaceAfterShareResult), - &listOnlyMachineNamespaceAfterShare, - ) - assert.Nil(s.T(), err) - - assert.Len(s.T(), listOnlyMachineNamespaceAfterShare, 5) - - assert.Equal(s.T(), uint64(7), listOnlyMachineNamespaceAfterShare[4].Id) - - assert.Equal(s.T(), "shared-machine-2", listOnlyMachineNamespaceAfterShare[4].Name) - - assert.True(s.T(), listOnlyMachineNamespaceAfterShare[4].Registered) - - // test: unshare node - - unshareMachineResult, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "unshare", - "--namespace", - namespace.Name, - "--identifier", - "7", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var unshareMachine v1.Machine - err = json.Unmarshal([]byte(unshareMachineResult), &unshareMachine) - assert.Nil(s.T(), err) - - assert.Equal(s.T(), uint64(7), unshareMachine.Id) - - assert.Equal(s.T(), "shared-machine-2", unshareMachine.Name) - - assert.True(s.T(), unshareMachine.Registered) - - // Test: list main namespace after machine has been shared - listOnlyMachineNamespaceAfterUnshareResult, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "list", - "--namespace", - namespace.Name, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listOnlyMachineNamespaceAfterUnshare []v1.Machine - err = json.Unmarshal( - []byte(listOnlyMachineNamespaceAfterUnshareResult), - &listOnlyMachineNamespaceAfterUnshare, - ) - assert.Nil(s.T(), err) - - assert.Len(s.T(), listOnlyMachineNamespaceAfterUnshare, 4) } func (s *IntegrationCLITestSuite) TestNodeExpireCommand() { @@ -1082,7 +967,6 @@ func (s *IntegrationCLITestSuite) TestRouteCommand() { assert.Equal(s.T(), uint64(1), machine.Id) assert.Equal(s.T(), "route-machine", machine.Name) - assert.True(s.T(), machine.Registered) listAllResult, err := ExecuteCommand( &s.headscale, diff --git a/integration_common_test.go b/integration_common_test.go index de304d0d..70285fc4 100644 --- a/integration_common_test.go +++ b/integration_common_test.go @@ -6,6 +6,7 @@ package headscale import ( "bytes" "fmt" + "strings" "time" "github.com/ory/dockertest/v3" @@ -18,8 +19,15 @@ const DOCKER_EXECUTE_TIMEOUT = 10 * time.Second var ( IpPrefix4 = netaddr.MustParseIPPrefix("100.64.0.0/10") IpPrefix6 = netaddr.MustParseIPPrefix("fd7a:115c:a1e0::/48") + + tailscaleVersions = []string{"1.22.0", "1.20.4", "1.18.2", "1.16.2", "1.14.3", "1.12.3"} ) +type TestNamespace struct { + count int + tailscales map[string]dockertest.Resource +} + type ExecuteCommandConfig struct { timeout time.Duration } @@ -119,3 +127,35 @@ func DockerAllowNetworkAdministration(config *docker.HostConfig) { Target: "/dev/net/tun", }) } + +func getIPs( + tailscales map[string]dockertest.Resource, +) (map[string][]netaddr.IP, error) { + ips := make(map[string][]netaddr.IP) + for hostname, tailscale := range tailscales { + command := []string{"tailscale", "ip"} + + result, err := ExecuteCommand( + &tailscale, + command, + []string{}, + ) + if err != nil { + return nil, err + } + + for _, address := range strings.Split(result, "\n") { + address = strings.TrimSuffix(address, "\n") + if len(address) < 1 { + continue + } + ip, err := netaddr.ParseIP(address) + if err != nil { + return nil, err + } + ips[hostname] = append(ips[hostname], ip) + } + } + + return ips, nil +} diff --git a/integration_embedded_derp_test.go b/integration_embedded_derp_test.go new file mode 100644 index 00000000..a1737173 --- /dev/null +++ b/integration_embedded_derp_test.go @@ -0,0 +1,396 @@ +//go:build integration + +package headscale + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "path" + "strings" + "sync" + "testing" + "time" + + v1 "github.com/juanfont/headscale/gen/go/headscale/v1" + "github.com/ory/dockertest/v3" + "github.com/ory/dockertest/v3/docker" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + + "github.com/ccding/go-stun/stun" +) + +const ( + headscaleHostname = "headscale-derp" + namespaceName = "derpnamespace" + totalContainers = 3 +) + +type IntegrationDERPTestSuite struct { + suite.Suite + stats *suite.SuiteInformation + + pool dockertest.Pool + networks map[int]dockertest.Network // so we keep the containers isolated + headscale dockertest.Resource + + tailscales map[string]dockertest.Resource + joinWaitGroup sync.WaitGroup +} + +func TestDERPIntegrationTestSuite(t *testing.T) { + s := new(IntegrationDERPTestSuite) + + s.tailscales = make(map[string]dockertest.Resource) + s.networks = make(map[int]dockertest.Network) + + suite.Run(t, s) + + // HandleStats, which allows us to check if we passed and save logs + // is called after TearDown, so we cannot tear down containers before + // we have potentially saved the logs. + for _, tailscale := range s.tailscales { + if err := s.pool.Purge(&tailscale); err != nil { + log.Printf("Could not purge resource: %s\n", err) + } + } + + if !s.stats.Passed() { + err := s.saveLog(&s.headscale, "test_output") + if err != nil { + log.Printf("Could not save log: %s\n", err) + } + } + if err := s.pool.Purge(&s.headscale); err != nil { + log.Printf("Could not purge resource: %s\n", err) + } + + for _, network := range s.networks { + if err := network.Close(); err != nil { + log.Printf("Could not close network: %s\n", err) + } + } +} + +func (s *IntegrationDERPTestSuite) SetupSuite() { + if ppool, err := dockertest.NewPool(""); err == nil { + s.pool = *ppool + } else { + log.Fatalf("Could not connect to docker: %s", err) + } + + for i := 0; i < totalContainers; i++ { + if pnetwork, err := s.pool.CreateNetwork(fmt.Sprintf("headscale-derp-%d", i)); err == nil { + s.networks[i] = *pnetwork + } else { + log.Fatalf("Could not create network: %s", err) + } + } + + headscaleBuildOptions := &dockertest.BuildOptions{ + Dockerfile: "Dockerfile", + ContextDir: ".", + } + + currentPath, err := os.Getwd() + if err != nil { + log.Fatalf("Could not determine current path: %s", err) + } + + headscaleOptions := &dockertest.RunOptions{ + Name: headscaleHostname, + Mounts: []string{ + fmt.Sprintf("%s/integration_test/etc_embedded_derp:/etc/headscale", currentPath), + }, + Cmd: []string{"headscale", "serve"}, + ExposedPorts: []string{"8443/tcp", "3478/udp"}, + PortBindings: map[docker.Port][]docker.PortBinding{ + "8443/tcp": {{HostPort: "8443"}}, + "3478/udp": {{HostPort: "3478"}}, + }, + } + + log.Println("Creating headscale container") + if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil { + s.headscale = *pheadscale + } else { + log.Fatalf("Could not start resource: %s", err) + } + log.Println("Created headscale container to test DERP") + + log.Println("Creating tailscale containers") + + for i := 0; i < totalContainers; i++ { + version := tailscaleVersions[i%len(tailscaleVersions)] + hostname, container := s.tailscaleContainer( + fmt.Sprint(i), + version, + s.networks[i], + ) + s.tailscales[hostname] = *container + } + + log.Println("Waiting for headscale to be ready") + hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8443/tcp")) + + if err := s.pool.Retry(func() error { + url := fmt.Sprintf("https://%s/health", hostEndpoint) + insecureTransport := http.DefaultTransport.(*http.Transport).Clone() + insecureTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + client := &http.Client{Transport: insecureTransport} + resp, err := client.Get(url) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("status code not OK") + } + + return nil + }); err != nil { + // TODO(kradalby): If we cannot access headscale, or any other fatal error during + // test setup, we need to abort and tear down. However, testify does not seem to + // support that at the moment: + // https://github.com/stretchr/testify/issues/849 + return // fmt.Errorf("Could not connect to headscale: %s", err) + } + log.Println("headscale container is ready") + + log.Printf("Creating headscale namespace: %s\n", namespaceName) + result, err := ExecuteCommand( + &s.headscale, + []string{"headscale", "namespaces", "create", namespaceName}, + []string{}, + ) + log.Println("headscale create namespace result: ", result) + assert.Nil(s.T(), err) + + log.Printf("Creating pre auth key for %s\n", namespaceName) + preAuthResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "--namespace", + namespaceName, + "preauthkeys", + "create", + "--reusable", + "--expiration", + "24h", + "--output", + "json", + }, + []string{"LOG_LEVEL=error"}, + ) + assert.Nil(s.T(), err) + + var preAuthKey v1.PreAuthKey + err = json.Unmarshal([]byte(preAuthResult), &preAuthKey) + assert.Nil(s.T(), err) + assert.True(s.T(), preAuthKey.Reusable) + + headscaleEndpoint := fmt.Sprintf("https://headscale:%s", s.headscale.GetPort("8443/tcp")) + + log.Printf( + "Joining tailscale containers to headscale at %s\n", + headscaleEndpoint, + ) + for hostname, tailscale := range s.tailscales { + s.joinWaitGroup.Add(1) + go s.Join(headscaleEndpoint, preAuthKey.Key, hostname, tailscale) + } + + s.joinWaitGroup.Wait() + + // The nodes need a bit of time to get their updated maps from headscale + // TODO: See if we can have a more deterministic wait here. + time.Sleep(60 * time.Second) +} + +func (s *IntegrationDERPTestSuite) Join( + endpoint, key, hostname string, + tailscale dockertest.Resource, +) { + defer s.joinWaitGroup.Done() + + command := []string{ + "tailscale", + "up", + "-login-server", + endpoint, + "--authkey", + key, + "--hostname", + hostname, + } + + log.Println("Join command:", command) + log.Printf("Running join command for %s\n", hostname) + _, err := ExecuteCommand( + &tailscale, + command, + []string{}, + ) + assert.Nil(s.T(), err) + log.Printf("%s joined\n", hostname) +} + +func (s *IntegrationDERPTestSuite) tailscaleContainer(identifier, version string, network dockertest.Network, +) (string, *dockertest.Resource) { + tailscaleBuildOptions := &dockertest.BuildOptions{ + Dockerfile: "Dockerfile.tailscale", + ContextDir: ".", + BuildArgs: []docker.BuildArg{ + { + Name: "TAILSCALE_VERSION", + Value: version, + }, + }, + } + hostname := fmt.Sprintf( + "tailscale-%s-%s", + strings.Replace(version, ".", "-", -1), + identifier, + ) + tailscaleOptions := &dockertest.RunOptions{ + Name: hostname, + Networks: []*dockertest.Network{&network}, + Cmd: []string{ + "tailscaled", "--tun=tsdev", + }, + + // expose the host IP address, so we can access it from inside the container + ExtraHosts: []string{"host.docker.internal:host-gateway", "headscale:host-gateway"}, + } + + pts, err := s.pool.BuildAndRunWithBuildOptions( + tailscaleBuildOptions, + tailscaleOptions, + DockerRestartPolicy, + DockerAllowLocalIPv6, + DockerAllowNetworkAdministration, + ) + if err != nil { + log.Fatalf("Could not start resource: %s", err) + } + log.Printf("Created %s container\n", hostname) + + return hostname, pts +} + +func (s *IntegrationDERPTestSuite) TearDownSuite() { +} + +func (s *IntegrationDERPTestSuite) HandleStats( + suiteName string, + stats *suite.SuiteInformation, +) { + s.stats = stats +} + +func (s *IntegrationDERPTestSuite) saveLog( + resource *dockertest.Resource, + basePath string, +) error { + err := os.MkdirAll(basePath, os.ModePerm) + if err != nil { + return err + } + + var stdout bytes.Buffer + var stderr bytes.Buffer + + err = s.pool.Client.Logs( + docker.LogsOptions{ + Context: context.TODO(), + Container: resource.Container.ID, + OutputStream: &stdout, + ErrorStream: &stderr, + Tail: "all", + RawTerminal: false, + Stdout: true, + Stderr: true, + Follow: false, + Timestamps: false, + }, + ) + if err != nil { + return err + } + + log.Printf("Saving logs for %s to %s\n", resource.Container.Name, basePath) + + err = ioutil.WriteFile( + path.Join(basePath, resource.Container.Name+".stdout.log"), + []byte(stdout.String()), + 0o644, + ) + if err != nil { + return err + } + + err = ioutil.WriteFile( + path.Join(basePath, resource.Container.Name+".stderr.log"), + []byte(stdout.String()), + 0o644, + ) + if err != nil { + return err + } + + return nil +} + +func (s *IntegrationDERPTestSuite) TestPingAllPeersByHostname() { + ips, err := getIPs(s.tailscales) + assert.Nil(s.T(), err) + for hostname, tailscale := range s.tailscales { + for peername := range ips { + if peername == hostname { + continue + } + s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) { + command := []string{ + "tailscale", "ping", + "--timeout=10s", + "--c=5", + "--until-direct=false", + peername, + } + + log.Printf( + "Pinging using hostname from %s to %s\n", + hostname, + peername, + ) + log.Println(command) + result, err := ExecuteCommand( + &tailscale, + command, + []string{}, + ) + assert.Nil(t, err) + log.Printf("Result for %s: %s\n", hostname, result) + assert.Contains(t, result, "via DERP(headscale)") + }) + } + } +} + +func (s *IntegrationDERPTestSuite) TestDERPSTUN() { + headscaleSTUNAddr := fmt.Sprintf("localhost:%s", s.headscale.GetPort("3478/udp")) + client := stun.NewClient() + client.SetVerbose(true) + client.SetVVerbose(true) + client.SetServerAddr(headscaleSTUNAddr) + _, _, err := client.Discover() + assert.Nil(s.T(), err) +} diff --git a/integration_test.go b/integration_test.go index 43845b61..1649f322 100644 --- a/integration_test.go +++ b/integration_test.go @@ -15,6 +15,7 @@ import ( "os" "path" "strings" + "sync" "testing" "time" @@ -28,13 +29,6 @@ import ( "tailscale.com/ipn/ipnstate" ) -var tailscaleVersions = []string{"1.20.4", "1.18.2", "1.16.2", "1.14.3", "1.12.3"} - -type TestNamespace struct { - count int - tailscales map[string]dockertest.Resource -} - type IntegrationTestSuite struct { suite.Suite stats *suite.SuiteInformation @@ -44,17 +38,19 @@ type IntegrationTestSuite struct { headscale dockertest.Resource namespaces map[string]TestNamespace + + joinWaitGroup sync.WaitGroup } func TestIntegrationTestSuite(t *testing.T) { s := new(IntegrationTestSuite) s.namespaces = map[string]TestNamespace{ - "main": { - count: 20, + "thisspace": { + count: 15, tailscales: make(map[string]dockertest.Resource), }, - "shared": { + "otherspace": { count: 5, tailscales: make(map[string]dockertest.Resource), }, @@ -118,7 +114,7 @@ func (s *IntegrationTestSuite) saveLog( return err } - fmt.Printf("Saving logs for %s to %s\n", resource.Container.Name, basePath) + log.Printf("Saving logs for %s to %s\n", resource.Container.Name, basePath) err = ioutil.WriteFile( path.Join(basePath, resource.Container.Name+".stdout.log"), @@ -141,6 +137,34 @@ func (s *IntegrationTestSuite) saveLog( return nil } +func (s *IntegrationTestSuite) Join( + endpoint, key, hostname string, + tailscale dockertest.Resource, +) { + defer s.joinWaitGroup.Done() + + command := []string{ + "tailscale", + "up", + "-login-server", + endpoint, + "--authkey", + key, + "--hostname", + hostname, + } + + log.Println("Join command:", command) + log.Printf("Running join command for %s\n", hostname) + _, err := ExecuteCommand( + &tailscale, + command, + []string{}, + ) + assert.Nil(s.T(), err) + log.Printf("%s joined\n", hostname) +} + func (s *IntegrationTestSuite) tailscaleContainer( namespace, identifier, version string, ) (string, *dockertest.Resource) { @@ -178,7 +202,7 @@ func (s *IntegrationTestSuite) tailscaleContainer( if err != nil { log.Fatalf("Could not start resource: %s", err) } - fmt.Printf("Created %s container\n", hostname) + log.Printf("Created %s container\n", hostname) return hostname, pts } @@ -221,15 +245,15 @@ func (s *IntegrationTestSuite) SetupSuite() { Cmd: []string{"headscale", "serve"}, } - fmt.Println("Creating headscale container") + log.Println("Creating headscale container") if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil { s.headscale = *pheadscale } else { log.Fatalf("Could not start resource: %s", err) } - fmt.Println("Created headscale container") + log.Println("Created headscale container") - fmt.Println("Creating tailscale containers") + log.Println("Creating tailscale containers") for namespace, scales := range s.namespaces { for i := 0; i < scales.count; i++ { version := tailscaleVersions[i%len(tailscaleVersions)] @@ -243,7 +267,7 @@ func (s *IntegrationTestSuite) SetupSuite() { } } - fmt.Println("Waiting for headscale to be ready") + log.Println("Waiting for headscale to be ready") hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8080/tcp")) if err := s.pool.Retry(func() error { @@ -266,19 +290,19 @@ func (s *IntegrationTestSuite) SetupSuite() { // https://github.com/stretchr/testify/issues/849 return // fmt.Errorf("Could not connect to headscale: %s", err) } - fmt.Println("headscale container is ready") + log.Println("headscale container is ready") for namespace, scales := range s.namespaces { - fmt.Printf("Creating headscale namespace: %s\n", namespace) + log.Printf("Creating headscale namespace: %s\n", namespace) result, err := ExecuteCommand( &s.headscale, []string{"headscale", "namespaces", "create", namespace}, []string{}, ) - fmt.Println("headscale create namespace result: ", result) + log.Println("headscale create namespace result: ", result) assert.Nil(s.T(), err) - fmt.Printf("Creating pre auth key for %s\n", namespace) + log.Printf("Creating pre auth key for %s\n", namespace) preAuthResult, err := ExecuteCommand( &s.headscale, []string{ @@ -304,33 +328,16 @@ func (s *IntegrationTestSuite) SetupSuite() { headscaleEndpoint := "http://headscale:8080" - fmt.Printf( + log.Printf( "Joining tailscale containers to headscale at %s\n", headscaleEndpoint, ) for hostname, tailscale := range scales.tailscales { - command := []string{ - "tailscale", - "up", - "-login-server", - headscaleEndpoint, - "--authkey", - preAuthKey.Key, - "--hostname", - hostname, - } - - fmt.Println("Join command:", command) - fmt.Printf("Running join command for %s\n", hostname) - result, err := ExecuteCommand( - &tailscale, - command, - []string{}, - ) - fmt.Println("tailscale result: ", result) - assert.Nil(s.T(), err) - fmt.Printf("%s joined\n", hostname) + s.joinWaitGroup.Add(1) + go s.Join(headscaleEndpoint, preAuthKey.Key, hostname, tailscale) } + + s.joinWaitGroup.Wait() } // The nodes need a bit of time to get their updated maps from headscale @@ -350,7 +357,7 @@ func (s *IntegrationTestSuite) HandleStats( func (s *IntegrationTestSuite) TestListNodes() { for namespace, scales := range s.namespaces { - fmt.Println("Listing nodes") + log.Println("Listing nodes") result, err := ExecuteCommand( &s.headscale, []string{"headscale", "--namespace", namespace, "nodes", "list"}, @@ -358,7 +365,7 @@ func (s *IntegrationTestSuite) TestListNodes() { ) assert.Nil(s.T(), err) - fmt.Printf("List nodes: \n%s\n", result) + log.Printf("List nodes: \n%s\n", result) // Chck that the correct count of host is present in node list lines := strings.Split(result, "\n") @@ -381,7 +388,7 @@ func (s *IntegrationTestSuite) TestGetIpAddresses() { s.T().Run(hostname, func(t *testing.T) { assert.NotNil(t, ip) - fmt.Printf("IP for %s: %s\n", hostname, ip) + log.Printf("IP for %s: %s\n", hostname, ip) // c.Assert(ip.Valid(), check.IsTrue) assert.True(t, ip.Is4() || ip.Is6()) @@ -410,7 +417,7 @@ func (s *IntegrationTestSuite) TestGetIpAddresses() { // s.T().Run(hostname, func(t *testing.T) { // command := []string{"tailscale", "status", "--json"} // -// fmt.Printf("Getting status for %s\n", hostname) +// log.Printf("Getting status for %s\n", hostname) // result, err := ExecuteCommand( // &tailscale, // command, @@ -452,6 +459,7 @@ func getIPsfromIPNstate(status ipnstate.Status) []netaddr.IP { return ips } +// TODO: Adopt test for cross communication between namespaces func (s *IntegrationTestSuite) TestPingAllPeersByAddress() { for _, scales := range s.namespaces { ips, err := getIPs(scales.tailscales) @@ -476,7 +484,7 @@ func (s *IntegrationTestSuite) TestPingAllPeersByAddress() { ip.String(), } - fmt.Printf( + log.Printf( "Pinging from %s to %s (%s)\n", hostname, peername, @@ -488,7 +496,7 @@ func (s *IntegrationTestSuite) TestPingAllPeersByAddress() { []string{}, ) assert.Nil(t, err) - fmt.Printf("Result for %s: %s\n", hostname, result) + log.Printf("Result for %s: %s\n", hostname, result) assert.Contains(t, result, "pong") }) } @@ -497,111 +505,6 @@ func (s *IntegrationTestSuite) TestPingAllPeersByAddress() { } } -func (s *IntegrationTestSuite) TestSharedNodes() { - main := s.namespaces["main"] - shared := s.namespaces["shared"] - - result, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "list", - "--output", - "json", - "--namespace", - "shared", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var machineList []v1.Machine - err = json.Unmarshal([]byte(result), &machineList) - assert.Nil(s.T(), err) - - for _, machine := range machineList { - result, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "share", - "--identifier", fmt.Sprint(machine.Id), - "--namespace", "main", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - fmt.Println("Shared node with result: ", result) - } - - result, err = ExecuteCommand( - &s.headscale, - []string{"headscale", "nodes", "list", "--namespace", "main"}, - []string{}, - ) - assert.Nil(s.T(), err) - fmt.Println("Nodelist after sharing", result) - - // Chck that the correct count of host is present in node list - lines := strings.Split(result, "\n") - assert.Equal(s.T(), len(main.tailscales)+len(shared.tailscales), len(lines)-2) - - for hostname := range main.tailscales { - assert.Contains(s.T(), result, hostname) - } - - for hostname := range shared.tailscales { - assert.Contains(s.T(), result, hostname) - } - - // TODO(juanfont): We have to find out why do we need to wait - time.Sleep(100 * time.Second) // Wait for the nodes to receive updates - - sharedIps, err := getIPs(shared.tailscales) - assert.Nil(s.T(), err) - - for hostname, tailscale := range main.tailscales { - for peername, peerIPs := range sharedIps { - for i, ip := range peerIPs { - // We currently cant ping ourselves, so skip that. - if peername == hostname { - continue - } - s.T(). - Run(fmt.Sprintf("%s-%s-%d", hostname, peername, i), func(t *testing.T) { - // We are only interested in "direct ping" which means what we - // might need a couple of more attempts before reaching the node. - command := []string{ - "tailscale", "ping", - "--timeout=15s", - "--c=20", - "--until-direct=true", - ip.String(), - } - - fmt.Printf( - "Pinging from %s to %s (%s)\n", - hostname, - peername, - ip, - ) - result, err := ExecuteCommand( - &tailscale, - command, - []string{}, - ) - assert.Nil(t, err) - fmt.Printf("Result for %s: %s\n", hostname, result) - assert.Contains(t, result, "pong") - }) - } - } - } -} - func (s *IntegrationTestSuite) TestTailDrop() { for _, scales := range s.namespaces { ips, err := getIPs(scales.tailscales) @@ -616,6 +519,7 @@ func (s *IntegrationTestSuite) TestTailDrop() { } time.Sleep(sleepInverval) } + return } @@ -638,7 +542,7 @@ func (s *IntegrationTestSuite) TestTailDrop() { fmt.Sprintf("%s:", peername), } retry(10, 1*time.Second, func() error { - fmt.Printf( + log.Printf( "Sending file from %s to %s\n", hostname, peername, @@ -677,7 +581,7 @@ func (s *IntegrationTestSuite) TestTailDrop() { "ls", fmt.Sprintf("/tmp/file_from_%s", peername), } - fmt.Printf( + log.Printf( "Checking file in %s (%s) from %s (%s)\n", hostname, ips[hostname], @@ -690,7 +594,7 @@ func (s *IntegrationTestSuite) TestTailDrop() { []string{}, ) assert.Nil(t, err) - fmt.Printf("Result for %s: %s\n", peername, result) + log.Printf("Result for %s: %s\n", peername, result) assert.Equal( t, fmt.Sprintf("/tmp/file_from_%s\n", peername), @@ -720,7 +624,7 @@ func (s *IntegrationTestSuite) TestPingAllPeersByHostname() { fmt.Sprintf("%s.%s.headscale.net", peername, namespace), } - fmt.Printf( + log.Printf( "Pinging using hostname from %s to %s\n", hostname, peername, @@ -731,7 +635,7 @@ func (s *IntegrationTestSuite) TestPingAllPeersByHostname() { []string{}, ) assert.Nil(t, err) - fmt.Printf("Result for %s: %s\n", hostname, result) + log.Printf("Result for %s: %s\n", hostname, result) assert.Contains(t, result, "pong") }) } @@ -754,7 +658,7 @@ func (s *IntegrationTestSuite) TestMagicDNS() { fmt.Sprintf("%s.%s.headscale.net", peername, namespace), } - fmt.Printf( + log.Printf( "Resolving name %s from %s\n", peername, hostname, @@ -765,7 +669,7 @@ func (s *IntegrationTestSuite) TestMagicDNS() { []string{}, ) assert.Nil(t, err) - fmt.Printf("Result for %s: %s\n", hostname, result) + log.Printf("Result for %s: %s\n", hostname, result) for _, ip := range ips { assert.Contains(t, result, ip.String()) @@ -776,38 +680,6 @@ func (s *IntegrationTestSuite) TestMagicDNS() { } } -func getIPs( - tailscales map[string]dockertest.Resource, -) (map[string][]netaddr.IP, error) { - ips := make(map[string][]netaddr.IP) - for hostname, tailscale := range tailscales { - command := []string{"tailscale", "ip"} - - result, err := ExecuteCommand( - &tailscale, - command, - []string{}, - ) - if err != nil { - return nil, err - } - - for _, address := range strings.Split(result, "\n") { - address = strings.TrimSuffix(address, "\n") - if len(address) < 1 { - continue - } - ip, err := netaddr.ParseIP(address) - if err != nil { - return nil, err - } - ips[hostname] = append(ips[hostname], ip) - } - } - - return ips, nil -} - func getAPIURLs( tailscales map[string]dockertest.Resource, ) (map[netaddr.IP]string, error) { diff --git a/integration_test/etc/config.yaml b/integration_test/etc/config.yaml index 63af7ebb..f055b4ca 100644 --- a/integration_test/etc/config.yaml +++ b/integration_test/etc/config.yaml @@ -14,6 +14,7 @@ dns_config: db_path: /tmp/integration_test_db.sqlite3 private_key_path: private.key listen_addr: 0.0.0.0:8080 +metrics_listen_addr: 127.0.0.1:9090 server_url: http://headscale:8080 derp: diff --git a/integration_test/etc_embedded_derp/config.yaml b/integration_test/etc_embedded_derp/config.yaml new file mode 100644 index 00000000..1531d347 --- /dev/null +++ b/integration_test/etc_embedded_derp/config.yaml @@ -0,0 +1,29 @@ +log_level: trace +acl_policy_path: "" +db_type: sqlite3 +ephemeral_node_inactivity_timeout: 30m +ip_prefixes: + - fd7a:115c:a1e0::/48 + - 100.64.0.0/10 +dns_config: + base_domain: headscale.net + magic_dns: true + domains: [] + nameservers: + - 1.1.1.1 +db_path: /tmp/integration_test_db.sqlite3 +private_key_path: private.key +listen_addr: 0.0.0.0:8443 +server_url: https://headscale:8443 +tls_cert_path: "/etc/headscale/tls/server.crt" +tls_key_path: "/etc/headscale/tls/server.key" +tls_client_auth_mode: disabled +derp: + server: + enabled: true + region_id: 999 + region_code: "headscale" + region_name: "Headscale Embedded DERP" + stun: + enabled: true + listen_addr: "0.0.0.0:3478" diff --git a/integration_test/etc_embedded_derp/tls/server.crt b/integration_test/etc_embedded_derp/tls/server.crt new file mode 100644 index 00000000..95556495 --- /dev/null +++ b/integration_test/etc_embedded_derp/tls/server.crt @@ -0,0 +1,22 @@ + +-----BEGIN CERTIFICATE----- +MIIC8jCCAdqgAwIBAgIULbu+UbSTMG/LtxooLLh7BgSEyqEwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJaGVhZHNjYWxlMCAXDTIyMDMwNTE2NDgwM1oYDzI1MjEx +MTA0MTY0ODAzWjAUMRIwEAYDVQQDDAloZWFkc2NhbGUwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDqcfpToLZUF0rlNwXkkt3lbyw4Cl4TJdx36o2PKaOK +U+tze/IjRsCWeMwrcR1o9TNZcxsD+c2J48D1WATuQJlMeg+2UJXGaTGRKkkbPMy3 +5m7AFf/Q16UEOgm2NYjZaQ8faRGIMYURG/6sXmNeETJvBixpBev9yKJuVXgqHNS4 +NpEkNwdOCuAZXrmw0HCbiusawJOay4tFvhH14rav8Uimonl8UTNVXufMzyUOuoaQ +TGflmzYX3hIoswRnTPlIWFoqObvx2Q8H+of3uQJXy0m8I6OrIoXLNxnqYMfFls79 +9SYgVc2jPsCbh5fwyRbx2Hof7sIZ1K/mNgxJRG1E3ZiLAgMBAAGjOjA4MBQGA1Ud +EQQNMAuCCWhlYWRzY2FsZTALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUH +AwEwDQYJKoZIhvcNAQELBQADggEBANGlVN7NCsJaKz0k0nhlRGK+tcxn2p1PXN/i +Iy+JX8ahixPC4ocRwOhrXgb390ZXLLwq08HrWYRB/Wi1VUzCp5d8dVxvrR43dJ+v +L2EOBiIKgcu2C3pWW1qRR46/EoXUU9kSH2VNBvIhNufi32kEOidoDzxtQf6qVCoF +guUt1JkAqrynv1UvR/2ZRM/WzM/oJ8qfECwrwDxyYhkqU5Z5jCWg0C6kPIBvNdzt +B0eheWS+ZxVwkePTR4e17kIafwknth3lo+orxVrq/xC+OVM1bGrt2ZyD64ZvEqQl +w6kgbzBdLScAQptWOFThwhnJsg0UbYKimZsnYmjVEuN59TJv92M= +-----END CERTIFICATE----- + +(Expires on Nov 4 16:48:03 2521 GMT) + diff --git a/integration_test/etc_embedded_derp/tls/server.key b/integration_test/etc_embedded_derp/tls/server.key new file mode 100644 index 00000000..8a2df34b --- /dev/null +++ b/integration_test/etc_embedded_derp/tls/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDqcfpToLZUF0rl +NwXkkt3lbyw4Cl4TJdx36o2PKaOKU+tze/IjRsCWeMwrcR1o9TNZcxsD+c2J48D1 +WATuQJlMeg+2UJXGaTGRKkkbPMy35m7AFf/Q16UEOgm2NYjZaQ8faRGIMYURG/6s +XmNeETJvBixpBev9yKJuVXgqHNS4NpEkNwdOCuAZXrmw0HCbiusawJOay4tFvhH1 +4rav8Uimonl8UTNVXufMzyUOuoaQTGflmzYX3hIoswRnTPlIWFoqObvx2Q8H+of3 +uQJXy0m8I6OrIoXLNxnqYMfFls799SYgVc2jPsCbh5fwyRbx2Hof7sIZ1K/mNgxJ +RG1E3ZiLAgMBAAECggEBALu1Ni/u5Qy++YA8ZcN0s6UXNdhItLmv/q0kZuLQ+9et +CT8VZfFInLndTdsaXenDKLHdryunviFA8SV+q7P2lMbek+Xs735EiyMnMBFWxLIZ +FWNGOeQERGL19QCmLEOmEi2b+iWJQHlKaMWpbPXL3w11a+lKjIBNO4ALfoJ5QveZ +cGMKsJdm/mpqBvLeNeh2eAFk3Gp6sT1g80Ge8NkgyzFBNIqnut0eerM15kPTc6Qz +12JLaOXUuV3PrcB4PN4nOwrTDg88GDNOQtc1Pc9r4nOHyLfr8X7QEtj1wXSwmOuK +d6ynMnAmoxVA9wEnupLbil1bzohRzpsTpkmDruYaBEECgYEA/Z09I8D6mt2NVqIE +KyvLjBK39ijSV9r3/lvB2Ple2OOL5YQEd+yTrIFy+3zdUnDgD1zmNnXjmjvHZ9Lc +IFf2o06AF84QLNB5gLPdDQkGNFdDqUxljBrfAfE3oANmPS/B0SijMGOOOiDO2FtO +xl1nfRr78mswuRs9awoUWCdNRKUCgYEA7KaTYKIQW/FEjw9lshp74q5vbn6zoXF5 +7N8VkwI+bBVNvRbM9XZ8qhfgRdu9eXs5oL/N4mSYY54I8fA//pJ0Z2vpmureMm1V +mL5WBUmSD9DIbAchoK+sRiQhVmNMBQC6cHMABA7RfXvBeGvWrm9pKCS6ZLgLjkjp +PsmAcaXQcW8CgYEA2inAxljjOwUK6FNGsrxhxIT1qtNC3kCGxE+6WSNq67gSR8Vg +8qiX//T7LEslOB3RIGYRwxd2St7RkgZZRZllmOWWWuPwFhzf6E7RAL2akLvggGov +kG4tGEagSw2hjVDfsUT73ExHtMk0Jfmlsg33UC8+PDLpHtLH6qQpDAwC8+ECgYEA +o+AqOIWhvHmT11l7O915Ip1WzvZwYADbxLsrDnVEUsZh4epTHjvh0kvcY6PqTqCV +ZIrOANNWb811Nkz/k8NJVoD08PFp0xPBbZeIq/qpachTsfMyRzq/mobUiyUR9Hjv +ooUQYr78NOApNsG+lWbTNBhS9wI4BlzZIECbcJe5g4MCgYEAndRoy8S+S0Hx/S8a +O3hzXeDmivmgWqn8NVD4AKOovpkz4PaIVVQbAQkiNfAx8/DavPvjEKAbDezJ4ECV +j7IsOWtDVI7pd6eF9fTcECwisrda8aUoiOap8AQb48153Vx+g2N4Vy3uH0xJs4cz +TDALZPOBg8VlV+HEFDP43sp9Bf0= +-----END PRIVATE KEY----- diff --git a/machine.go b/machine.go index 2b462985..a637f544 100644 --- a/machine.go +++ b/machine.go @@ -2,8 +2,6 @@ package headscale import ( "database/sql/driver" - "encoding/json" - "errors" "fmt" "sort" "strconv" @@ -14,17 +12,24 @@ import ( v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/rs/zerolog/log" "google.golang.org/protobuf/types/known/timestamppb" - "gorm.io/datatypes" "inet.af/netaddr" "tailscale.com/tailcfg" "tailscale.com/types/key" ) const ( - errMachineNotFound = Error("machine not found") - errMachineAlreadyRegistered = Error("machine already registered") - errMachineRouteIsNotAvailable = Error("route is not available on machine") - errMachineAddressesInvalid = Error("failed to parse machine addresses") + errMachineNotFound = Error("machine not found") + errMachineRouteIsNotAvailable = Error("route is not available on machine") + errMachineAddressesInvalid = Error("failed to parse machine addresses") + errMachineNotFoundRegistrationCache = Error( + "machine not found in registration cache", + ) + errCouldNotConvertMachineInterface = Error("failed to convert machine interface") + errHostnameTooLong = Error("Hostname too long") +) + +const ( + maxHostnameLength = 255 ) // Machine is a Headscale client. @@ -38,18 +43,19 @@ type Machine struct { NamespaceID uint Namespace Namespace `gorm:"foreignKey:NamespaceID"` - Registered bool // temp RegisterMethod string - AuthKeyID uint - AuthKey *PreAuthKey + + // TODO(kradalby): This seems like irrelevant information? + AuthKeyID uint + AuthKey *PreAuthKey LastSeen *time.Time LastSuccessfulUpdate *time.Time Expiry *time.Time - HostInfo datatypes.JSON - Endpoints datatypes.JSON - EnabledRoutes datatypes.JSON + HostInfo HostInfo + Endpoints StringList + EnabledRoutes IPPrefixes CreatedAt time.Time UpdatedAt time.Time @@ -61,11 +67,6 @@ type ( MachinesP []*Machine ) -// For the time being this method is rather naive. -func (machine Machine) isRegistered() bool { - return machine.Registered -} - type MachineAddresses []netaddr.IP func (ma MachineAddresses) ToStringSlice() []string { @@ -112,22 +113,124 @@ func (machine Machine) isExpired() bool { // If Expiry is not set, the client has not indicated that // it wants an expiry time, it is therefor considered // to mean "not expired" - if machine.Expiry.IsZero() { + if machine.Expiry == nil || machine.Expiry.IsZero() { return false } return time.Now().UTC().After(*machine.Expiry) } -func (h *Headscale) getDirectPeers(machine *Machine) (Machines, error) { +func containsAddresses(inputs []string, addrs []string) bool { + for _, addr := range addrs { + if containsString(inputs, addr) { + return true + } + } + + return false +} + +// matchSourceAndDestinationWithRule. +func matchSourceAndDestinationWithRule( + ruleSources []string, + ruleDestinations []string, + source []string, + destination []string, +) bool { + return containsAddresses(ruleSources, source) && + containsAddresses(ruleDestinations, destination) +} + +// getFilteredByACLPeerss should return the list of peers authorized to be accessed from machine. +func getFilteredByACLPeers( + machines []Machine, + rules []tailcfg.FilterRule, + machine *Machine, +) Machines { + log.Trace(). + Caller(). + Str("machine", machine.Name). + Msg("Finding peers filtered by ACLs") + + peers := make(map[uint64]Machine) + // Aclfilter peers here. We are itering through machines in all namespaces and search through the computed aclRules + // for match between rule SrcIPs and DstPorts. If the rule is a match we allow the machine to be viewable. + for _, peer := range machines { + if peer.ID == machine.ID { + continue + } + for _, rule := range rules { + var dst []string + for _, d := range rule.DstPorts { + dst = append(dst, d.IP) + } + if matchSourceAndDestinationWithRule( + rule.SrcIPs, + dst, + machine.IPAddresses.ToStringSlice(), + peer.IPAddresses.ToStringSlice(), + ) || // match source and destination + matchSourceAndDestinationWithRule( + rule.SrcIPs, + dst, + peer.IPAddresses.ToStringSlice(), + machine.IPAddresses.ToStringSlice(), + ) || // match return path + matchSourceAndDestinationWithRule( + rule.SrcIPs, + dst, + machine.IPAddresses.ToStringSlice(), + []string{"*"}, + ) || // match source and all destination + matchSourceAndDestinationWithRule( + rule.SrcIPs, + dst, + []string{"*"}, + []string{"*"}, + ) || // match source and all destination + matchSourceAndDestinationWithRule( + rule.SrcIPs, + dst, + []string{"*"}, + peer.IPAddresses.ToStringSlice(), + ) || // match source and all destination + matchSourceAndDestinationWithRule( + rule.SrcIPs, + dst, + []string{"*"}, + machine.IPAddresses.ToStringSlice(), + ) { // match all sources and source + peers[peer.ID] = peer + } + } + } + + authorizedPeers := make([]Machine, 0, len(peers)) + for _, m := range peers { + authorizedPeers = append(authorizedPeers, m) + } + sort.Slice( + authorizedPeers, + func(i, j int) bool { return authorizedPeers[i].ID < authorizedPeers[j].ID }, + ) + + log.Trace(). + Caller(). + Str("machine", machine.Name). + Msgf("Found some machines: %v", machines) + + return authorizedPeers +} + +func (h *Headscale) ListPeers(machine *Machine) (Machines, error) { log.Trace(). Caller(). Str("machine", machine.Name). Msg("Finding direct peers") machines := Machines{} - if err := h.db.Preload("Namespace").Where("namespace_id = ? AND machine_key <> ? AND registered", - machine.NamespaceID, machine.MachineKey).Find(&machines).Error; err != nil { + if err := h.db.Preload("AuthKey").Preload("AuthKey.Namespace").Preload("Namespace").Where("machine_key <> ?", + machine.MachineKey).Find(&machines).Error; err != nil { log.Error().Err(err).Msg("Error accessing db") return Machines{}, err @@ -138,109 +241,40 @@ func (h *Headscale) getDirectPeers(machine *Machine) (Machines, error) { log.Trace(). Caller(). Str("machine", machine.Name). - Msgf("Found direct machines: %s", machines.String()) + Msgf("Found peers: %s", machines.String()) return machines, nil } -// getShared fetches machines that are shared to the `Namespace` of the machine we are getting peers for. -func (h *Headscale) getShared(machine *Machine) (Machines, error) { - log.Trace(). - Caller(). - Str("machine", machine.Name). - Msg("Finding shared peers") +func (h *Headscale) getPeers(machine *Machine) (Machines, error) { + var peers Machines + var err error - sharedMachines := []SharedMachine{} - if err := h.db.Preload("Namespace").Preload("Machine").Preload("Machine.Namespace").Where("namespace_id = ?", - machine.NamespaceID).Find(&sharedMachines).Error; err != nil { - return Machines{}, err - } - - peers := make(Machines, 0) - for _, sharedMachine := range sharedMachines { - peers = append(peers, sharedMachine.Machine) - } - - sort.Slice(peers, func(i, j int) bool { return peers[i].ID < peers[j].ID }) - - log.Trace(). - Caller(). - Str("machine", machine.Name). - Msgf("Found shared peers: %s", peers.String()) - - return peers, nil -} - -// getSharedTo fetches the machines of the namespaces this machine is shared in. -func (h *Headscale) getSharedTo(machine *Machine) (Machines, error) { - log.Trace(). - Caller(). - Str("machine", machine.Name). - Msg("Finding peers in namespaces this machine is shared with") - - sharedMachines := []SharedMachine{} - if err := h.db.Preload("Namespace").Preload("Machine").Preload("Machine.Namespace").Where("machine_id = ?", - machine.ID).Find(&sharedMachines).Error; err != nil { - return Machines{}, err - } - - peers := make(Machines, 0) - for _, sharedMachine := range sharedMachines { - namespaceMachines, err := h.ListMachinesInNamespace( - sharedMachine.Namespace.Name, - ) + // If ACLs rules are defined, filter visible host list with the ACLs + // else use the classic namespace scope + if h.aclPolicy != nil { + var machines []Machine + machines, err = h.ListMachines() if err != nil { + log.Error().Err(err).Msg("Error retrieving list of machines") + + return Machines{}, err + } + peers = getFilteredByACLPeers(machines, h.aclRules, machine) + } else { + peers, err = h.ListPeers(machine) + if err != nil { + log.Error(). + Caller(). + Err(err). + Msg("Cannot fetch peers") + return Machines{}, err } - peers = append(peers, namespaceMachines...) } sort.Slice(peers, func(i, j int) bool { return peers[i].ID < peers[j].ID }) - log.Trace(). - Caller(). - Str("machine", machine.Name). - Msgf("Found peers we are shared with: %s", peers.String()) - - return peers, nil -} - -func (h *Headscale) getPeers(machine *Machine) (Machines, error) { - direct, err := h.getDirectPeers(machine) - if err != nil { - log.Error(). - Caller(). - Err(err). - Msg("Cannot fetch peers") - - return Machines{}, err - } - - shared, err := h.getShared(machine) - if err != nil { - log.Error(). - Caller(). - Err(err). - Msg("Cannot fetch peers") - - return Machines{}, err - } - - sharedTo, err := h.getSharedTo(machine) - if err != nil { - log.Error(). - Caller(). - Err(err). - Msg("Cannot fetch peers") - - return Machines{}, err - } - - peers := append(direct, shared...) - peers = append(peers, sharedTo...) - - sort.Slice(peers, func(i, j int) bool { return peers[i].ID < peers[j].ID }) - log.Trace(). Caller(). Str("machine", machine.Name). @@ -258,7 +292,7 @@ func (h *Headscale) getValidPeers(machine *Machine) (Machines, error) { } for _, peer := range peers { - if peer.isRegistered() && !peer.isExpired() { + if !peer.isExpired() { validPeers = append(validPeers, peer) } } @@ -347,13 +381,6 @@ func (h *Headscale) RefreshMachine(machine *Machine, expiry time.Time) { // DeleteMachine softs deletes a Machine from the database. func (h *Headscale) DeleteMachine(machine *Machine) error { - err := h.RemoveSharedMachineFromAllNamespaces(machine) - if err != nil && errors.Is(err, errMachineNotShared) { - return err - } - - machine.Registered = false - h.db.Save(&machine) // we mark it as unregistered, just in case if err := h.db.Delete(&machine).Error; err != nil { return err } @@ -371,11 +398,6 @@ func (h *Headscale) TouchMachine(machine *Machine) error { // HardDeleteMachine hard deletes a Machine from the database. func (h *Headscale) HardDeleteMachine(machine *Machine) error { - err := h.RemoveSharedMachineFromAllNamespaces(machine) - if err != nil && errors.Is(err, errMachineNotShared) { - return err - } - if err := h.db.Unscoped().Delete(&machine).Error; err != nil { return err } @@ -384,20 +406,8 @@ func (h *Headscale) HardDeleteMachine(machine *Machine) error { } // GetHostInfo returns a Hostinfo struct for the machine. -func (machine *Machine) GetHostInfo() (*tailcfg.Hostinfo, error) { - hostinfo := tailcfg.Hostinfo{} - if len(machine.HostInfo) != 0 { - hi, err := machine.HostInfo.MarshalJSON() - if err != nil { - return nil, err - } - err = json.Unmarshal(hi, &hostinfo) - if err != nil { - return nil, err - } - } - - return &hostinfo, nil +func (machine *Machine) GetHostInfo() tailcfg.Hostinfo { + return tailcfg.Hostinfo(machine.HostInfo) } func (h *Headscale) isOutdated(machine *Machine) bool { @@ -407,17 +417,9 @@ func (h *Headscale) isOutdated(machine *Machine) bool { return true } - sharedMachines, _ := h.getShared(machine) - namespaceSet := set.New(set.ThreadSafe) namespaceSet.Add(machine.Namespace.Name) - // Check if any of our shared namespaces has updates that we have - // not propagated. - for _, sharedMachine := range sharedMachines { - namespaceSet.Add(sharedMachine.Namespace.Name) - } - namespaces := make([]string, namespaceSet.Size()) for index, namespace := range namespaceSet.List() { if name, ok := namespace.(string); ok { @@ -532,55 +534,15 @@ func (machine Machine) toNode( []netaddr.IPPrefix{}, addrs...) // we append the node own IP, as it is required by the clients + // TODO(kradalby): Needs investigation, We probably dont need this condition + // now that we dont have shared nodes if includeRoutes { - routesStr := []string{} - if len(machine.EnabledRoutes) != 0 { - allwIps, err := machine.EnabledRoutes.MarshalJSON() - if err != nil { - return nil, err - } - err = json.Unmarshal(allwIps, &routesStr) - if err != nil { - return nil, err - } - } - - for _, routeStr := range routesStr { - ip, err := netaddr.ParseIPPrefix(routeStr) - if err != nil { - return nil, err - } - allowedIPs = append(allowedIPs, ip) - } - } - - endpoints := []string{} - if len(machine.Endpoints) != 0 { - be, err := machine.Endpoints.MarshalJSON() - if err != nil { - return nil, err - } - err = json.Unmarshal(be, &endpoints) - if err != nil { - return nil, err - } - } - - hostinfo := tailcfg.Hostinfo{} - if len(machine.HostInfo) != 0 { - hi, err := machine.HostInfo.MarshalJSON() - if err != nil { - return nil, err - } - err = json.Unmarshal(hi, &hostinfo) - if err != nil { - return nil, err - } + allowedIPs = append(allowedIPs, machine.EnabledRoutes...) } var derp string - if hostinfo.NetInfo != nil { - derp = fmt.Sprintf("127.3.3.40:%d", hostinfo.NetInfo.PreferredDERP) + if machine.HostInfo.NetInfo != nil { + derp = fmt.Sprintf("127.3.3.40:%d", machine.HostInfo.NetInfo.PreferredDERP) } else { derp = "127.3.3.40:0" // Zero means disconnected or unknown. } @@ -600,10 +562,19 @@ func (machine Machine) toNode( machine.Namespace.Name, baseDomain, ) + if len(hostname) > maxHostnameLength { + return nil, fmt.Errorf( + "hostname %q is too long it cannot except 255 ASCII chars: %w", + hostname, + errHostnameTooLong, + ) + } } else { hostname = machine.Name } + hostInfo := machine.GetHostInfo() + node := tailcfg.Node{ ID: tailcfg.NodeID(machine.ID), // this is the actual ID StableID: tailcfg.StableNodeID( @@ -617,15 +588,15 @@ func (machine Machine) toNode( DiscoKey: discoKey, Addresses: addrs, AllowedIPs: allowedIPs, - Endpoints: endpoints, + Endpoints: machine.Endpoints, DERP: derp, - Hostinfo: hostinfo, + Hostinfo: hostInfo.View(), Created: machine.CreatedAt, LastSeen: machine.LastSeen, KeepAlive: true, - MachineAuthorized: machine.Registered, + MachineAuthorized: !machine.isExpired(), Capabilities: []string{tailcfg.CapabilityFileSharing}, } @@ -643,8 +614,6 @@ func (machine *Machine) toProto() *v1.Machine { Name: machine.Name, Namespace: machine.Namespace.toProto(), - Registered: machine.Registered, - // TODO(kradalby): Implement register method enum converter // RegisterMethod: , @@ -672,73 +641,52 @@ func (machine *Machine) toProto() *v1.Machine { return machineProto } -// RegisterMachine is executed from the CLI to register a new Machine using its MachineKey. -func (h *Headscale) RegisterMachine( +func (h *Headscale) RegisterMachineFromAuthCallback( machineKeyStr string, namespaceName string, + registrationMethod string, ) (*Machine, error) { - namespace, err := h.GetNamespace(namespaceName) - if err != nil { - return nil, err - } + if machineInterface, ok := h.registrationCache.Get(machineKeyStr); ok { + if registrationMachine, ok := machineInterface.(Machine); ok { + namespace, err := h.GetNamespace(namespaceName) + if err != nil { + return nil, fmt.Errorf( + "failed to find namespace in register machine from auth callback, %w", + err, + ) + } - var machineKey key.MachinePublic - err = machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machineKeyStr))) - if err != nil { - return nil, err - } + registrationMachine.NamespaceID = namespace.ID + registrationMachine.RegisterMethod = registrationMethod - log.Trace(). - Caller(). - Str("machine_key_str", machineKeyStr). - Str("machine_key", machineKey.String()). - Msg("Registering machine") + machine, err := h.RegisterMachine( + registrationMachine, + ) - machine, err := h.GetMachineByMachineKey(machineKey) - if err != nil { - return nil, err - } - - // TODO(kradalby): Currently, if it fails to find a requested expiry, non will be set - // This means that if a user is to slow with register a machine, it will possibly not - // have the correct expiry. - requestedTime := time.Time{} - if requestedTimeIf, found := h.requestedExpiryCache.Get(machineKey.String()); found { - log.Trace(). - Caller(). - Str("machine", machine.Name). - Msg("Expiry time found in cache, assigning to node") - if reqTime, ok := requestedTimeIf.(time.Time); ok { - requestedTime = reqTime + return machine, err + } else { + return nil, errCouldNotConvertMachineInterface } } - if machine.isRegistered() { - log.Trace(). - Caller(). - Str("machine", machine.Name). - Msg("machine already registered, reauthenticating") + return nil, errMachineNotFoundRegistrationCache +} - h.RefreshMachine(machine, requestedTime) - - return machine, nil - } +// RegisterMachine is executed from the CLI to register a new Machine using its MachineKey. +func (h *Headscale) RegisterMachine(machine Machine, +) (*Machine, error) { + log.Trace(). + Caller(). + Str("machine_key", machine.MachineKey). + Msg("Registering machine") log.Trace(). Caller(). Str("machine", machine.Name). Msg("Attempting to register machine") - if machine.isRegistered() { - err := errMachineAlreadyRegistered - log.Error(). - Caller(). - Err(err). - Str("machine", machine.Name). - Msg("Attempting to register machine") - - return nil, err - } + h.ipAllocationMutex.Lock() + defer h.ipAllocationMutex.Unlock() ips, err := h.getAvailableIPs() if err != nil { @@ -751,17 +699,8 @@ func (h *Headscale) RegisterMachine( return nil, err } - log.Trace(). - Caller(). - Str("machine", machine.Name). - Str("ip", strings.Join(ips.ToStringSlice(), ",")). - Msg("Found IP for host") - machine.IPAddresses = ips - machine.NamespaceID = namespace.ID - machine.Registered = true - machine.RegisterMethod = RegisterMethodCLI - machine.Expiry = &requestedTime + h.db.Save(&machine) log.Trace(). @@ -770,40 +709,15 @@ func (h *Headscale) RegisterMachine( Str("ip", strings.Join(ips.ToStringSlice(), ",")). Msg("Machine registered with the database") - return machine, nil + return &machine, nil } -func (machine *Machine) GetAdvertisedRoutes() ([]netaddr.IPPrefix, error) { - hostInfo, err := machine.GetHostInfo() - if err != nil { - return nil, err - } - - return hostInfo.RoutableIPs, nil +func (machine *Machine) GetAdvertisedRoutes() []netaddr.IPPrefix { + return machine.HostInfo.RoutableIPs } -func (machine *Machine) GetEnabledRoutes() ([]netaddr.IPPrefix, error) { - data, err := machine.EnabledRoutes.MarshalJSON() - if err != nil { - return nil, err - } - - routesStr := []string{} - err = json.Unmarshal(data, &routesStr) - if err != nil { - return nil, err - } - - routes := make([]netaddr.IPPrefix, len(routesStr)) - for index, routeStr := range routesStr { - route, err := netaddr.ParseIPPrefix(routeStr) - if err != nil { - return nil, err - } - routes[index] = route - } - - return routes, nil +func (machine *Machine) GetEnabledRoutes() []netaddr.IPPrefix { + return machine.EnabledRoutes } func (machine *Machine) IsRoutesEnabled(routeStr string) bool { @@ -812,10 +726,7 @@ func (machine *Machine) IsRoutesEnabled(routeStr string) bool { return false } - enabledRoutes, err := machine.GetEnabledRoutes() - if err != nil { - return false - } + enabledRoutes := machine.GetEnabledRoutes() for _, enabledRoute := range enabledRoutes { if route == enabledRoute { @@ -839,13 +750,8 @@ func (h *Headscale) EnableRoutes(machine *Machine, routeStrs ...string) error { newRoutes[index] = route } - availableRoutes, err := machine.GetAdvertisedRoutes() - if err != nil { - return err - } - for _, newRoute := range newRoutes { - if !containsIPPrefix(availableRoutes, newRoute) { + if !containsIPPrefix(machine.GetAdvertisedRoutes(), newRoute) { return fmt.Errorf( "route (%s) is not available on node %s: %w", machine.Name, @@ -854,30 +760,19 @@ func (h *Headscale) EnableRoutes(machine *Machine, routeStrs ...string) error { } } - routes, err := json.Marshal(newRoutes) - if err != nil { - return err - } - - machine.EnabledRoutes = datatypes.JSON(routes) + machine.EnabledRoutes = newRoutes h.db.Save(&machine) return nil } -func (machine *Machine) RoutesToProto() (*v1.Routes, error) { - availableRoutes, err := machine.GetAdvertisedRoutes() - if err != nil { - return nil, err - } +func (machine *Machine) RoutesToProto() *v1.Routes { + availableRoutes := machine.GetAdvertisedRoutes() - enabledRoutes, err := machine.GetEnabledRoutes() - if err != nil { - return nil, err - } + enabledRoutes := machine.GetEnabledRoutes() return &v1.Routes{ AdvertisedRoutes: ipPrefixToString(availableRoutes), EnabledRoutes: ipPrefixToString(enabledRoutes), - }, nil + } } diff --git a/machine_test.go b/machine_test.go index ff1dc914..a455a0bb 100644 --- a/machine_test.go +++ b/machine_test.go @@ -1,11 +1,15 @@ package headscale import ( + "fmt" + "reflect" "strconv" + "testing" "time" "gopkg.in/check.v1" "inet.af/netaddr" + "tailscale.com/tailcfg" ) func (s *Suite) TestGetMachine(c *check.C) { @@ -25,16 +29,12 @@ func (s *Suite) TestGetMachine(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } app.db.Save(machine) - machineFromDB, err := app.GetMachine("test", "testmachine") - c.Assert(err, check.IsNil) - - _, err = machineFromDB.GetHostInfo() + _, err = app.GetMachine("test", "testmachine") c.Assert(err, check.IsNil) } @@ -55,16 +55,12 @@ func (s *Suite) TestGetMachineByID(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } app.db.Save(&machine) - machineByID, err := app.GetMachineByID(0) - c.Assert(err, check.IsNil) - - _, err = machineByID.GetHostInfo() + _, err = app.GetMachineByID(0) c.Assert(err, check.IsNil) } @@ -78,7 +74,6 @@ func (s *Suite) TestDeleteMachine(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(1), } @@ -101,7 +96,6 @@ func (s *Suite) TestHardDeleteMachine(c *check.C) { DiscoKey: "faa", Name: "testmachine3", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(1), } @@ -114,7 +108,7 @@ func (s *Suite) TestHardDeleteMachine(c *check.C) { c.Assert(err, check.NotNil) } -func (s *Suite) TestGetDirectPeers(c *check.C) { +func (s *Suite) TestListPeers(c *check.C) { namespace, err := app.CreateNamespace("test") c.Assert(err, check.IsNil) @@ -132,7 +126,6 @@ func (s *Suite) TestGetDirectPeers(c *check.C) { DiscoKey: "faa" + strconv.Itoa(index), Name: "testmachine" + strconv.Itoa(index), NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } @@ -142,10 +135,7 @@ func (s *Suite) TestGetDirectPeers(c *check.C) { machine0ByID, err := app.GetMachineByID(0) c.Assert(err, check.IsNil) - _, err = machine0ByID.GetHostInfo() - c.Assert(err, check.IsNil) - - peersOfMachine0, err := app.getDirectPeers(machine0ByID) + peersOfMachine0, err := app.ListPeers(machine0ByID) c.Assert(err, check.IsNil) c.Assert(len(peersOfMachine0), check.Equals, 9) @@ -154,6 +144,85 @@ func (s *Suite) TestGetDirectPeers(c *check.C) { c.Assert(peersOfMachine0[8].Name, check.Equals, "testmachine10") } +func (s *Suite) TestGetACLFilteredPeers(c *check.C) { + type base struct { + namespace *Namespace + key *PreAuthKey + } + + stor := make([]base, 0) + + for _, name := range []string{"test", "admin"} { + namespace, err := app.CreateNamespace(name) + c.Assert(err, check.IsNil) + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) + c.Assert(err, check.IsNil) + stor = append(stor, base{namespace, pak}) + } + + _, err := app.GetMachineByID(0) + c.Assert(err, check.NotNil) + + for index := 0; index <= 10; index++ { + machine := Machine{ + ID: uint64(index), + MachineKey: "foo" + strconv.Itoa(index), + NodeKey: "bar" + strconv.Itoa(index), + DiscoKey: "faa" + strconv.Itoa(index), + IPAddresses: MachineAddresses{ + netaddr.MustParseIP(fmt.Sprintf("100.64.0.%v", strconv.Itoa(index+1))), + }, + Name: "testmachine" + strconv.Itoa(index), + NamespaceID: stor[index%2].namespace.ID, + RegisterMethod: RegisterMethodAuthKey, + AuthKeyID: uint(stor[index%2].key.ID), + } + app.db.Save(&machine) + } + + app.aclPolicy = &ACLPolicy{ + Groups: map[string][]string{ + "group:test": {"admin"}, + }, + Hosts: map[string]netaddr.IPPrefix{}, + TagOwners: map[string][]string{}, + ACLs: []ACL{ + {Action: "accept", Users: []string{"admin"}, Ports: []string{"*:*"}}, + {Action: "accept", Users: []string{"test"}, Ports: []string{"test:*"}}, + }, + Tests: []ACLTest{}, + } + + err = app.UpdateACLRules() + c.Assert(err, check.IsNil) + + adminMachine, err := app.GetMachineByID(1) + c.Logf("Machine(%v), namespace: %v", adminMachine.Name, adminMachine.Namespace) + c.Assert(err, check.IsNil) + + testMachine, err := app.GetMachineByID(2) + c.Logf("Machine(%v), namespace: %v", testMachine.Name, testMachine.Namespace) + c.Assert(err, check.IsNil) + + machines, err := app.ListMachines() + c.Assert(err, check.IsNil) + + peersOfTestMachine := getFilteredByACLPeers(machines, app.aclRules, testMachine) + peersOfAdminMachine := getFilteredByACLPeers(machines, app.aclRules, adminMachine) + + c.Log(peersOfTestMachine) + c.Assert(len(peersOfTestMachine), check.Equals, 4) + c.Assert(peersOfTestMachine[0].Name, check.Equals, "testmachine4") + c.Assert(peersOfTestMachine[1].Name, check.Equals, "testmachine6") + c.Assert(peersOfTestMachine[3].Name, check.Equals, "testmachine10") + + c.Log(peersOfAdminMachine) + c.Assert(len(peersOfAdminMachine), check.Equals, 9) + c.Assert(peersOfAdminMachine[0].Name, check.Equals, "testmachine2") + c.Assert(peersOfAdminMachine[2].Name, check.Equals, "testmachine4") + c.Assert(peersOfAdminMachine[5].Name, check.Equals, "testmachine7") +} + func (s *Suite) TestExpireMachine(c *check.C) { namespace, err := app.CreateNamespace("test") c.Assert(err, check.IsNil) @@ -171,7 +240,6 @@ func (s *Suite) TestExpireMachine(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), Expiry: &time.Time{}, @@ -208,3 +276,381 @@ func (s *Suite) TestSerdeAddressStrignSlice(c *check.C) { c.Assert(deserialized[i], check.Equals, input[i]) } } + +// nolint +func Test_getFilteredByACLPeers(t *testing.T) { + type args struct { + machines []Machine + rules []tailcfg.FilterRule + machine *Machine + } + tests := []struct { + name string + args args + want Machines + }{ + { + name: "all hosts can talk to each other", + args: args{ + machines: []Machine{ // list of all machines in the database + { + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + ID: 2, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "marc"}, + }, + { + ID: 3, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "mickael"}, + }, + }, + rules: []tailcfg.FilterRule{ // list of all ACLRules registered + { + SrcIPs: []string{"100.64.0.1", "100.64.0.2", "100.64.0.3"}, + DstPorts: []tailcfg.NetPortRange{ + {IP: "*"}, + }, + }, + }, + machine: &Machine{ // current machine + ID: 1, + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")}, + Namespace: Namespace{Name: "joe"}, + }, + }, + want: Machines{ + { + ID: 2, + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")}, + Namespace: Namespace{Name: "marc"}, + }, + { + ID: 3, + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.3")}, + Namespace: Namespace{Name: "mickael"}, + }, + }, + }, + { + name: "One host can talk to another, but not all hosts", + args: args{ + machines: []Machine{ // list of all machines in the database + { + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + ID: 2, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "marc"}, + }, + { + ID: 3, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "mickael"}, + }, + }, + rules: []tailcfg.FilterRule{ // list of all ACLRules registered + { + SrcIPs: []string{"100.64.0.1", "100.64.0.2", "100.64.0.3"}, + DstPorts: []tailcfg.NetPortRange{ + {IP: "100.64.0.2"}, + }, + }, + }, + machine: &Machine{ // current machine + ID: 1, + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")}, + Namespace: Namespace{Name: "joe"}, + }, + }, + want: Machines{ + { + ID: 2, + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")}, + Namespace: Namespace{Name: "marc"}, + }, + }, + }, + { + name: "host cannot directly talk to destination, but return path is authorized", + args: args{ + machines: []Machine{ // list of all machines in the database + { + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + ID: 2, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "marc"}, + }, + { + ID: 3, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "mickael"}, + }, + }, + rules: []tailcfg.FilterRule{ // list of all ACLRules registered + { + SrcIPs: []string{"100.64.0.3"}, + DstPorts: []tailcfg.NetPortRange{ + {IP: "100.64.0.2"}, + }, + }, + }, + machine: &Machine{ // current machine + ID: 2, + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")}, + Namespace: Namespace{Name: "marc"}, + }, + }, + want: Machines{ + { + ID: 3, + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.3")}, + Namespace: Namespace{Name: "mickael"}, + }, + }, + }, + { + name: "rules allows all hosts to reach one destination", + args: args{ + machines: []Machine{ // list of all machines in the database + { + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + ID: 2, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "marc"}, + }, + { + ID: 3, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "mickael"}, + }, + }, + rules: []tailcfg.FilterRule{ // list of all ACLRules registered + { + SrcIPs: []string{"*"}, + DstPorts: []tailcfg.NetPortRange{ + {IP: "100.64.0.2"}, + }, + }, + }, + machine: &Machine{ // current machine + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + }, + want: Machines{ + { + ID: 2, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "marc"}, + }, + }, + }, + { + name: "rules allows all hosts to reach one destination, destination can reach all hosts", + args: args{ + machines: []Machine{ // list of all machines in the database + { + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + ID: 2, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "marc"}, + }, + { + ID: 3, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "mickael"}, + }, + }, + rules: []tailcfg.FilterRule{ // list of all ACLRules registered + { + SrcIPs: []string{"*"}, + DstPorts: []tailcfg.NetPortRange{ + {IP: "100.64.0.2"}, + }, + }, + }, + machine: &Machine{ // current machine + ID: 2, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "marc"}, + }, + }, + want: Machines{ + { + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + ID: 3, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "mickael"}, + }, + }, + }, + { + name: "rule allows all hosts to reach all destinations", + args: args{ + machines: []Machine{ // list of all machines in the database + { + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + ID: 2, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "marc"}, + }, + { + ID: 3, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "mickael"}, + }, + }, + rules: []tailcfg.FilterRule{ // list of all ACLRules registered + { + SrcIPs: []string{"*"}, + DstPorts: []tailcfg.NetPortRange{ + {IP: "*"}, + }, + }, + }, + machine: &Machine{ // current machine + ID: 2, + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")}, + Namespace: Namespace{Name: "marc"}, + }, + }, + want: Machines{ + { + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + ID: 3, + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.3")}, + Namespace: Namespace{Name: "mickael"}, + }, + }, + }, + { + name: "without rule all communications are forbidden", + args: args{ + machines: []Machine{ // list of all machines in the database + { + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + ID: 2, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "marc"}, + }, + { + ID: 3, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "mickael"}, + }, + }, + rules: []tailcfg.FilterRule{ // list of all ACLRules registered + }, + machine: &Machine{ // current machine + ID: 2, + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")}, + Namespace: Namespace{Name: "marc"}, + }, + }, + want: Machines{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := getFilteredByACLPeers( + tt.args.machines, + tt.args.rules, + tt.args.machine, + ) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("getFilteredByACLPeers() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/namespaces.go b/namespaces.go index bdd440cf..bb32795a 100644 --- a/namespaces.go +++ b/namespaces.go @@ -2,7 +2,10 @@ package headscale import ( "errors" + "fmt" + "regexp" "strconv" + "strings" "time" v1 "github.com/juanfont/headscale/gen/go/headscale/v1" @@ -16,8 +19,16 @@ const ( errNamespaceExists = Error("Namespace already exists") errNamespaceNotFound = Error("Namespace not found") errNamespaceNotEmptyOfNodes = Error("Namespace not empty: node(s) found") + errInvalidNamespaceName = Error("Invalid namespace name") ) +const ( + // value related to RFC 1123 and 952. + labelHostnameLength = 63 +) + +var invalidCharsInNamespaceRegex = regexp.MustCompile("[^a-z0-9-.]+") + // Namespace is the way Headscale implements the concept of users in Tailscale // // At the end of the day, users in Tailscale are some kind of 'bubbles' or namespaces @@ -30,6 +41,10 @@ type Namespace struct { // CreateNamespace creates a new Namespace. Returns error if could not be created // or another namespace already exists. func (h *Headscale) CreateNamespace(name string) (*Namespace, error) { + err := CheckForFQDNRules(name) + if err != nil { + return nil, err + } namespace := Namespace{} if err := h.db.Where("name = ?", name).First(&namespace).Error; err == nil { return nil, errNamespaceExists @@ -84,10 +99,15 @@ func (h *Headscale) DestroyNamespace(name string) error { // RenameNamespace renames a Namespace. Returns error if the Namespace does // not exist or if another Namespace exists with the new name. func (h *Headscale) RenameNamespace(oldName, newName string) error { + var err error oldNamespace, err := h.GetNamespace(oldName) if err != nil { return err } + err = CheckForFQDNRules(newName) + if err != nil { + return err + } _, err = h.GetNamespace(newName) if err == nil { return errNamespaceExists @@ -130,6 +150,10 @@ func (h *Headscale) ListNamespaces() ([]Namespace, error) { // ListMachinesInNamespace gets all the nodes in a given namespace. func (h *Headscale) ListMachinesInNamespace(name string) ([]Machine, error) { + err := CheckForFQDNRules(name) + if err != nil { + return nil, err + } namespace, err := h.GetNamespace(name) if err != nil { return nil, err @@ -143,33 +167,12 @@ func (h *Headscale) ListMachinesInNamespace(name string) ([]Machine, error) { return machines, nil } -// ListSharedMachinesInNamespace returns all the machines that are shared to the specified namespace. -func (h *Headscale) ListSharedMachinesInNamespace(name string) ([]Machine, error) { - namespace, err := h.GetNamespace(name) - if err != nil { - return nil, err - } - sharedMachines := []SharedMachine{} - if err := h.db.Preload("Namespace").Where(&SharedMachine{NamespaceID: namespace.ID}).Find(&sharedMachines).Error; err != nil { - return nil, err - } - - machines := []Machine{} - for _, sharedMachine := range sharedMachines { - machine, err := h.GetMachineByID( - sharedMachine.MachineID, - ) // otherwise not everything comes filled - if err != nil { - return nil, err - } - machines = append(machines, *machine) - } - - return machines, nil -} - // SetMachineNamespace assigns a Machine to a namespace. func (h *Headscale) SetMachineNamespace(machine *Machine, namespaceName string) error { + err := CheckForFQDNRules(namespaceName) + if err != nil { + return err + } namespace, err := h.GetNamespace(namespaceName) if err != nil { return err @@ -233,3 +236,55 @@ func (n *Namespace) toProto() *v1.Namespace { CreatedAt: timestamppb.New(n.CreatedAt), } } + +// NormalizeToFQDNRules will replace forbidden chars in namespace +// it can also return an error if the namespace doesn't respect RFC 952 and 1123. +func NormalizeToFQDNRules(name string, stripEmailDomain bool) (string, error) { + name = strings.ToLower(name) + name = strings.ReplaceAll(name, "'", "") + atIdx := strings.Index(name, "@") + if stripEmailDomain && atIdx > 0 { + name = name[:atIdx] + } else { + name = strings.ReplaceAll(name, "@", ".") + } + name = invalidCharsInNamespaceRegex.ReplaceAllString(name, "-") + + for _, elt := range strings.Split(name, ".") { + if len(elt) > labelHostnameLength { + return "", fmt.Errorf( + "label %v is more than 63 chars: %w", + elt, + errInvalidNamespaceName, + ) + } + } + + return name, nil +} + +func CheckForFQDNRules(name string) error { + if len(name) > labelHostnameLength { + return fmt.Errorf( + "Namespace must not be over 63 chars. %v doesn't comply with this rule: %w", + name, + errInvalidNamespaceName, + ) + } + if strings.ToLower(name) != name { + return fmt.Errorf( + "Namespace name should be lowercase. %v doesn't comply with this rule: %w", + name, + errInvalidNamespaceName, + ) + } + if invalidCharsInNamespaceRegex.MatchString(name) { + return fmt.Errorf( + "Namespace name should only be composed of lowercase ASCII letters numbers, hyphen and dots. %v doesn't comply with theses rules: %w", + name, + errInvalidNamespaceName, + ) + } + + return nil +} diff --git a/namespaces_test.go b/namespaces_test.go index d07deb96..5d873bd6 100644 --- a/namespaces_test.go +++ b/namespaces_test.go @@ -1,7 +1,8 @@ package headscale import ( - "github.com/rs/zerolog/log" + "testing" + "gopkg.in/check.v1" "gorm.io/gorm" "inet.af/netaddr" @@ -53,7 +54,6 @@ func (s *Suite) TestDestroyNamespaceErrors(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } @@ -72,23 +72,23 @@ func (s *Suite) TestRenameNamespace(c *check.C) { c.Assert(err, check.IsNil) c.Assert(len(namespaces), check.Equals, 1) - err = app.RenameNamespace("test", "test_renamed") + err = app.RenameNamespace("test", "test-renamed") c.Assert(err, check.IsNil) _, err = app.GetNamespace("test") c.Assert(err, check.Equals, errNamespaceNotFound) - _, err = app.GetNamespace("test_renamed") + _, err = app.GetNamespace("test-renamed") c.Assert(err, check.IsNil) - err = app.RenameNamespace("test_does_not_exit", "test") + err = app.RenameNamespace("test-does-not-exit", "test") c.Assert(err, check.Equals, errNamespaceNotFound) namespaceTest2, err := app.CreateNamespace("test2") c.Assert(err, check.IsNil) c.Assert(namespaceTest2.Name, check.Equals, "test2") - err = app.RenameNamespace("test2", "test_renamed") + err = app.RenameNamespace("test2", "test-renamed") c.Assert(err, check.Equals, errNamespaceExists) } @@ -145,7 +145,6 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { Name: "test_get_shared_nodes_1", NamespaceID: namespaceShared1.ID, Namespace: *namespaceShared1, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")}, AuthKeyID: uint(preAuthKeyShared1.ID), @@ -163,7 +162,6 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { Name: "test_get_shared_nodes_2", NamespaceID: namespaceShared2.ID, Namespace: *namespaceShared2, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")}, AuthKeyID: uint(preAuthKeyShared2.ID), @@ -181,7 +179,6 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { Name: "test_get_shared_nodes_3", NamespaceID: namespaceShared3.ID, Namespace: *namespaceShared3, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")}, AuthKeyID: uint(preAuthKeyShared3.ID), @@ -199,15 +196,12 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { Name: "test_get_shared_nodes_4", NamespaceID: namespaceShared1.ID, Namespace: *namespaceShared1, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")}, AuthKeyID: uint(preAuthKey2Shared1.ID), } app.db.Save(machine2InShared1) - err = app.AddSharedMachineToNamespace(machineInShared2, namespaceShared1) - c.Assert(err, check.IsNil) peersOfMachine1InShared1, err := app.getPeers(machineInShared1) c.Assert(err, check.IsNil) @@ -216,8 +210,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { peersOfMachine1InShared1, ) - log.Trace().Msgf("userProfiles %#v", userProfiles) - c.Assert(len(userProfiles), check.Equals, 2) + c.Assert(len(userProfiles), check.Equals, 3) found := false for _, userProfiles := range userProfiles { @@ -239,3 +232,143 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { } c.Assert(found, check.Equals, true) } + +func TestNormalizeToFQDNRules(t *testing.T) { + type args struct { + name string + stripEmailDomain bool + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "normalize simple name", + args: args{ + name: "normalize-simple.name", + stripEmailDomain: false, + }, + want: "normalize-simple.name", + wantErr: false, + }, + { + name: "normalize an email", + args: args{ + name: "foo.bar@example.com", + stripEmailDomain: false, + }, + want: "foo.bar.example.com", + wantErr: false, + }, + { + name: "normalize an email domain should be removed", + args: args{ + name: "foo.bar@example.com", + stripEmailDomain: true, + }, + want: "foo.bar", + wantErr: false, + }, + { + name: "strip enabled no email passed as argument", + args: args{ + name: "not-email-and-strip-enabled", + stripEmailDomain: true, + }, + want: "not-email-and-strip-enabled", + wantErr: false, + }, + { + name: "normalize complex email", + args: args{ + name: "foo.bar+complex-email@example.com", + stripEmailDomain: false, + }, + want: "foo.bar-complex-email.example.com", + wantErr: false, + }, + { + name: "namespace name with space", + args: args{ + name: "name space", + stripEmailDomain: false, + }, + want: "name-space", + wantErr: false, + }, + { + name: "namespace with quote", + args: args{ + name: "Jamie's iPhone 5", + stripEmailDomain: false, + }, + want: "jamies-iphone-5", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NormalizeToFQDNRules(tt.args.name, tt.args.stripEmailDomain) + if (err != nil) != tt.wantErr { + t.Errorf( + "NormalizeToFQDNRules() error = %v, wantErr %v", + err, + tt.wantErr, + ) + + return + } + if got != tt.want { + t.Errorf("NormalizeToFQDNRules() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCheckForFQDNRules(t *testing.T) { + type args struct { + name string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "valid: namespace", + args: args{name: "valid-namespace"}, + wantErr: false, + }, + { + name: "invalid: capitalized namespace", + args: args{name: "Invalid-CapItaLIzed-namespace"}, + wantErr: true, + }, + { + name: "invalid: email as namespace", + args: args{name: "foo.bar@example.com"}, + wantErr: true, + }, + { + name: "invalid: chars in namespace name", + args: args{name: "super-namespace+name"}, + wantErr: true, + }, + { + name: "invalid: too long name for namespace", + args: args{ + name: "super-long-namespace-name-that-should-be-a-little-more-than-63-chars", + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := CheckForFQDNRules(tt.args.name); (err != nil) != tt.wantErr { + t.Errorf("CheckForFQDNRules() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/oidc.go b/oidc.go index a47863ff..29ce351f 100644 --- a/oidc.go +++ b/oidc.go @@ -9,23 +9,17 @@ import ( "fmt" "html/template" "net/http" - "regexp" "strings" - "time" "github.com/coreos/go-oidc/v3/oidc" "github.com/gin-gonic/gin" - "github.com/patrickmn/go-cache" "github.com/rs/zerolog/log" "golang.org/x/oauth2" - "gorm.io/gorm" "tailscale.com/types/key" ) const ( - oidcStateCacheExpiration = time.Minute * 5 - oidcStateCacheCleanupInterval = time.Minute * 10 - randomByteSize = 16 + randomByteSize = 16 ) type IDTokenClaims struct { @@ -62,14 +56,6 @@ func (h *Headscale) initOIDC() error { } } - // init the state cache if it hasn't been already - if h.oidcStateCache == nil { - h.oidcStateCache = cache.New( - oidcStateCacheExpiration, - oidcStateCacheCleanupInterval, - ) - } - return nil } @@ -102,7 +88,7 @@ func (h *Headscale) RegisterOIDC(ctx *gin.Context) { stateStr := hex.EncodeToString(randomBlob)[:32] // place the machine key into the state cache, so it can be retrieved later - h.oidcStateCache.Set(stateStr, machineKeyStr, oidcStateCacheExpiration) + h.registrationCache.Set(stateStr, machineKeyStr, registerCacheExpiration) authURL := h.oauth2Config.AuthCodeURL(stateStr) log.Debug().Msgf("Redirecting to %s for authentication", authURL) @@ -126,7 +112,6 @@ var oidcCallbackTemplate = template.Must( `), ) -// TODO: Why is the entire machine registration logic duplicated here? // OIDCCallback handles the callback from the OIDC endpoint // Retrieves the mkey from the state cache and adds the machine to the users email namespace // TODO: A confirmation page for new machines should be added to avoid phishing vulnerabilities @@ -198,7 +183,7 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { } // retrieve machinekey from state cache - machineKeyIf, machineKeyFound := h.oidcStateCache.Get(state) + machineKeyIf, machineKeyFound := h.registrationCache.Get(state) if !machineKeyFound { log.Error(). @@ -208,10 +193,12 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { return } - machineKeyStr, machineKeyOK := machineKeyIf.(string) + machineKeyFromCache, machineKeyOK := machineKeyIf.(string) var machineKey key.MachinePublic - err = machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machineKeyStr))) + err = machineKey.UnmarshalText( + []byte(MachinePublicKeyEnsurePrefix(machineKeyFromCache)), + ) if err != nil { log.Error(). Msg("could not parse machine public key") @@ -230,33 +217,19 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { return } - // TODO(kradalby): Currently, if it fails to find a requested expiry, non will be set - requestedTime := time.Time{} - if requestedTimeIf, found := h.requestedExpiryCache.Get(machineKey.String()); found { - if reqTime, ok := requestedTimeIf.(time.Time); ok { - requestedTime = reqTime - } - } + // retrieve machine information if it exist + // The error is not important, because if it does not + // exist, then this is a new machine and we will move + // on to registration. + machine, _ := h.GetMachineByMachineKey(machineKey) - // retrieve machine information - machine, err := h.GetMachineByMachineKey(machineKey) - if err != nil { - log.Error().Msg("machine key not found in database") - ctx.String( - http.StatusInternalServerError, - "could not get machine info from database", - ) - - return - } - - if machine.isRegistered() { + if machine != nil { log.Trace(). Caller(). Str("machine", machine.Name). Msg("machine already registered, reauthenticating") - h.RefreshMachine(machine, requestedTime) + h.RefreshMachine(machine, *machine.Expiry) var content bytes.Buffer if err := oidcCallbackTemplate.Execute(&content, oidcCallbackTemplateConfig{ @@ -280,111 +253,89 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { return } - now := time.Now().UTC() - - if namespaceName, ok := h.getNamespaceFromEmail(claims.Email); ok { - // register the machine if it's new - if !machine.Registered { - log.Debug().Msg("Registering new machine after successful callback") - - namespace, err := h.GetNamespace(namespaceName) - if errors.Is(err, gorm.ErrRecordNotFound) { - namespace, err = h.CreateNamespace(namespaceName) - - if err != nil { - log.Error(). - Err(err). - Caller(). - Msgf("could not create new namespace '%s'", namespaceName) - ctx.String( - http.StatusInternalServerError, - "could not create new namespace", - ) - - return - } - } else if err != nil { - log.Error(). - Caller(). - Err(err). - Str("namespace", namespaceName). - Msg("could not find or create namespace") - ctx.String( - http.StatusInternalServerError, - "could not find or create namespace", - ) - - return - } - - ips, err := h.getAvailableIPs() - if err != nil { - log.Error(). - Caller(). - Err(err). - Msg("could not get an IP from the pool") - ctx.String( - http.StatusInternalServerError, - "could not get an IP from the pool", - ) - - return - } - - machine.IPAddresses = ips - machine.NamespaceID = namespace.ID - machine.Registered = true - machine.RegisterMethod = RegisterMethodOIDC - machine.LastSuccessfulUpdate = &now - machine.Expiry = &requestedTime - h.db.Save(&machine) - } - - var content bytes.Buffer - if err := oidcCallbackTemplate.Execute(&content, oidcCallbackTemplateConfig{ - User: claims.Email, - Verb: "Authenticated", - }); err != nil { - log.Error(). - Str("func", "OIDCCallback"). - Str("type", "authenticate"). - Err(err). - Msg("Could not render OIDC callback template") - ctx.Data( - http.StatusInternalServerError, - "text/html; charset=utf-8", - []byte("Could not render OIDC callback template"), - ) - } - - ctx.Data(http.StatusOK, "text/html; charset=utf-8", content.Bytes()) + namespaceName, err := NormalizeToFQDNRules( + claims.Email, + h.cfg.OIDC.StripEmaildomain, + ) + if err != nil { + log.Error().Err(err).Caller().Msgf("couldn't normalize email") + ctx.String( + http.StatusInternalServerError, + "couldn't normalize email", + ) return } - log.Error(). - Caller(). - Str("email", claims.Email). - Str("username", claims.Username). - Str("machine", machine.Name). - Msg("Email could not be mapped to a namespace") - ctx.String( - http.StatusBadRequest, - "email from claim could not be mapped to a namespace", - ) -} + // register the machine if it's new + log.Debug().Msg("Registering new machine after successful callback") -// getNamespaceFromEmail passes the users email through a list of "matchers" -// and iterates through them until it matches and returns a namespace. -// If no match is found, an empty string will be returned. -// TODO(kradalby): golang Maps key order is not stable, so this list is _not_ deterministic. Find a way to make the list of keys stable, preferably in the order presented in a users configuration. -func (h *Headscale) getNamespaceFromEmail(email string) (string, bool) { - for match, namespace := range h.cfg.OIDC.MatchMap { - regex := regexp.MustCompile(match) - if regex.MatchString(email) { - return namespace, true + namespace, err := h.GetNamespace(namespaceName) + if errors.Is(err, errNamespaceNotFound) { + namespace, err = h.CreateNamespace(namespaceName) + + if err != nil { + log.Error(). + Err(err). + Caller(). + Msgf("could not create new namespace '%s'", namespaceName) + ctx.String( + http.StatusInternalServerError, + "could not create new namespace", + ) + + return } + } else if err != nil { + log.Error(). + Caller(). + Err(err). + Str("namespace", namespaceName). + Msg("could not find or create namespace") + ctx.String( + http.StatusInternalServerError, + "could not find or create namespace", + ) + + return } - return "", false + machineKeyStr := MachinePublicKeyStripPrefix(machineKey) + + _, err = h.RegisterMachineFromAuthCallback( + machineKeyStr, + namespace.Name, + RegisterMethodOIDC, + ) + if err != nil { + log.Error(). + Caller(). + Err(err). + Msg("could not register machine") + ctx.String( + http.StatusInternalServerError, + "could not register machine", + ) + + return + } + + var content bytes.Buffer + if err := oidcCallbackTemplate.Execute(&content, oidcCallbackTemplateConfig{ + User: claims.Email, + Verb: "Authenticated", + }); err != nil { + log.Error(). + Str("func", "OIDCCallback"). + Str("type", "authenticate"). + Err(err). + Msg("Could not render OIDC callback template") + ctx.Data( + http.StatusInternalServerError, + "text/html; charset=utf-8", + []byte("Could not render OIDC callback template"), + ) + } + + ctx.Data(http.StatusOK, "text/html; charset=utf-8", content.Bytes()) } diff --git a/oidc_test.go b/oidc_test.go deleted file mode 100644 index d50027a9..00000000 --- a/oidc_test.go +++ /dev/null @@ -1,180 +0,0 @@ -package headscale - -import ( - "sync" - "testing" - - "github.com/coreos/go-oidc/v3/oidc" - "github.com/patrickmn/go-cache" - "golang.org/x/oauth2" - "gorm.io/gorm" - "tailscale.com/tailcfg" - "tailscale.com/types/key" -) - -func TestHeadscale_getNamespaceFromEmail(t *testing.T) { - type fields struct { - cfg Config - db *gorm.DB - dbString string - dbType string - dbDebug bool - privateKey *key.MachinePrivate - aclPolicy *ACLPolicy - aclRules []tailcfg.FilterRule - lastStateChange sync.Map - oidcProvider *oidc.Provider - oauth2Config *oauth2.Config - oidcStateCache *cache.Cache - } - type args struct { - email string - } - tests := []struct { - name string - fields fields - args args - want string - want1 bool - }{ - { - name: "match all", - fields: fields{ - cfg: Config{ - OIDC: OIDCConfig{ - MatchMap: map[string]string{ - ".*": "space", - }, - }, - }, - }, - args: args{ - email: "test@example.no", - }, - want: "space", - want1: true, - }, - { - name: "match user", - fields: fields{ - cfg: Config{ - OIDC: OIDCConfig{ - MatchMap: map[string]string{ - "specific@user\\.no": "user-namespace", - }, - }, - }, - }, - args: args{ - email: "specific@user.no", - }, - want: "user-namespace", - want1: true, - }, - { - name: "match domain", - fields: fields{ - cfg: Config{ - OIDC: OIDCConfig{ - MatchMap: map[string]string{ - ".*@example\\.no": "example", - }, - }, - }, - }, - args: args{ - email: "test@example.no", - }, - want: "example", - want1: true, - }, - { - name: "multi match domain", - fields: fields{ - cfg: Config{ - OIDC: OIDCConfig{ - MatchMap: map[string]string{ - ".*@example\\.no": "exammple", - ".*@gmail\\.com": "gmail", - }, - }, - }, - }, - args: args{ - email: "someuser@gmail.com", - }, - want: "gmail", - want1: true, - }, - { - name: "no match domain", - fields: fields{ - cfg: Config{ - OIDC: OIDCConfig{ - MatchMap: map[string]string{ - ".*@dontknow.no": "never", - }, - }, - }, - }, - args: args{ - email: "test@wedontknow.no", - }, - want: "", - want1: false, - }, - { - name: "multi no match domain", - fields: fields{ - cfg: Config{ - OIDC: OIDCConfig{ - MatchMap: map[string]string{ - ".*@dontknow.no": "never", - ".*@wedontknow.no": "other", - ".*\\.no": "stuffy", - }, - }, - }, - }, - args: args{ - email: "tasy@nonofthem.com", - }, - want: "", - want1: false, - }, - } - //nolint - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - app := &Headscale{ - cfg: test.fields.cfg, - db: test.fields.db, - dbString: test.fields.dbString, - dbType: test.fields.dbType, - dbDebug: test.fields.dbDebug, - privateKey: test.fields.privateKey, - aclPolicy: test.fields.aclPolicy, - aclRules: test.fields.aclRules, - lastStateChange: test.fields.lastStateChange, - oidcProvider: test.fields.oidcProvider, - oauth2Config: test.fields.oauth2Config, - oidcStateCache: test.fields.oidcStateCache, - } - got, got1 := app.getNamespaceFromEmail(test.args.email) - if got != test.want { - t.Errorf( - "Headscale.getNamespaceFromEmail() got = %v, want %v", - got, - test.want, - ) - } - if got1 != test.want1 { - t.Errorf( - "Headscale.getNamespaceFromEmail() got1 = %v, want %v", - got1, - test.want1, - ) - } - }) - } -} diff --git a/apple_mobileconfig.go b/platform_config.go similarity index 61% rename from apple_mobileconfig.go rename to platform_config.go index e5d9eede..d36a37ce 100644 --- a/apple_mobileconfig.go +++ b/platform_config.go @@ -4,33 +4,126 @@ import ( "bytes" "html/template" "net/http" + textTemplate "text/template" "github.com/gin-gonic/gin" "github.com/gofrs/uuid" "github.com/rs/zerolog/log" ) -// AppleMobileConfig shows a simple message in the browser to point to the CLI -// Listens in /register. -func (h *Headscale) AppleMobileConfig(ctx *gin.Context) { +// WindowsConfigMessage shows a simple message in the browser for how to configure the Windows Tailscale client. +func (h *Headscale) WindowsConfigMessage(ctx *gin.Context) { + winTemplate := template.Must(template.New("windows").Parse(` + + +

headscale

+

Windows registry configuration

+

+ This page provides Windows registry information for the official Windows Tailscale client. +

+

+ The registry file will configure Tailscale to use {{.URL}} as its control server. +

+

Caution

+

You should always download and inspect the registry file before installing it:

+
curl {{.URL}}/windows/tailscale.reg
+ +

Installation

+

Headscale can be set to the default server by running the registry file:

+ +

+ Windows registry file +

+ +
    +
  1. Download the registry file, then run it
  2. +
  3. Follow the prompts
  4. +
  5. Install and run the official windows Tailscale client
  6. +
  7. When the installation has finished, start Tailscale, and log in by clicking the icon in the system tray
  8. +
+

Or

+

Open command prompt with Administrator rights. Issue the following commands to add the required registry entries:

+
+REG ADD "HKLM\Software\Tailscale IPN" /v UnattendedMode /t REG_SZ /d always
+REG ADD "HKLM\Software\Tailscale IPN" /v LoginURL /t REG_SZ /d "{{.URL}}"
+

+ Restart Tailscale and log in. +

+ + +`)) + + config := map[string]interface{}{ + "URL": h.cfg.ServerURL, + } + + var payload bytes.Buffer + if err := winTemplate.Execute(&payload, config); err != nil { + log.Error(). + Str("handler", "WindowsRegConfig"). + Err(err). + Msg("Could not render Windows index template") + ctx.Data( + http.StatusInternalServerError, + "text/html; charset=utf-8", + []byte("Could not render Windows index template"), + ) + + return + } + + ctx.Data(http.StatusOK, "text/html; charset=utf-8", payload.Bytes()) +} + +// WindowsRegConfig generates and serves a .reg file configured with the Headscale server address. +func (h *Headscale) WindowsRegConfig(ctx *gin.Context) { + config := WindowsRegistryConfig{ + URL: h.cfg.ServerURL, + } + + var content bytes.Buffer + if err := windowsRegTemplate.Execute(&content, config); err != nil { + log.Error(). + Str("handler", "WindowsRegConfig"). + Err(err). + Msg("Could not render Apple macOS template") + ctx.Data( + http.StatusInternalServerError, + "text/html; charset=utf-8", + []byte("Could not render Windows registry template"), + ) + + return + } + + ctx.Data( + http.StatusOK, + "text/x-ms-regedit; charset=utf-8", + content.Bytes(), + ) +} + +// AppleConfigMessage shows a simple message in the browser to point the user to the iOS/MacOS profile and instructions for how to install it. +func (h *Headscale) AppleConfigMessage(ctx *gin.Context) { appleTemplate := template.Must(template.New("apple").Parse(` -

Apple configuration profiles

+

headscale

+

Apple configuration profiles

This page provides configuration profiles for the official Tailscale clients for iOS and macOS.

- The profiles will configure Tailscale.app to use {{.Url}} as its control server. + The profiles will configure Tailscale.app to use {{.URL}} as its control server.

Caution

-

You should always inspect the profile before installing it:

+

You should always download and inspect the profile before installing it:

-

curl {{.Url}}/apple/macos

- +
curl {{.URL}}/apple/macos
+

Profiles

- +

macOS

Headscale can be set to the default server by installing a Headscale configuration profile:

@@ -58,7 +151,7 @@ func (h *Headscale) AppleMobileConfig(ctx *gin.Context) { defaults write io.tailscale.ipn.macos ControlURL {{.URL}}

Restart Tailscale.app and log in.

- + `)) @@ -191,6 +284,10 @@ func (h *Headscale) ApplePlatformConfig(ctx *gin.Context) { ) } +type WindowsRegistryConfig struct { + URL string +} + type AppleMobileConfig struct { UUID uuid.UUID URL string @@ -202,8 +299,16 @@ type AppleMobilePlatformConfig struct { URL string } -var commonTemplate = template.Must( - template.New("mobileconfig").Parse(` +var windowsRegTemplate = textTemplate.Must( + textTemplate.New("windowsconfig").Parse(`Windows Registry Editor Version 5.00 + +[HKEY_LOCAL_MACHINE\SOFTWARE\Tailscale IPN] +"UnattendedMode"="always" +"LoginURL"="{{.URL}}" +`)) + +var commonTemplate = textTemplate.Must( + textTemplate.New("mobileconfig").Parse(` @@ -229,7 +334,7 @@ var commonTemplate = template.Must( `), ) -var iosTemplate = template.Must(template.New("iosTemplate").Parse(` +var iosTemplate = textTemplate.Must(textTemplate.New("iosTemplate").Parse(` PayloadType io.tailscale.ipn.ios diff --git a/poll.go b/poll.go index dd3956fb..15945a9b 100644 --- a/poll.go +++ b/poll.go @@ -2,7 +2,6 @@ package headscale import ( "context" - "encoding/json" "errors" "fmt" "io" @@ -11,7 +10,6 @@ import ( "github.com/gin-gonic/gin" "github.com/rs/zerolog/log" - "gorm.io/datatypes" "gorm.io/gorm" "tailscale.com/tailcfg" "tailscale.com/types/key" @@ -85,12 +83,33 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) { Str("machine", machine.Name). Msg("Found machine in database") - hostinfo, _ := json.Marshal(req.Hostinfo) - machine.Name = req.Hostinfo.Hostname - machine.HostInfo = datatypes.JSON(hostinfo) + hname, err := NormalizeToFQDNRules( + req.Hostinfo.Hostname, + h.cfg.OIDC.StripEmaildomain, + ) + if err != nil { + log.Error(). + Caller(). + Str("func", "handleAuthKey"). + Str("hostinfo.name", req.Hostinfo.Hostname). + Err(err) + } + machine.Name = hname + machine.HostInfo = HostInfo(*req.Hostinfo) machine.DiscoKey = DiscoPublicKeyStripPrefix(req.DiscoKey) now := time.Now().UTC() + // update ACLRules with peer informations (to update server tags if necessary) + if h.aclPolicy != nil { + err = h.UpdateACLRules() + if err != nil { + log.Error(). + Caller(). + Str("func", "handleAuthKey"). + Str("machine", machine.Name). + Err(err) + } + } // From Tailscale client: // // ReadOnly is whether the client just wants to fetch the MapResponse, @@ -100,8 +119,7 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) { // The intended use is for clients to discover the DERP map at start-up // before their first real endpoint update. if !req.ReadOnly { - endpoints, _ := json.Marshal(req.Endpoints) - machine.Endpoints = datatypes.JSON(endpoints) + machine.Endpoints = req.Endpoints machine.LastSeen = &now } h.db.Updates(machine) diff --git a/preauth_keys.go b/preauth_keys.go index 50bc4746..55f62226 100644 --- a/preauth_keys.go +++ b/preauth_keys.go @@ -113,6 +113,12 @@ func (h *Headscale) ExpirePreAuthKey(k *PreAuthKey) error { return nil } +// UsePreAuthKey marks a PreAuthKey as used. +func (h *Headscale) UsePreAuthKey(k *PreAuthKey) { + k.Used = true + h.db.Save(k) +} + // checkKeyValidity does the heavy lifting for validation of the PreAuthKey coming from a node // If returns no error and a PreAuthKey, it can be used. func (h *Headscale) checkKeyValidity(k string) (*PreAuthKey, error) { diff --git a/preauth_keys_test.go b/preauth_keys_test.go index f8cf276d..6f233a94 100644 --- a/preauth_keys_test.go +++ b/preauth_keys_test.go @@ -80,7 +80,6 @@ func (*Suite) TestAlreadyUsedKey(c *check.C) { DiscoKey: "faa", Name: "testest", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } @@ -105,7 +104,6 @@ func (*Suite) TestReusableBeingUsedKey(c *check.C) { DiscoKey: "faa", Name: "testest", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } @@ -143,7 +141,6 @@ func (*Suite) TestEphemeralKey(c *check.C) { DiscoKey: "faa", Name: "testest", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, LastSeen: &now, AuthKeyID: uint(pak.ID), diff --git a/proto/headscale/v1/headscale.proto b/proto/headscale/v1/headscale.proto index 3cbbb8ed..d6571b0d 100644 --- a/proto/headscale/v1/headscale.proto +++ b/proto/headscale/v1/headscale.proto @@ -104,18 +104,6 @@ service HeadscaleService { get: "/api/v1/machine" }; } - - rpc ShareMachine(ShareMachineRequest) returns (ShareMachineResponse) { - option (google.api.http) = { - post: "/api/v1/machine/{machine_id}/share/{namespace}" - }; - } - - rpc UnshareMachine(UnshareMachineRequest) returns (UnshareMachineResponse) { - option (google.api.http) = { - post: "/api/v1/machine/{machine_id}/unshare/{namespace}" - }; - } // --- Machine end --- // --- Route start --- diff --git a/proto/headscale/v1/machine.proto b/proto/headscale/v1/machine.proto index 47664e15..75552d78 100644 --- a/proto/headscale/v1/machine.proto +++ b/proto/headscale/v1/machine.proto @@ -22,16 +22,16 @@ message Machine { string name = 6; Namespace namespace = 7; - bool registered = 8; - RegisterMethod register_method = 9; - google.protobuf.Timestamp last_seen = 10; - google.protobuf.Timestamp last_successful_update = 11; - google.protobuf.Timestamp expiry = 12; + google.protobuf.Timestamp last_seen = 8; + google.protobuf.Timestamp last_successful_update = 9; + google.protobuf.Timestamp expiry = 10; - PreAuthKey pre_auth_key = 13; + PreAuthKey pre_auth_key = 11; - google.protobuf.Timestamp created_at = 14; + google.protobuf.Timestamp created_at = 12; + + RegisterMethod register_method = 13; // google.protobuf.Timestamp updated_at = 14; // google.protobuf.Timestamp deleted_at = 15; @@ -80,24 +80,6 @@ message ListMachinesResponse { repeated Machine machines = 1; } -message ShareMachineRequest { - uint64 machine_id = 1; - string namespace = 2; -} - -message ShareMachineResponse { - Machine machine = 1; -} - -message UnshareMachineRequest { - uint64 machine_id = 1; - string namespace = 2; -} - -message UnshareMachineResponse { - Machine machine = 1; -} - message DebugCreateMachineRequest { string namespace = 1; string key = 2; diff --git a/routes.go b/routes.go index 0065a03f..e8de299b 100644 --- a/routes.go +++ b/routes.go @@ -1,9 +1,6 @@ package headscale import ( - "encoding/json" - - "gorm.io/datatypes" "inet.af/netaddr" ) @@ -23,12 +20,7 @@ func (h *Headscale) GetAdvertisedNodeRoutes( return nil, err } - hostInfo, err := machine.GetHostInfo() - if err != nil { - return nil, err - } - - return &hostInfo.RoutableIPs, nil + return &machine.HostInfo.RoutableIPs, nil } // Deprecated: use machine function instead @@ -43,27 +35,7 @@ func (h *Headscale) GetEnabledNodeRoutes( return nil, err } - data, err := machine.EnabledRoutes.MarshalJSON() - if err != nil { - return nil, err - } - - routesStr := []string{} - err = json.Unmarshal(data, &routesStr) - if err != nil { - return nil, err - } - - routes := make([]netaddr.IPPrefix, len(routesStr)) - for index, routeStr := range routesStr { - route, err := netaddr.ParseIPPrefix(routeStr) - if err != nil { - return nil, err - } - routes[index] = route - } - - return routes, nil + return machine.EnabledRoutes, nil } // Deprecated: use machine function instead @@ -135,12 +107,7 @@ func (h *Headscale) EnableNodeRoute( return errRouteIsNotAvailable } - routes, err := json.Marshal(enabledRoutes) - if err != nil { - return err - } - - machine.EnabledRoutes = datatypes.JSON(routes) + machine.EnabledRoutes = enabledRoutes h.db.Save(&machine) return nil diff --git a/routes_test.go b/routes_test.go index 94bda45b..31b8d445 100644 --- a/routes_test.go +++ b/routes_test.go @@ -1,10 +1,7 @@ package headscale import ( - "encoding/json" - "gopkg.in/check.v1" - "gorm.io/datatypes" "inet.af/netaddr" "tailscale.com/tailcfg" ) @@ -25,8 +22,6 @@ func (s *Suite) TestGetRoutes(c *check.C) { hostInfo := tailcfg.Hostinfo{ RoutableIPs: []netaddr.IPPrefix{route}, } - hostinfo, err := json.Marshal(hostInfo) - c.Assert(err, check.IsNil) machine := Machine{ ID: 0, @@ -35,10 +30,9 @@ func (s *Suite) TestGetRoutes(c *check.C) { DiscoKey: "faa", Name: "test_get_route_machine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), - HostInfo: datatypes.JSON(hostinfo), + HostInfo: HostInfo(hostInfo), } app.db.Save(&machine) @@ -79,8 +73,6 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) { hostInfo := tailcfg.Hostinfo{ RoutableIPs: []netaddr.IPPrefix{route, route2}, } - hostinfo, err := json.Marshal(hostInfo) - c.Assert(err, check.IsNil) machine := Machine{ ID: 0, @@ -89,10 +81,9 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) { DiscoKey: "faa", Name: "test_enable_route_machine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), - HostInfo: datatypes.JSON(hostinfo), + HostInfo: HostInfo(hostInfo), } app.db.Save(&machine) diff --git a/sharing.go b/sharing.go deleted file mode 100644 index caac5319..00000000 --- a/sharing.go +++ /dev/null @@ -1,81 +0,0 @@ -package headscale - -import "gorm.io/gorm" - -const ( - errSameNamespace = Error("Destination namespace same as origin") - errMachineAlreadyShared = Error("Node already shared to this namespace") - errMachineNotShared = Error("Machine not shared to this namespace") -) - -// SharedMachine is a join table to support sharing nodes between namespaces. -type SharedMachine struct { - gorm.Model - MachineID uint64 - Machine Machine - NamespaceID uint - Namespace Namespace -} - -// AddSharedMachineToNamespace adds a machine as a shared node to a namespace. -func (h *Headscale) AddSharedMachineToNamespace( - machine *Machine, - namespace *Namespace, -) error { - if machine.NamespaceID == namespace.ID { - return errSameNamespace - } - - sharedMachines := []SharedMachine{} - if err := h.db.Where("machine_id = ? AND namespace_id = ?", machine.ID, namespace.ID).Find(&sharedMachines).Error; err != nil { - return err - } - if len(sharedMachines) > 0 { - return errMachineAlreadyShared - } - - sharedMachine := SharedMachine{ - MachineID: machine.ID, - Machine: *machine, - NamespaceID: namespace.ID, - Namespace: *namespace, - } - h.db.Save(&sharedMachine) - - return nil -} - -// RemoveSharedMachineFromNamespace removes a shared machine from a namespace. -func (h *Headscale) RemoveSharedMachineFromNamespace( - machine *Machine, - namespace *Namespace, -) error { - if machine.NamespaceID == namespace.ID { - // Can't unshare from primary namespace - return errMachineNotShared - } - - sharedMachine := SharedMachine{} - result := h.db.Where("machine_id = ? AND namespace_id = ?", machine.ID, namespace.ID). - Unscoped(). - Delete(&sharedMachine) - if result.Error != nil { - return result.Error - } - - if result.RowsAffected == 0 { - return errMachineNotShared - } - - return nil -} - -// RemoveSharedMachineFromAllNamespaces removes a machine as a shared node from all namespaces. -func (h *Headscale) RemoveSharedMachineFromAllNamespaces(machine *Machine) error { - sharedMachine := SharedMachine{} - if result := h.db.Where("machine_id = ?", machine.ID).Unscoped().Delete(&sharedMachine); result.Error != nil { - return result.Error - } - - return nil -} diff --git a/sharing_test.go b/sharing_test.go deleted file mode 100644 index b7fef4e2..00000000 --- a/sharing_test.go +++ /dev/null @@ -1,341 +0,0 @@ -package headscale - -import ( - "gopkg.in/check.v1" - "inet.af/netaddr" -) - -func CreateNodeNamespace( - c *check.C, - namespaceName, node, key, ip string, -) (*Namespace, *Machine) { - namespace, err := app.CreateNamespace(namespaceName) - c.Assert(err, check.IsNil) - - pak1, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) - c.Assert(err, check.IsNil) - - _, err = app.GetMachine(namespace.Name, node) - c.Assert(err, check.NotNil) - - machine := &Machine{ - ID: 0, - MachineKey: key, - NodeKey: key, - DiscoKey: key, - Name: node, - NamespaceID: namespace.ID, - Registered: true, - RegisterMethod: RegisterMethodAuthKey, - IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")}, - AuthKeyID: uint(pak1.ID), - } - app.db.Save(machine) - - _, err = app.GetMachine(namespace.Name, machine.Name) - c.Assert(err, check.IsNil) - - return namespace, machine -} - -func (s *Suite) TestBasicSharedNodesInNamespace(c *check.C) { - namespace1, machine1 := CreateNodeNamespace( - c, - "shared1", - "test_get_shared_nodes_1", - "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", - "100.64.0.1", - ) - _, machine2 := CreateNodeNamespace( - c, - "shared2", - "test_get_shared_nodes_2", - "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", - "100.64.0.2", - ) - - peersOfMachine1BeforeShared, err := app.getPeers(machine1) - c.Assert(err, check.IsNil) - c.Assert(len(peersOfMachine1BeforeShared), check.Equals, 0) - - err = app.AddSharedMachineToNamespace(machine2, namespace1) - c.Assert(err, check.IsNil) - - peersOfMachine1AfterShared, err := app.getPeers(machine1) - c.Assert(err, check.IsNil) - c.Assert(len(peersOfMachine1AfterShared), check.Equals, 1) - c.Assert(peersOfMachine1AfterShared[0].ID, check.Equals, machine2.ID) -} - -func (s *Suite) TestSameNamespace(c *check.C) { - namespace1, machine1 := CreateNodeNamespace( - c, - "shared1", - "test_get_shared_nodes_1", - "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", - "100.64.0.1", - ) - - peersOfMachine1BeforeShare, err := app.getPeers(machine1) - c.Assert(err, check.IsNil) - c.Assert(len(peersOfMachine1BeforeShare), check.Equals, 0) - - err = app.AddSharedMachineToNamespace(machine1, namespace1) - c.Assert(err, check.Equals, errSameNamespace) -} - -func (s *Suite) TestUnshare(c *check.C) { - namespace1, machine1 := CreateNodeNamespace( - c, - "shared1", - "test_unshare_1", - "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", - "100.64.0.1", - ) - _, machine2 := CreateNodeNamespace( - c, - "shared2", - "test_unshare_2", - "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", - "100.64.0.2", - ) - - peersOfMachine1BeforeShare, err := app.getPeers(machine1) - c.Assert(err, check.IsNil) - c.Assert(len(peersOfMachine1BeforeShare), check.Equals, 0) - - err = app.AddSharedMachineToNamespace(machine2, namespace1) - c.Assert(err, check.IsNil) - - peersOfMachine1BeforeShare, err = app.getShared(machine1) - c.Assert(err, check.IsNil) - c.Assert(len(peersOfMachine1BeforeShare), check.Equals, 1) - - err = app.RemoveSharedMachineFromNamespace(machine2, namespace1) - c.Assert(err, check.IsNil) - - peersOfMachine1BeforeShare, err = app.getShared(machine1) - c.Assert(err, check.IsNil) - c.Assert(len(peersOfMachine1BeforeShare), check.Equals, 0) - - err = app.RemoveSharedMachineFromNamespace(machine2, namespace1) - c.Assert(err, check.Equals, errMachineNotShared) - - err = app.RemoveSharedMachineFromNamespace(machine1, namespace1) - c.Assert(err, check.Equals, errMachineNotShared) -} - -func (s *Suite) TestAlreadyShared(c *check.C) { - namespace1, machine1 := CreateNodeNamespace( - c, - "shared1", - "test_get_shared_nodes_1", - "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", - "100.64.0.1", - ) - _, machine2 := CreateNodeNamespace( - c, - "shared2", - "test_get_shared_nodes_2", - "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", - "100.64.0.2", - ) - - peersOfMachine1BeforeShare, err := app.getPeers(machine1) - c.Assert(err, check.IsNil) - c.Assert(len(peersOfMachine1BeforeShare), check.Equals, 0) - - err = app.AddSharedMachineToNamespace(machine2, namespace1) - c.Assert(err, check.IsNil) - err = app.AddSharedMachineToNamespace(machine2, namespace1) - c.Assert(err, check.Equals, errMachineAlreadyShared) -} - -func (s *Suite) TestDoNotIncludeRoutesOnShared(c *check.C) { - namespace1, machine1 := CreateNodeNamespace( - c, - "shared1", - "test_get_shared_nodes_1", - "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", - "100.64.0.1", - ) - _, machine2 := CreateNodeNamespace( - c, - "shared2", - "test_get_shared_nodes_2", - "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", - "100.64.0.2", - ) - - peersOfMachine1BeforeShare, err := app.getPeers(machine1) - c.Assert(err, check.IsNil) - c.Assert(len(peersOfMachine1BeforeShare), check.Equals, 0) - - err = app.AddSharedMachineToNamespace(machine2, namespace1) - c.Assert(err, check.IsNil) - - peersOfMachine1AfterShare, err := app.getPeers(machine1) - c.Assert(err, check.IsNil) - c.Assert(len(peersOfMachine1AfterShare), check.Equals, 1) - c.Assert(peersOfMachine1AfterShare[0].Name, check.Equals, "test_get_shared_nodes_2") -} - -func (s *Suite) TestComplexSharingAcrossNamespaces(c *check.C) { - namespace1, machine1 := CreateNodeNamespace( - c, - "shared1", - "test_get_shared_nodes_1", - "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", - "100.64.0.1", - ) - _, machine2 := CreateNodeNamespace( - c, - "shared2", - "test_get_shared_nodes_2", - "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", - "100.64.0.2", - ) - _, machine3 := CreateNodeNamespace( - c, - "shared3", - "test_get_shared_nodes_3", - "6e704bee83eb93db6fc2c417d7882964cd3f8cc87082cbb645982e34020c76c8", - "100.64.0.3", - ) - - pak4, err := app.CreatePreAuthKey(namespace1.Name, false, false, nil) - c.Assert(err, check.IsNil) - - machine4 := &Machine{ - ID: 4, - MachineKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f", - NodeKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f", - DiscoKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f", - Name: "test_get_shared_nodes_4", - NamespaceID: namespace1.ID, - Registered: true, - RegisterMethod: RegisterMethodAuthKey, - IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")}, - AuthKeyID: uint(pak4.ID), - } - app.db.Save(machine4) - - _, err = app.GetMachine(namespace1.Name, machine4.Name) - c.Assert(err, check.IsNil) - - peersOfMachine1BeforeShare, err := app.getPeers(machine1) - c.Assert(err, check.IsNil) - c.Assert(len(peersOfMachine1BeforeShare), check.Equals, 1) // node1 can see node4 - c.Assert(peersOfMachine1BeforeShare[0].Name, check.Equals, machine4.Name) - - err = app.AddSharedMachineToNamespace(machine2, namespace1) - c.Assert(err, check.IsNil) - - peersOfMachine1AfterShare, err := app.getPeers(machine1) - c.Assert(err, check.IsNil) - c.Assert( - len(peersOfMachine1AfterShare), - check.Equals, - 2, - ) // node1 can see node2 (shared) and node4 (same namespace) - c.Assert(peersOfMachine1AfterShare[0].Name, check.Equals, machine2.Name) - c.Assert(peersOfMachine1AfterShare[1].Name, check.Equals, machine4.Name) - - sharedOfMachine1, err := app.getShared(machine1) - c.Assert(err, check.IsNil) - c.Assert(len(sharedOfMachine1), check.Equals, 1) // node1 can see node2 as shared - c.Assert(sharedOfMachine1[0].Name, check.Equals, machine2.Name) - - peersOfMachine3, err := app.getPeers(machine3) - c.Assert(err, check.IsNil) - c.Assert(len(peersOfMachine3), check.Equals, 0) // node3 is alone - - peersOfMachine2, err := app.getPeers(machine2) - c.Assert(err, check.IsNil) - c.Assert( - len(peersOfMachine2), - check.Equals, - 2, - ) // node2 should see node1 (sharedTo) and node4 (sharedTo), as is shared in namespace1 - c.Assert(peersOfMachine2[0].Name, check.Equals, machine1.Name) - c.Assert(peersOfMachine2[1].Name, check.Equals, machine4.Name) -} - -func (s *Suite) TestDeleteSharedMachine(c *check.C) { - namespace1, machine1 := CreateNodeNamespace( - c, - "shared1", - "test_get_shared_nodes_1", - "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", - "100.64.0.1", - ) - _, machine2 := CreateNodeNamespace( - c, - "shared2", - "test_get_shared_nodes_2", - "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", - "100.64.0.2", - ) - _, machine3 := CreateNodeNamespace( - c, - "shared3", - "test_get_shared_nodes_3", - "6e704bee83eb93db6fc2c417d7882964cd3f8cc87082cbb645982e34020c76c8", - "100.64.0.3", - ) - - pak4n1, err := app.CreatePreAuthKey(namespace1.Name, false, false, nil) - c.Assert(err, check.IsNil) - machine4 := &Machine{ - ID: 4, - MachineKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f", - NodeKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f", - DiscoKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f", - Name: "test_get_shared_nodes_4", - NamespaceID: namespace1.ID, - Registered: true, - RegisterMethod: RegisterMethodAuthKey, - IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")}, - AuthKeyID: uint(pak4n1.ID), - } - app.db.Save(machine4) - - _, err = app.GetMachine(namespace1.Name, machine4.Name) - c.Assert(err, check.IsNil) - - peersOfMachine1BeforeShare, err := app.getPeers(machine1) - c.Assert(err, check.IsNil) - c.Assert(len(peersOfMachine1BeforeShare), check.Equals, 1) // nodes 1 and 4 - c.Assert(peersOfMachine1BeforeShare[0].Name, check.Equals, machine4.Name) - - err = app.AddSharedMachineToNamespace(machine2, namespace1) - c.Assert(err, check.IsNil) - - peersOfMachine1AfterShare, err := app.getPeers(machine1) - c.Assert(err, check.IsNil) - c.Assert(len(peersOfMachine1AfterShare), check.Equals, 2) // nodes 1, 2, 4 - c.Assert(peersOfMachine1AfterShare[0].Name, check.Equals, machine2.Name) - c.Assert(peersOfMachine1AfterShare[1].Name, check.Equals, machine4.Name) - - sharedOfMachine1, err := app.getShared(machine1) - c.Assert(err, check.IsNil) - c.Assert(len(sharedOfMachine1), check.Equals, 1) // nodes 1, 2, 4 - c.Assert(sharedOfMachine1[0].Name, check.Equals, machine2.Name) - - peersOfMachine3, err := app.getPeers(machine3) - c.Assert(err, check.IsNil) - c.Assert(len(peersOfMachine3), check.Equals, 0) // node 3 is alone - - sharedMachinesInNamespace1, err := app.ListSharedMachinesInNamespace( - namespace1.Name, - ) - c.Assert(err, check.IsNil) - c.Assert(len(sharedMachinesInNamespace1), check.Equals, 1) - - err = app.DeleteMachine(machine2) - c.Assert(err, check.IsNil) - - sharedMachinesInNamespace1, err = app.ListSharedMachinesInNamespace(namespace1.Name) - c.Assert(err, check.IsNil) - c.Assert(len(sharedMachinesInNamespace1), check.Equals, 0) -} diff --git a/tests/acls/acl_policy_basic_wildcards.yaml b/tests/acls/acl_policy_basic_wildcards.yaml new file mode 100644 index 00000000..8e7c817f --- /dev/null +++ b/tests/acls/acl_policy_basic_wildcards.yaml @@ -0,0 +1,10 @@ +--- +Hosts: + host-1: 100.100.100.100/32 + subnet-1: 100.100.101.100/24 +ACLs: + - Action: accept + Users: + - "*" + Ports: + - host-1:* diff --git a/utils.go b/utils.go index a6be8cc4..af267eb3 100644 --- a/utils.go +++ b/utils.go @@ -157,9 +157,6 @@ func GetIPPrefixEndpoints(na netaddr.IPPrefix) (network, broadcast netaddr.IP) { return } -// TODO: Is this concurrency safe? -// What would happen if multiple hosts were to register at the same time? -// Would we attempt to assign the same addresses to multiple nodes? func (h *Headscale) getAvailableIP(ipPrefix netaddr.IPPrefix) (*netaddr.IP, error) { usedIps, err := h.getUsedIPs() if err != nil { @@ -179,7 +176,7 @@ func (h *Headscale) getAvailableIP(ipPrefix netaddr.IPPrefix) (*netaddr.IP, erro switch { case ip.Compare(ipPrefixBroadcastAddress) == 0: fallthrough - case containsIPs(usedIps, ip): + case usedIps.Contains(ip): fallthrough case ip.IsZero() || ip.IsLoopback(): ip = ip.Next() @@ -192,29 +189,43 @@ func (h *Headscale) getAvailableIP(ipPrefix netaddr.IPPrefix) (*netaddr.IP, erro } } -func (h *Headscale) getUsedIPs() ([]netaddr.IP, error) { +func (h *Headscale) getUsedIPs() (*netaddr.IPSet, error) { // FIXME: This really deserves a better data model, // but this was quick to get running and it should be enough // to begin experimenting with a dual stack tailnet. var addressesSlices []string h.db.Model(&Machine{}).Pluck("ip_addresses", &addressesSlices) - ips := make([]netaddr.IP, 0, len(h.cfg.IPPrefixes)*len(addressesSlices)) + var ips netaddr.IPSetBuilder for _, slice := range addressesSlices { - var a MachineAddresses - err := a.Scan(slice) + var machineAddresses MachineAddresses + err := machineAddresses.Scan(slice) if err != nil { - return nil, fmt.Errorf("failed to read ip from database: %w", err) + return &netaddr.IPSet{}, fmt.Errorf( + "failed to read ip from database: %w", + err, + ) + } + + for _, ip := range machineAddresses { + ips.Add(ip) } - ips = append(ips, a...) } - return ips, nil + ipSet, err := ips.IPSet() + if err != nil { + return &netaddr.IPSet{}, fmt.Errorf( + "failed to build IP Set: %w", + err, + ) + } + + return ipSet, nil } -func containsIPs(ips []netaddr.IP, ip netaddr.IP) bool { - for _, v := range ips { - if v == ip { +func containsString(ss []string, s string) bool { + for _, v := range ss { + if v == s { return true } } diff --git a/utils_test.go b/utils_test.go index feb44d5a..7c72c09a 100644 --- a/utils_test.go +++ b/utils_test.go @@ -20,7 +20,7 @@ func (s *Suite) TestGetUsedIps(c *check.C) { ips, err := app.getAvailableIPs() c.Assert(err, check.IsNil) - namespace, err := app.CreateNamespace("test_ip") + namespace, err := app.CreateNamespace("test-ip") c.Assert(err, check.IsNil) pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) @@ -36,7 +36,6 @@ func (s *Suite) TestGetUsedIps(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), IPAddresses: ips, @@ -48,9 +47,12 @@ func (s *Suite) TestGetUsedIps(c *check.C) { c.Assert(err, check.IsNil) expected := netaddr.MustParseIP("10.27.0.1") + expectedIPSetBuilder := netaddr.IPSetBuilder{} + expectedIPSetBuilder.Add(expected) + expectedIPSet, _ := expectedIPSetBuilder.IPSet() - c.Assert(len(usedIps), check.Equals, 1) - c.Assert(usedIps[0], check.Equals, expected) + c.Assert(usedIps.Equal(expectedIPSet), check.Equals, true) + c.Assert(usedIps.Contains(expected), check.Equals, true) machine1, err := app.GetMachineByID(0) c.Assert(err, check.IsNil) @@ -64,6 +66,8 @@ func (s *Suite) TestGetMultiIp(c *check.C) { c.Assert(err, check.IsNil) for index := 1; index <= 350; index++ { + app.ipAllocationMutex.Lock() + ips, err := app.getAvailableIPs() c.Assert(err, check.IsNil) @@ -80,23 +84,35 @@ func (s *Suite) TestGetMultiIp(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), IPAddresses: ips, } app.db.Save(&machine) + + app.ipAllocationMutex.Unlock() } usedIps, err := app.getUsedIPs() - c.Assert(err, check.IsNil) - c.Assert(len(usedIps), check.Equals, 350) + expected0 := netaddr.MustParseIP("10.27.0.1") + expected9 := netaddr.MustParseIP("10.27.0.10") + expected300 := netaddr.MustParseIP("10.27.0.45") - c.Assert(usedIps[0], check.Equals, netaddr.MustParseIP("10.27.0.1")) - c.Assert(usedIps[9], check.Equals, netaddr.MustParseIP("10.27.0.10")) - c.Assert(usedIps[300], check.Equals, netaddr.MustParseIP("10.27.1.45")) + notExpectedIPSetBuilder := netaddr.IPSetBuilder{} + notExpectedIPSetBuilder.Add(expected0) + notExpectedIPSetBuilder.Add(expected9) + notExpectedIPSetBuilder.Add(expected300) + notExpectedIPSet, err := notExpectedIPSetBuilder.IPSet() + c.Assert(err, check.IsNil) + + // We actually expect it to be a lot larger + c.Assert(usedIps.Equal(notExpectedIPSet), check.Equals, false) + + c.Assert(usedIps.Contains(expected0), check.Equals, true) + c.Assert(usedIps.Contains(expected9), check.Equals, true) + c.Assert(usedIps.Contains(expected300), check.Equals, true) // Check that we can read back the IPs machine1, err := app.GetMachineByID(1) @@ -142,7 +158,7 @@ func (s *Suite) TestGetAvailableIpMachineWithoutIP(c *check.C) { c.Assert(len(ips), check.Equals, 1) c.Assert(ips[0].String(), check.Equals, expected.String()) - namespace, err := app.CreateNamespace("test_ip") + namespace, err := app.CreateNamespace("test-ip") c.Assert(err, check.IsNil) pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) @@ -158,7 +174,6 @@ func (s *Suite) TestGetAvailableIpMachineWithoutIP(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), }