diff --git a/.dockerignore b/.dockerignore index f90134b3..057a20e7 100644 --- a/.dockerignore +++ b/.dockerignore @@ -15,3 +15,4 @@ README.md LICENSE .vscode +*.sock diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..f0902132 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,28 @@ +--- +name: "Bug report" +about: "Create a bug report to help us improve" +title: "" +labels: ["bug"] +assignees: "" +--- + +**Bug description** + + + +**To Reproduce** + + + +**Context info** + + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..99cc36fa --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +# Issues must have some content +blank_issues_enabled: false + +# Contact links +contact_links: + - name: "headscale usage documentation" + url: "https://github.com/juanfont/headscale/blob/main/docs" + about: "Find documentation about how to configure and run headscale." + - name: "headscale Discord community" + url: "https://discord.com/invite/XcQxk2VHjx" + about: "Please ask and answer questions about usage of headscale here." diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..f4cd6e71 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,15 @@ +--- +name: "Feature request" +about: "Suggest an idea for headscale" +title: "" +labels: ["enhancement"] +assignees: "" +--- + +**Feature request** + + + + diff --git a/.github/ISSUE_TEMPLATE/other_issue.md b/.github/ISSUE_TEMPLATE/other_issue.md new file mode 100644 index 00000000..76811e67 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/other_issue.md @@ -0,0 +1,28 @@ +--- +name: "Other issue" +about: "Report a different issue" +title: "" +labels: ["bug"] +assignees: "" +--- + + + +**Issue description** + + + +**To Reproduce** + + + +**Context info** + + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..c7015806 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +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 + + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f1773af9..d92e9e6e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,23 +14,38 @@ jobs: steps: - uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v14.1 + with: + files: | + go.* + **/*.go + integration_test/ + config-example.yaml - name: Setup Go + if: steps.changed-files.outputs.any_changed == 'true' uses: actions/setup-go@v2 with: - go-version: "1.16.3" + go-version: "1.17" - name: Install dependencies + if: steps.changed-files.outputs.any_changed == 'true' run: | go version - go install golang.org/x/lint/golint@latest sudo apt update sudo apt install -y make - - name: Run lint + - name: Run build + if: steps.changed-files.outputs.any_changed == 'true' run: make build - uses: actions/upload-artifact@v2 + if: steps.changed-files.outputs.any_changed == 'true' with: name: headscale-linux path: headscale diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c0286571..26a24ae7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,39 +1,74 @@ +--- name: CI on: [push, pull_request] jobs: - # The "build" workflow - lint: - # The type of runner that the job will run on + golangci-lint: runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 - - # Install and run golangci-lint as a separate step, it's much faster this - # way because this action has caching. It'll get run again in `make lint` - # below, but it's still much faster in the end than installing - # golangci-lint manually in the `Run lint` step. - - uses: golangci/golangci-lint-action@v2 with: - args: --timeout 5m + fetch-depth: 2 - # Setup Go - - name: Setup Go - uses: actions/setup-go@v2 + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v14.1 with: - go-version: "1.16.3" # The Go version to download (if necessary) and use. + files: | + go.* + **/*.go + integration_test/ + config-example.yaml - # Install all the dependencies - - name: Install dependencies - run: | - go version - go install golang.org/x/lint/golint@latest - sudo apt update - sudo apt install -y make + - name: golangci-lint + if: steps.changed-files.outputs.any_changed == 'true' + uses: golangci/golangci-lint-action@v2 + with: + version: latest - - name: Run lint - run: make lint + # Only block PRs on new problems. + # If this is not enabled, we will end up having PRs + # blocked because new linters has appared and other + # parts of the code is affected. + only-new-issues: true + + prettier-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v14.1 + with: + files: | + **/*.md + **/*.yml + **/*.yaml + **/*.ts + **/*.js + **/*.sass + **/*.css + **/*.scss + **/*.html + + - name: Prettify code + if: steps.changed-files.outputs.any_changed == 'true' + uses: creyD/prettier_action@v4.0 + with: + prettier_options: >- + --check **/*.{ts,js,md,yaml,yml,sass,css,scss,html} + only_changed: false + dry: true + + proto-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: bufbuild/buf-setup-action@v0.7.0 + - uses: bufbuild/buf-lint-action@v1 + with: + input: "proto" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c0605f10..c9a40bc0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.16 + go-version: 1.17 - name: Install dependencies run: | @@ -40,6 +40,19 @@ jobs: uses: actions/checkout@v2 with: fetch-depth: 0 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Set up QEMU for multiple platforms + uses: docker/setup-qemu-action@master + with: + platforms: arm64,amd64 + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- - name: Docker meta id: meta uses: docker/metadata-action@v3 @@ -52,6 +65,7 @@ jobs: type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} + type=raw,value=latest type=sha - name: Login to DockerHub uses: docker/login-action@v1 @@ -72,3 +86,138 @@ jobs: context: . tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new + - name: Prepare cache for next build + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache + + docker-debug-release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Set up QEMU for multiple platforms + uses: docker/setup-qemu-action@master + with: + platforms: arm64,amd64 + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache-debug + key: ${{ runner.os }}-buildx-debug-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx-debug- + - name: Docker meta + id: meta-debug + uses: docker/metadata-action@v3 + with: + # list of Docker images to use as base name for tags + images: | + ${{ secrets.DOCKERHUB_USERNAME }}/headscale + ghcr.io/${{ github.repository_owner }}/headscale + flavor: | + latest=false + tags: | + type=semver,pattern={{version}}-debug + type=semver,pattern={{major}}.{{minor}}-debug + type=semver,pattern={{major}}-debug + type=raw,value=latest-debug + type=sha,suffix=-debug + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Login to GHCR + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push + id: docker_build + uses: docker/build-push-action@v2 + with: + push: true + context: . + file: Dockerfile.debug + tags: ${{ steps.meta-debug.outputs.tags }} + labels: ${{ steps.meta-debug.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=local,src=/tmp/.buildx-cache-debug + cache-to: type=local,dest=/tmp/.buildx-cache-debug-new + - name: Prepare cache for next build + run: | + rm -rf /tmp/.buildx-cache-debug + mv /tmp/.buildx-cache-debug-new /tmp/.buildx-cache-debug + + docker-alpine-release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Set up QEMU for multiple platforms + uses: docker/setup-qemu-action@master + with: + platforms: arm64,amd64 + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache-alpine + key: ${{ runner.os }}-buildx-alpine-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx-alpine- + - name: Docker meta + id: meta-alpine + uses: docker/metadata-action@v3 + with: + # list of Docker images to use as base name for tags + images: | + ${{ secrets.DOCKERHUB_USERNAME }}/headscale + ghcr.io/${{ github.repository_owner }}/headscale + flavor: | + latest=false + tags: | + type=semver,pattern={{version}}-alpine + type=semver,pattern={{major}}.{{minor}}-alpine + type=semver,pattern={{major}}-alpine + type=raw,value=latest-alpine + type=sha,suffix=-alpine + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Login to GHCR + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push + id: docker_build + uses: docker/build-push-action@v2 + with: + push: true + context: . + file: Dockerfile.alpine + tags: ${{ steps.meta-alpine.outputs.tags }} + labels: ${{ steps.meta-alpine.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=local,src=/tmp/.buildx-cache-alpine + cache-to: type=local,dest=/tmp/.buildx-cache-alpine-new + - name: Prepare cache for next build + run: | + rm -rf /tmp/.buildx-cache-alpine + mv /tmp/.buildx-cache-alpine-new /tmp/.buildx-cache-alpine diff --git a/.github/workflows/test-integration.yml b/.github/workflows/test-integration.yml index e939df22..9f526f97 100644 --- a/.github/workflows/test-integration.yml +++ b/.github/workflows/test-integration.yml @@ -3,21 +3,30 @@ name: CI on: [pull_request] jobs: - # The "build" workflow integration-test: - # The type of runner that the job will run on runs-on: ubuntu-latest - # Steps represent a sequence of tasks that will be executed as part of the job steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v14.1 + with: + files: | + go.* + **/*.go + integration_test/ + config-example.yaml - # Setup Go - name: Setup Go + if: steps.changed-files.outputs.any_changed == 'true' uses: actions/setup-go@v2 with: - go-version: "1.16.3" + go-version: "1.17" - name: Run Integration tests + if: steps.changed-files.outputs.any_changed == 'true' run: go test -tags integration -timeout 30m diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3d254fa6..9ce8a779 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,31 +3,41 @@ name: CI on: [push, pull_request] jobs: - # The "build" workflow test: - # The type of runner that the job will run on runs-on: ubuntu-latest - # Steps represent a sequence of tasks that will be executed as part of the job steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v14.1 + with: + files: | + go.* + **/*.go + integration_test/ + config-example.yaml - # Setup Go - name: Setup Go + if: steps.changed-files.outputs.any_changed == 'true' uses: actions/setup-go@v2 with: - go-version: "1.16.3" # The Go version to download (if necessary) and use. + go-version: "1.17" - # Install all the dependencies - name: Install dependencies + if: steps.changed-files.outputs.any_changed == 'true' run: | go version sudo apt update sudo apt install -y make - name: Run tests + if: steps.changed-files.outputs.any_changed == 'true' run: make test - name: Run build + if: steps.changed-files.outputs.any_changed == 'true' run: make diff --git a/.gitignore b/.gitignore index 610550b9..d047cbfd 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,8 @@ /headscale config.json config.yaml +derp.yaml +*.hujson *.key /db.sqlite *.sqlite3 diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 00000000..965f5496 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,56 @@ +--- +run: + timeout: 10m + +issues: + skip-dirs: + - gen +linters: + enable-all: true + disable: + - exhaustivestruct + - revive + - lll + - interfacer + - scopelint + - maligned + - golint + - gofmt + - gochecknoglobals + - gochecknoinits + - gocognit + - funlen + - exhaustivestruct + - tagliatelle + - godox + - ireturn + + # We should strive to enable these: + - wrapcheck + - dupl + - makezero + + # We might want to enable this, but it might be a lot of work + - cyclop + - nestif + - wsl # might be incompatible with gofumpt + - testpackage + - paralleltest + +linters-settings: + varnamelen: + ignore-type-assert-ok: true + ignore-map-index-ok: true + ignore-names: + - err + - db + - id + - ip + - ok + - c + + gocritic: + disabled-checks: + - appendAssign + # TODO(kradalby): Remove this + - ifElseChain diff --git a/.goreleaser.yml b/.goreleaser.yml index d20aed6b..d1dec26f 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,8 +1,11 @@ -# This is an example .goreleaser.yml file with some sane defaults. -# Make sure to check the documentation at http://goreleaser.com +--- before: hooks: - - go mod tidy + - go mod tidy -compat=1.17 + +release: + prerelease: auto + builds: - id: darwin-amd64 main: ./cmd/headscale/headscale.go @@ -29,7 +32,7 @@ builds: goarch: - arm goarm: - - 7 + - "7" env: - CC=arm-linux-gnueabihf-gcc - CXX=arm-linux-gnueabihf-g++ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..e9ac7191 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,60 @@ +# CHANGELOG + +**TBD (TBD):** + +**Changes**: + +**0.12.4 (2022-01-29):** + +**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):** + +**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):** + +Happy New Year! + +**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):** + +(We are skipping 0.12.0 to correct a mishap done weeks ago with the version tagging) + +**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: + 1. Delete your current key + 2. Restart `headscale`, a new key will be generated. + 3. Restart all Tailscale clients to fetch the new key + +**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**: + +- 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):** + +**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 6e216aad..d5fe3421 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,21 @@ -FROM golang:1.17.1-bullseye AS build +# Builder image +FROM golang:1.17.6-bullseye AS build ENV GOPATH /go +WORKDIR /go/src/headscale COPY go.mod go.sum /go/src/headscale/ -WORKDIR /go/src/headscale RUN go mod download -COPY . /go/src/headscale +COPY . . RUN go install -a -ldflags="-extldflags=-static" -tags netgo,sqlite_omit_load_extension ./cmd/headscale +RUN strip /go/bin/headscale RUN test -e /go/bin/headscale -FROM ubuntu:20.04 +# Production image +FROM gcr.io/distroless/base-debian11 -RUN apt-get update \ - && apt-get install -y ca-certificates \ - && update-ca-certificates \ - && rm -rf /var/lib/apt/lists/* - -COPY --from=build /go/bin/headscale /usr/local/bin/headscale +COPY --from=build /go/bin/headscale /bin/headscale ENV TZ UTC EXPOSE 8080/tcp diff --git a/Dockerfile.alpine b/Dockerfile.alpine new file mode 100644 index 00000000..8185dbe4 --- /dev/null +++ b/Dockerfile.alpine @@ -0,0 +1,23 @@ +# Builder image +FROM golang:1.17.6-alpine AS build +ENV GOPATH /go +WORKDIR /go/src/headscale + +COPY go.mod go.sum /go/src/headscale/ +RUN apk add gcc musl-dev +RUN go mod download + +COPY . . + +RUN go install -a -ldflags="-extldflags=-static" -tags netgo,sqlite_omit_load_extension ./cmd/headscale +RUN strip /go/bin/headscale +RUN test -e /go/bin/headscale + +# Production image +FROM alpine:latest + +COPY --from=build /go/bin/headscale /bin/headscale +ENV TZ UTC + +EXPOSE 8080/tcp +CMD ["headscale"] diff --git a/Dockerfile.debug b/Dockerfile.debug new file mode 100644 index 00000000..320b5c93 --- /dev/null +++ b/Dockerfile.debug @@ -0,0 +1,23 @@ +# Builder image +FROM golang:1.17.1-bullseye AS build +ENV GOPATH /go +WORKDIR /go/src/headscale + +COPY go.mod go.sum /go/src/headscale/ +RUN go mod download + +COPY . . + +RUN go install -a -ldflags="-extldflags=-static" -tags netgo,sqlite_omit_load_extension ./cmd/headscale +RUN test -e /go/bin/headscale + +# Debug image +FROM gcr.io/distroless/base-debian11:debug + +COPY --from=build /go/bin/headscale /bin/headscale +ENV TZ UTC + +# Need to reset the entrypoint or everything will run as a busybox script +ENTRYPOINT [] +EXPOSE 8080/tcp +CMD ["headscale"] diff --git a/Makefile b/Makefile index 755253fc..6b4c02ff 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,14 @@ # Calculate version version = $(shell ./scripts/version-at-commit.sh) +rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)) + +# GO_SOURCES = $(wildcard *.go) +# PROTO_SOURCES = $(wildcard **/*.proto) +GO_SOURCES = $(call rwildcard,,*.go) +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 @@ -12,6 +20,9 @@ test: test_integration: go test -tags integration -timeout 30m ./... +test_integration_cli: + go test -tags integration -v integration_cli_test.go integration_common_test.go + coverprofile_func: go tool cover -func=coverage.out @@ -19,9 +30,26 @@ coverprofile_html: go tool cover -html=coverage.out lint: - golint - golangci-lint run --timeout 5m + golangci-lint run --fix --timeout 10m + +fmt: + prettier --write '**/**.{ts,js,md,yaml,yml,sass,css,scss,html}' + golines --max-len=88 --base-formatter=gofumpt -w $(GO_SOURCES) + clang-format -style="{BasedOnStyle: Google, IndentWidth: 4, AlignConsecutiveDeclarations: true, AlignConsecutiveAssignments: true, ColumnLimit: 0}" -i $(PROTO_SOURCES) + +proto-lint: + cd proto/ && buf lint compress: build upx --brute headscale +generate: + rm -rf gen + buf generate proto + +install-protobuf-plugins: + go install \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \ + google.golang.org/protobuf/cmd/protoc-gen-go \ + google.golang.org/grpc/cmd/protoc-gen-go-grpc diff --git a/README.md b/README.md index a3c09396..9a599d3d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ An open source, self-hosted implementation of the Tailscale coordination 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. + ## Overview 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 all kinds of [NAT traversal sorcery](https://tailscale.com/blog/how-nat-traversal-works/). @@ -29,6 +31,7 @@ headscale implements this coordination server. - [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/`) @@ -47,17 +50,67 @@ headscale implements this coordination server. Suggestions/PRs welcomed! - ## Running headscale Please have a look at the documentation under [`docs/`](docs/). - ## Disclaimer 1. We have nothing to do with Tailscale, or Tailscale Inc. -2. The purpose of writing this was to learn how Tailscale works. +2. The purpose of Headscale is maintaining a working, self-hosted Tailscale control panel. +## Contributing + +To contribute to Headscale you would need the lastest version of [Go](https://golang.org) and [Buf](https://buf.build)(Protobuf generator). + +### Code style + +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 +[`gofumpt`](https://github.com/mvdan/gofumpt). +Please configure your editor to run the tools while developing and make sure to +run `make lint` and `make fmt` before committing any code. + +The **Proto** code is linted with [`buf`](https://docs.buf.build/lint/overview) and +formatted with [`clang-format`](https://clang.llvm.org/docs/ClangFormat.html). + +The **rest** (Markdown, YAML, etc) is formatted with [`prettier`](https://prettier.io). + +Check out the `.golangci.yaml` and `Makefile` to see the specific configuration. + +### Install development tools + +- Go +- Buf +- Protobuf tools: + +```shell +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: + +```shell +make generate +``` + +**Note**: Please check in changes from `gen/` in a separate commit to make it easier to review. + +To run the tests: + +```shell +make test +``` + +To build the program: + +```shell +make build +``` ## Contributors @@ -91,6 +144,13 @@ Please have a look at the documentation under [`docs/`](docs/). ohdearaugustin + + + unreality/ +
+ unreality +
+ Aaron @@ -98,6 +158,8 @@ Please have a look at the documentation under [`docs/`](docs/). Aaron Bieber + + Paul @@ -105,8 +167,6 @@ Please have a look at the documentation under [`docs/`](docs/). Paul Tötterman - - Casey @@ -142,6 +202,8 @@ Please have a look at the documentation under [`docs/`](docs/). Felix Kronlage-Dammers + + Felix @@ -149,8 +211,6 @@ Please have a look at the documentation under [`docs/`](docs/). Felix Yan - - Shaanan @@ -186,6 +246,8 @@ Please have a look at the documentation under [`docs/`](docs/). Tjerk Woudsma + + Zakhar @@ -193,8 +255,6 @@ Please have a look at the documentation under [`docs/`](docs/). Zakhar Bessarab - - derelm/ @@ -218,5 +278,3 @@ Please have a look at the documentation under [`docs/`](docs/). - - diff --git a/acls.go b/acls.go index fea72a7f..1d54b26d 100644 --- a/acls.go +++ b/acls.go @@ -9,22 +9,39 @@ import ( "strings" "github.com/rs/zerolog/log" - "github.com/tailscale/hujson" "inet.af/netaddr" "tailscale.com/tailcfg" ) -const errorEmptyPolicy = Error("empty policy") -const errorInvalidAction = Error("invalid action") -const errorInvalidUserSection = Error("invalid user section") -const errorInvalidGroup = Error("invalid group") -const errorInvalidTag = Error("invalid tag") -const errorInvalidNamespace = Error("invalid namespace") -const errorInvalidPortFormat = Error("invalid port format") +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") +) -// LoadACLPolicy loads the ACL policy from the specify path, and generates the ACL rules +const ( + Base8 = 8 + Base10 = 10 + BitSize16 = 16 + BitSize32 = 32 + BitSize64 = 64 + portRangeBegin = 0 + portRangeEnd = 65535 + expectedTokenItems = 2 +) + +// LoadACLPolicy loads the ACL policy from the specify path, and generates the ACL rules. func (h *Headscale) LoadACLPolicy(path string) error { + log.Debug(). + Str("func", "LoadACLPolicy"). + Str("path", path). + Msg("Loading ACL policy from path") + policyFile, err := os.Open(path) if err != nil { return err @@ -32,16 +49,23 @@ func (h *Headscale) LoadACLPolicy(path string) error { defer policyFile.Close() var policy ACLPolicy - b, err := io.ReadAll(policyFile) + policyBytes, err := io.ReadAll(policyFile) if err != nil { return err } - err = hujson.Unmarshal(b, &policy) + + 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 errorEmptyPolicy + return errEmptyPolicy } h.aclPolicy = &policy @@ -50,40 +74,45 @@ func (h *Headscale) LoadACLPolicy(path string) error { return err } h.aclRules = rules + + log.Trace().Interface("ACL", rules).Msg("ACL rules generated") + return nil } -func (h *Headscale) generateACLRules() (*[]tailcfg.FilterRule, error) { +func (h *Headscale) generateACLRules() ([]tailcfg.FilterRule, error) { rules := []tailcfg.FilterRule{} - for i, a := range h.aclPolicy.ACLs { - if a.Action != "accept" { - return nil, errorInvalidAction + for index, acl := range h.aclPolicy.ACLs { + if acl.Action != "accept" { + return nil, errInvalidAction } - r := tailcfg.FilterRule{} + filterRule := tailcfg.FilterRule{} srcIPs := []string{} - for j, u := range a.Users { - srcs, err := h.generateACLPolicySrcIP(u) + for innerIndex, user := range acl.Users { + srcs, err := h.generateACLPolicySrcIP(user) if err != nil { log.Error(). - Msgf("Error parsing ACL %d, User %d", i, j) + Msgf("Error parsing ACL %d, User %d", index, innerIndex) + return nil, err } - srcIPs = append(srcIPs, *srcs...) + srcIPs = append(srcIPs, srcs...) } - r.SrcIPs = srcIPs + filterRule.SrcIPs = srcIPs destPorts := []tailcfg.NetPortRange{} - for j, d := range a.Ports { - dests, err := h.generateACLPolicyDestPorts(d) + for innerIndex, ports := range acl.Ports { + dests, err := h.generateACLPolicyDestPorts(ports) if err != nil { log.Error(). - Msgf("Error parsing ACL %d, Port %d", i, j) + Msgf("Error parsing ACL %d, Port %d", index, innerIndex) + return nil, err } - destPorts = append(destPorts, *dests...) + destPorts = append(destPorts, dests...) } rules = append(rules, tailcfg.FilterRule{ @@ -92,17 +121,19 @@ func (h *Headscale) generateACLRules() (*[]tailcfg.FilterRule, error) { }) } - return &rules, nil + return rules, nil } -func (h *Headscale) generateACLPolicySrcIP(u string) (*[]string, error) { +func (h *Headscale) generateACLPolicySrcIP(u string) ([]string, error) { return h.expandAlias(u) } -func (h *Headscale) generateACLPolicyDestPorts(d string) (*[]tailcfg.NetPortRange, error) { +func (h *Headscale) generateACLPolicyDestPorts( + d string, +) ([]tailcfg.NetPortRange, error) { tokens := strings.Split(d, ":") - if len(tokens) < 2 || len(tokens) > 3 { - return nil, errorInvalidPortFormat + if len(tokens) < expectedTokenItems || len(tokens) > 3 { + return nil, errInvalidPortFormat } var alias string @@ -112,7 +143,7 @@ func (h *Headscale) generateACLPolicyDestPorts(d string) (*[]tailcfg.NetPortRang // tag:montreal-webserver:80,443 // tag:api-server:443 // example-host-1:* - if len(tokens) == 2 { + if len(tokens) == expectedTokenItems { alias = tokens[0] } else { alias = fmt.Sprintf("%s:%s", tokens[0], tokens[1]) @@ -128,7 +159,7 @@ func (h *Headscale) generateACLPolicyDestPorts(d string) (*[]tailcfg.NetPortRang } dests := []tailcfg.NetPortRange{} - for _, d := range *expanded { + for _, d := range expanded { for _, p := range *ports { pr := tailcfg.NetPortRange{ IP: d, @@ -137,34 +168,36 @@ func (h *Headscale) generateACLPolicyDestPorts(d string) (*[]tailcfg.NetPortRang dests = append(dests, pr) } } - return &dests, nil + + return dests, nil } -func (h *Headscale) expandAlias(s string) (*[]string, error) { - if s == "*" { - return &[]string{"*"}, nil +func (h *Headscale) expandAlias(alias string) ([]string, error) { + if alias == "*" { + return []string{"*"}, nil } - if strings.HasPrefix(s, "group:") { - if _, ok := h.aclPolicy.Groups[s]; !ok { - return nil, errorInvalidGroup + if strings.HasPrefix(alias, "group:") { + if _, ok := h.aclPolicy.Groups[alias]; !ok { + return nil, errInvalidGroup } ips := []string{} - for _, n := range h.aclPolicy.Groups[s] { + for _, n := range h.aclPolicy.Groups[alias] { nodes, err := h.ListMachinesInNamespace(n) if err != nil { - return nil, errorInvalidNamespace + return nil, errInvalidNamespace } - for _, node := range *nodes { + for _, node := range nodes { ips = append(ips, node.IPAddress) } } - return &ips, nil + + return ips, nil } - if strings.HasPrefix(s, "tag:") { - if _, ok := h.aclPolicy.TagOwners[s]; !ok { - return nil, errorInvalidTag + if strings.HasPrefix(alias, "tag:") { + if _, ok := h.aclPolicy.TagOwners[alias]; !ok { + return nil, errInvalidTag } // This will have HORRIBLE performance. @@ -174,10 +207,10 @@ func (h *Headscale) expandAlias(s string) (*[]string, error) { return nil, err } ips := []string{} - for _, m := range machines { + for _, machine := range machines { hostinfo := tailcfg.Hostinfo{} - if len(m.HostInfo) != 0 { - hi, err := m.HostInfo.MarshalJSON() + if len(machine.HostInfo) != 0 { + hi, err := machine.HostInfo.MarshalJSON() if err != nil { return nil, err } @@ -188,69 +221,76 @@ func (h *Headscale) expandAlias(s string) (*[]string, error) { // FIXME: Check TagOwners allows this for _, t := range hostinfo.RequestTags { - if s[4:] == t { - ips = append(ips, m.IPAddress) + if alias[4:] == t { + ips = append(ips, machine.IPAddress) + break } } } } - return &ips, nil + + return ips, nil } - n, err := h.GetNamespace(s) + 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 { + for _, n := range nodes { ips = append(ips, n.IPAddress) } - return &ips, nil + + return ips, nil } - if h, ok := h.aclPolicy.Hosts[s]; ok { - return &[]string{h.String()}, nil + if h, ok := h.aclPolicy.Hosts[alias]; ok { + return []string{h.String()}, nil } - ip, err := netaddr.ParseIP(s) + ip, err := netaddr.ParseIP(alias) if err == nil { - return &[]string{ip.String()}, nil + return []string{ip.String()}, nil } - cidr, err := netaddr.ParseIPPrefix(s) + cidr, err := netaddr.ParseIPPrefix(alias) if err == nil { - return &[]string{cidr.String()}, nil + return []string{cidr.String()}, nil } - return nil, errorInvalidUserSection + return nil, errInvalidUserSection } -func (h *Headscale) expandPorts(s string) (*[]tailcfg.PortRange, error) { - if s == "*" { - return &[]tailcfg.PortRange{{First: 0, Last: 65535}}, nil +func (h *Headscale) expandPorts(portsStr string) (*[]tailcfg.PortRange, error) { + if portsStr == "*" { + return &[]tailcfg.PortRange{ + {First: portRangeBegin, Last: portRangeEnd}, + }, nil } ports := []tailcfg.PortRange{} - for _, p := range strings.Split(s, ",") { - rang := strings.Split(p, "-") - if len(rang) == 1 { - pi, err := strconv.ParseUint(rang[0], 10, 16) + for _, portStr := range strings.Split(portsStr, ",") { + rang := strings.Split(portStr, "-") + switch len(rang) { + case 1: + port, err := strconv.ParseUint(rang[0], Base10, BitSize16) if err != nil { return nil, err } ports = append(ports, tailcfg.PortRange{ - First: uint16(pi), - Last: uint16(pi), + First: uint16(port), + Last: uint16(port), }) - } else if len(rang) == 2 { - start, err := strconv.ParseUint(rang[0], 10, 16) + + case expectedTokenItems: + start, err := strconv.ParseUint(rang[0], Base10, BitSize16) if err != nil { return nil, err } - last, err := strconv.ParseUint(rang[1], 10, 16) + last, err := strconv.ParseUint(rang[1], Base10, BitSize16) if err != nil { return nil, err } @@ -258,9 +298,11 @@ func (h *Headscale) expandPorts(s string) (*[]tailcfg.PortRange, error) { First: uint16(start), Last: uint16(last), }) - } else { - return nil, errorInvalidPortFormat + + default: + return nil, errInvalidPortFormat } } + return &ports, nil } diff --git a/acls_test.go b/acls_test.go index dc5b4b31..629ce1da 100644 --- a/acls_test.go +++ b/acls_test.go @@ -5,156 +5,161 @@ import ( ) func (s *Suite) TestWrongPath(c *check.C) { - err := h.LoadACLPolicy("asdfg") + err := app.LoadACLPolicy("asdfg") c.Assert(err, check.NotNil) } func (s *Suite) TestBrokenHuJson(c *check.C) { - err := h.LoadACLPolicy("./tests/acls/broken.hujson") + err := app.LoadACLPolicy("./tests/acls/broken.hujson") c.Assert(err, check.NotNil) - } func (s *Suite) TestInvalidPolicyHuson(c *check.C) { - err := h.LoadACLPolicy("./tests/acls/invalid.hujson") + err := app.LoadACLPolicy("./tests/acls/invalid.hujson") c.Assert(err, check.NotNil) - c.Assert(err, check.Equals, errorEmptyPolicy) + c.Assert(err, check.Equals, errEmptyPolicy) } func (s *Suite) TestParseHosts(c *check.C) { - var hs Hosts - err := hs.UnmarshalJSON([]byte(`{"example-host-1": "100.100.100.100","example-host-2": "100.100.101.100/24"}`)) - c.Assert(hs, check.NotNil) + var hosts Hosts + err := hosts.UnmarshalJSON( + []byte( + `{"example-host-1": "100.100.100.100","example-host-2": "100.100.101.100/24"}`, + ), + ) + c.Assert(hosts, check.NotNil) c.Assert(err, check.IsNil) } func (s *Suite) TestParseInvalidCIDR(c *check.C) { - var hs Hosts - err := hs.UnmarshalJSON([]byte(`{"example-host-1": "100.100.100.100/42"}`)) - c.Assert(hs, check.IsNil) + var hosts Hosts + err := hosts.UnmarshalJSON([]byte(`{"example-host-1": "100.100.100.100/42"}`)) + c.Assert(hosts, check.IsNil) c.Assert(err, check.NotNil) } func (s *Suite) TestRuleInvalidGeneration(c *check.C) { - err := h.LoadACLPolicy("./tests/acls/acl_policy_invalid.hujson") + err := app.LoadACLPolicy("./tests/acls/acl_policy_invalid.hujson") c.Assert(err, check.NotNil) } func (s *Suite) TestBasicRule(c *check.C) { - err := h.LoadACLPolicy("./tests/acls/acl_policy_basic_1.hujson") + err := app.LoadACLPolicy("./tests/acls/acl_policy_basic_1.hujson") c.Assert(err, check.IsNil) - rules, err := h.generateACLRules() + rules, err := app.generateACLRules() c.Assert(err, check.IsNil) c.Assert(rules, check.NotNil) } func (s *Suite) TestPortRange(c *check.C) { - err := h.LoadACLPolicy("./tests/acls/acl_policy_basic_range.hujson") + err := app.LoadACLPolicy("./tests/acls/acl_policy_basic_range.hujson") c.Assert(err, check.IsNil) - rules, err := h.generateACLRules() + 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(5400)) - c.Assert((*rules)[0].DstPorts[0].Ports.Last, check.Equals, uint16(5500)) + 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(5400)) + c.Assert((rules)[0].DstPorts[0].Ports.Last, check.Equals, uint16(5500)) } func (s *Suite) TestPortWildcard(c *check.C) { - err := h.LoadACLPolicy("./tests/acls/acl_policy_basic_wildcards.hujson") + err := app.LoadACLPolicy("./tests/acls/acl_policy_basic_wildcards.hujson") c.Assert(err, check.IsNil) - rules, err := h.generateACLRules() + 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, "*") + 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) { - n, err := h.CreateNamespace("testnamespace") + namespace, err := app.CreateNamespace("testnamespace") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, false, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) c.Assert(err, check.IsNil) - _, err = h.GetMachine("testnamespace", "testmachine") + _, err = app.GetMachine("testnamespace", "testmachine") c.Assert(err, check.NotNil) - ip, _ := h.getAvailableIP() - m := Machine{ + ip, _ := app.getAvailableIP() + machine := Machine{ ID: 0, MachineKey: "foo", NodeKey: "bar", DiscoKey: "faa", Name: "testmachine", - NamespaceID: n.ID, + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, IPAddress: ip.String(), AuthKeyID: uint(pak.ID), } - h.db.Save(&m) + app.db.Save(&machine) - err = h.LoadACLPolicy("./tests/acls/acl_policy_basic_namespace_as_user.hujson") + err = app.LoadACLPolicy( + "./tests/acls/acl_policy_basic_namespace_as_user.hujson", + ) c.Assert(err, check.IsNil) - rules, err := h.generateACLRules() + 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.Not(check.Equals), "not an ip") - c.Assert((*rules)[0].SrcIPs[0], check.Equals, ip.String()) + 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.Not(check.Equals), "not an ip") + c.Assert((rules)[0].SrcIPs[0], check.Equals, ip.String()) } func (s *Suite) TestPortGroup(c *check.C) { - n, err := h.CreateNamespace("testnamespace") + namespace, err := app.CreateNamespace("testnamespace") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, false, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) c.Assert(err, check.IsNil) - _, err = h.GetMachine("testnamespace", "testmachine") + _, err = app.GetMachine("testnamespace", "testmachine") c.Assert(err, check.NotNil) - ip, _ := h.getAvailableIP() - m := Machine{ + ip, _ := app.getAvailableIP() + machine := Machine{ ID: 0, MachineKey: "foo", NodeKey: "bar", DiscoKey: "faa", Name: "testmachine", - NamespaceID: n.ID, + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, IPAddress: ip.String(), AuthKeyID: uint(pak.ID), } - h.db.Save(&m) + app.db.Save(&machine) - err = h.LoadACLPolicy("./tests/acls/acl_policy_basic_groups.hujson") + err = app.LoadACLPolicy("./tests/acls/acl_policy_basic_groups.hujson") c.Assert(err, check.IsNil) - rules, err := h.generateACLRules() + 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.Not(check.Equals), "not an ip") - c.Assert((*rules)[0].SrcIPs[0], check.Equals, ip.String()) + 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.Not(check.Equals), "not an ip") + c.Assert((rules)[0].SrcIPs[0], check.Equals, ip.String()) } diff --git a/acls_types.go b/acls_types.go index 01e42d50..08e650ff 100644 --- a/acls_types.go +++ b/acls_types.go @@ -1,13 +1,14 @@ package headscale import ( + "encoding/json" "strings" "github.com/tailscale/hujson" "inet.af/netaddr" ) -// ACLPolicy represents a Tailscale ACL Policy +// ACLPolicy represents a Tailscale ACL Policy. type ACLPolicy struct { Groups Groups `json:"Groups"` Hosts Hosts `json:"Hosts"` @@ -16,55 +17,63 @@ type ACLPolicy struct { Tests []ACLTest `json:"Tests"` } -// ACL is a basic rule for the ACL Policy +// ACL is a basic rule for the ACL Policy. type ACL struct { Action string `json:"Action"` Users []string `json:"Users"` Ports []string `json:"Ports"` } -// Groups references a series of alias in the ACL rules +// Groups references a series of alias in the ACL rules. type Groups map[string][]string -// Hosts are alias for IP addresses or subnets +// Hosts are alias for IP addresses or subnets. type Hosts map[string]netaddr.IPPrefix -// TagOwners specify what users (namespaces?) are allow to use certain tags +// TagOwners specify what users (namespaces?) are allow to use certain tags. type TagOwners map[string][]string -// ACLTest is not implemented, but should be use to check if a certain rule is allowed +// 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"` } -// UnmarshalJSON allows to parse the Hosts directly into netaddr objects -func (h *Hosts) UnmarshalJSON(data []byte) error { - hosts := Hosts{} - hs := make(map[string]string) - err := hujson.Unmarshal(data, &hs) +// UnmarshalJSON allows to parse the Hosts directly into netaddr objects. +func (hosts *Hosts) UnmarshalJSON(data []byte) error { + newHosts := Hosts{} + hostIPPrefixMap := make(map[string]string) + ast, err := hujson.Parse(data) if err != nil { return err } - for k, v := range hs { - if !strings.Contains(v, "/") { - v = v + "/32" + ast.Standardize() + data = ast.Pack() + err = json.Unmarshal(data, &hostIPPrefixMap) + if err != nil { + return err + } + for host, prefixStr := range hostIPPrefixMap { + if !strings.Contains(prefixStr, "/") { + prefixStr += "/32" } - prefix, err := netaddr.ParseIPPrefix(v) + prefix, err := netaddr.ParseIPPrefix(prefixStr) if err != nil { return err } - hosts[k] = prefix + newHosts[host] = prefix } - *h = hosts + *hosts = newHosts + return nil } -// IsZero is perhaps a bit naive here -func (p ACLPolicy) IsZero() bool { - if len(p.Groups) == 0 && len(p.Hosts) == 0 && len(p.ACLs) == 0 { +// 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 { return true } + return false } diff --git a/api.go b/api.go index a31cf529..d3bd5726 100644 --- a/api.go +++ b/api.go @@ -1,40 +1,51 @@ package headscale import ( + "bytes" "encoding/binary" "encoding/json" "errors" "fmt" + "html/template" "io" "net/http" + "strings" "time" - "github.com/rs/zerolog/log" - "github.com/gin-gonic/gin" "github.com/klauspost/compress/zstd" + "github.com/rs/zerolog/log" "gorm.io/gorm" "tailscale.com/tailcfg" - "tailscale.com/types/wgkey" + "tailscale.com/types/key" +) + +const ( + reservedResponseHeaderSize = 4 + RegisterMethodAuthKey = "authKey" + RegisterMethodOIDC = "oidc" + RegisterMethodCLI = "cli" + ErrRegisterMethodCLIDoesNotSupportExpire = Error( + "machines registered with CLI does not support expire", + ) ) // KeyHandler provides the Headscale pub key -// Listens in /key -func (h *Headscale) KeyHandler(c *gin.Context) { - c.Data(200, "text/plain; charset=utf-8", []byte(h.publicKey.HexString())) +// Listens in /key. +func (h *Headscale) KeyHandler(ctx *gin.Context) { + ctx.Data( + http.StatusOK, + "text/plain; charset=utf-8", + []byte(MachinePublicKeyStripPrefix(h.privateKey.Public())), + ) } -// RegisterWebAPI shows a simple message in the browser to point to the CLI -// Listens in /register -func (h *Headscale) RegisterWebAPI(c *gin.Context) { - mKeyStr := c.Query("key") - if mKeyStr == "" { - c.String(http.StatusBadRequest, "Wrong params") - return - } +type registerWebAPITemplateConfig struct { + Key string +} - c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(fmt.Sprintf(` - +var registerWebAPITemplate = template.Must( + template.New("registerweb").Parse(`

headscale

@@ -43,228 +54,193 @@ func (h *Headscale) RegisterWebAPI(c *gin.Context) {

- headscale -n NAMESPACE nodes register %s + headscale -n NAMESPACE nodes register --key {{.Key}}

- + `), +) - `, mKeyStr))) +// RegisterWebAPI shows a simple message in the browser to point to the CLI +// Listens in /register. +func (h *Headscale) RegisterWebAPI(ctx *gin.Context) { + machineKeyStr := ctx.Query("key") + if machineKeyStr == "" { + ctx.String(http.StatusBadRequest, "Wrong params") + + return + } + + var content bytes.Buffer + if err := registerWebAPITemplate.Execute(&content, registerWebAPITemplateConfig{ + Key: machineKeyStr, + }); err != nil { + log.Error(). + Str("func", "RegisterWebAPI"). + Err(err). + Msg("Could not render register web API template") + ctx.Data( + http.StatusInternalServerError, + "text/html; charset=utf-8", + []byte("Could not render register web API template"), + ) + } + + ctx.Data(http.StatusOK, "text/html; charset=utf-8", content.Bytes()) } // RegistrationHandler handles the actual registration process of a machine -// Endpoint /machine/:id -func (h *Headscale) RegistrationHandler(c *gin.Context) { - body, _ := io.ReadAll(c.Request.Body) - mKeyStr := c.Param("id") - mKey, err := wgkey.ParseHex(mKeyStr) +// Endpoint /machine/:id. +func (h *Headscale) RegistrationHandler(ctx *gin.Context) { + body, _ := io.ReadAll(ctx.Request.Body) + machineKeyStr := ctx.Param("id") + + var machineKey key.MachinePublic + err := machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machineKeyStr))) if err != nil { log.Error(). - Str("handler", "Registration"). + Caller(). Err(err). Msg("Cannot parse machine key") - machineRegistrations.WithLabelValues("unkown", "web", "error", "unknown").Inc() - c.String(http.StatusInternalServerError, "Sad!") + machineRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc() + ctx.String(http.StatusInternalServerError, "Sad!") + return } req := tailcfg.RegisterRequest{} - err = decode(body, &req, &mKey, h.privateKey) + err = decode(body, &req, &machineKey, h.privateKey) if err != nil { log.Error(). - Str("handler", "Registration"). + Caller(). Err(err). Msg("Cannot decode message") - machineRegistrations.WithLabelValues("unkown", "web", "error", "unknown").Inc() - c.String(http.StatusInternalServerError, "Very sad!") + machineRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc() + ctx.String(http.StatusInternalServerError, "Very sad!") + return } now := time.Now().UTC() - var m Machine - if result := h.db.Preload("Namespace").First(&m, "machine_key = ?", mKey.HexString()); errors.Is( - result.Error, - gorm.ErrRecordNotFound, - ) { + machine, err := h.GetMachineByMachineKey(machineKey) + if errors.Is(err, gorm.ErrRecordNotFound) { log.Info().Str("machine", req.Hostinfo.Hostname).Msg("New machine") - m = Machine{ - Expiry: &req.Expiry, - MachineKey: mKey.HexString(), - Name: req.Hostinfo.Hostname, - NodeKey: wgkey.Key(req.NodeKey).HexString(), - LastSuccessfulUpdate: &now, + newMachine := Machine{ + Expiry: &time.Time{}, + MachineKey: MachinePublicKeyStripPrefix(machineKey), + Name: req.Hostinfo.Hostname, } - if err := h.db.Create(&m).Error; err != nil { + if err := h.db.Create(&newMachine).Error; err != nil { log.Error(). - Str("handler", "Registration"). + Caller(). Err(err). Msg("Could not create row") - machineRegistrations.WithLabelValues("unkown", "web", "error", m.Namespace.Name).Inc() + machineRegistrations.WithLabelValues("unknown", "web", "error", machine.Namespace.Name). + Inc() + return } + machine = &newMachine } - if !m.Registered && req.Auth.AuthKey != "" { - h.handleAuthKey(c, h.db, mKey, req, m) - return - } + if machine.Registered { + // 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) + // - A valid, registered machine, looking for the node map + // - Expired machine wanting to reauthenticate + if machine.NodeKey == NodePublicKeyStripPrefix(req.NodeKey) { + // The client sends an Expiry in the past if the client is requesting to expire the key (aka logout) + // https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go#L648 + if !req.Expiry.IsZero() && req.Expiry.UTC().Before(now) { + h.handleMachineLogOut(ctx, machineKey, *machine) - resp := tailcfg.RegisterResponse{} - - // We have the updated key! - if m.NodeKey == wgkey.Key(req.NodeKey).HexString() { - if m.Registered { - log.Debug(). - Str("handler", "Registration"). - Str("machine", m.Name). - Msg("Client is registered and we have the current NodeKey. All clear to /map") - - resp.AuthURL = "" - resp.MachineAuthorized = true - resp.User = *m.Namespace.toUser() - respBody, err := encode(resp, &mKey, h.privateKey) - if err != nil { - log.Error(). - Str("handler", "Registration"). - Err(err). - Msg("Cannot encode message") - machineRegistrations.WithLabelValues("update", "web", "error", m.Namespace.Name).Inc() - c.String(http.StatusInternalServerError, "") return } - machineRegistrations.WithLabelValues("update", "web", "success", m.Namespace.Name).Inc() - c.Data(200, "application/json; charset=utf-8", respBody) + + // If machine is not expired, and is register, we have a already accepted this machine, + // let it proceed with a valid registration + if !machine.isExpired() { + h.handleMachineValidRegistration(ctx, machineKey, *machine) + + return + } + } + + // The NodeKey we have matches OldNodeKey, which means this is a refresh after a key expiration + if machine.NodeKey == NodePublicKeyStripPrefix(req.OldNodeKey) && + !machine.isExpired() { + h.handleMachineRefreshKey(ctx, machineKey, req, *machine) + return } - log.Debug(). - Str("handler", "Registration"). - Str("machine", m.Name). - Msg("Not registered and not NodeKey rotation. Sending a authurl to register") - resp.AuthURL = fmt.Sprintf("%s/register?key=%s", - h.cfg.ServerURL, mKey.HexString()) - respBody, err := encode(resp, &mKey, h.privateKey) - if err != nil { - log.Error(). - Str("handler", "Registration"). - Err(err). - Msg("Cannot encode message") - machineRegistrations.WithLabelValues("new", "web", "error", m.Namespace.Name).Inc() - c.String(http.StatusInternalServerError, "") - return - } - machineRegistrations.WithLabelValues("new", "web", "success", m.Namespace.Name).Inc() - c.Data(200, "application/json; charset=utf-8", respBody) + // The machine has expired + h.handleMachineExpired(ctx, machineKey, req, *machine) + return } - // The NodeKey we have matches OldNodeKey, which means this is a refresh after an key expiration - if m.NodeKey == wgkey.Key(req.OldNodeKey).HexString() { - log.Debug(). - Str("handler", "Registration"). - Str("machine", m.Name). - Msg("We have the OldNodeKey in the database. This is a key refresh") - m.NodeKey = wgkey.Key(req.NodeKey).HexString() - h.db.Save(&m) + // If the machine has AuthKey set, handle registration via PreAuthKeys + if req.Auth.AuthKey != "" { + h.handleAuthKey(ctx, machineKey, req, *machine) - resp.AuthURL = "" - resp.User = *m.Namespace.toUser() - respBody, err := encode(resp, &mKey, h.privateKey) - if err != nil { - log.Error(). - Str("handler", "Registration"). - Err(err). - Msg("Cannot encode message") - c.String(http.StatusInternalServerError, "Extremely sad!") - return - } - c.Data(200, "application/json; charset=utf-8", respBody) return } - // We arrive here after a client is restarted without finalizing the authentication flow or - // when headscale is stopped in the middle of the auth process. - if m.Registered { - log.Debug(). - Str("handler", "Registration"). - Str("machine", m.Name). - Msg("The node is sending us a new NodeKey, but machine is registered. All clear for /map") - resp.AuthURL = "" - resp.MachineAuthorized = true - resp.User = *m.Namespace.toUser() - respBody, err := encode(resp, &mKey, h.privateKey) - if err != nil { - log.Error(). - Str("handler", "Registration"). - Err(err). - Msg("Cannot encode message") - c.String(http.StatusInternalServerError, "") - return - } - c.Data(200, "application/json; charset=utf-8", respBody) - return - } - - log.Debug(). - Str("handler", "Registration"). - Str("machine", m.Name). - Msg("The node is sending us a new NodeKey, sending auth url") - resp.AuthURL = fmt.Sprintf("%s/register?key=%s", - h.cfg.ServerURL, mKey.HexString()) - respBody, err := encode(resp, &mKey, h.privateKey) - if err != nil { - log.Error(). - Str("handler", "Registration"). - Err(err). - Msg("Cannot encode message") - c.String(http.StatusInternalServerError, "") - return - } - c.Data(200, "application/json; charset=utf-8", respBody) + h.handleMachineRegistrationNew(ctx, machineKey, req, *machine) } -func (h *Headscale) getMapResponse(mKey wgkey.Key, req tailcfg.MapRequest, m *Machine) ([]byte, error) { +func (h *Headscale) getMapResponse( + machineKey key.MachinePublic, + req tailcfg.MapRequest, + machine *Machine, +) ([]byte, error) { log.Trace(). Str("func", "getMapResponse"). Str("machine", req.Hostinfo.Hostname). Msg("Creating Map response") - node, err := m.toNode(h.cfg.BaseDomain, h.cfg.DNSConfig, true) + node, err := machine.toNode(h.cfg.BaseDomain, h.cfg.DNSConfig, true) if err != nil { log.Error(). + Caller(). Str("func", "getMapResponse"). Err(err). Msg("Cannot convert to node") + return nil, err } - peers, err := h.getPeers(m) + peers, err := h.getValidPeers(machine) if err != nil { log.Error(). + Caller(). Str("func", "getMapResponse"). Err(err). Msg("Cannot fetch peers") + return nil, err } - profiles := getMapResponseUserProfiles(*m, peers) + profiles := getMapResponseUserProfiles(*machine, peers) nodePeers, err := peers.toNodes(h.cfg.BaseDomain, h.cfg.DNSConfig, true) if err != nil { log.Error(). + Caller(). Str("func", "getMapResponse"). Err(err). Msg("Failed to convert peers to Tailscale nodes") + return nil, err } - dnsConfig, err := getMapResponseDNSConfig(h.cfg.DNSConfig, h.cfg.BaseDomain, *m, peers) - if err != nil { - log.Error(). - Str("func", "getMapResponse"). - Err(err). - Msg("Failed generate the DNSConfig") - return nil, err - } + dnsConfig := getMapResponseDNSConfig( + h.cfg.DNSConfig, + h.cfg.BaseDomain, + *machine, + peers, + ) resp := tailcfg.MapResponse{ KeepAlive: false, @@ -272,7 +248,7 @@ func (h *Headscale) getMapResponse(mKey wgkey.Key, req tailcfg.MapRequest, m *Ma Peers: nodePeers, DNSConfig: dnsConfig, Domain: h.cfg.BaseDomain, - PacketFilter: *h.aclRules, + PacketFilter: h.aclRules, DERPMap: h.DERPMap, UserProfiles: profiles, } @@ -289,137 +265,351 @@ func (h *Headscale) getMapResponse(mKey wgkey.Key, req tailcfg.MapRequest, m *Ma encoder, _ := zstd.NewWriter(nil) srcCompressed := encoder.EncodeAll(src, nil) - respBody, err = encodeMsg(srcCompressed, &mKey, h.privateKey) - if err != nil { - return nil, err - } + respBody = h.privateKey.SealTo(machineKey, srcCompressed) } else { - respBody, err = encode(resp, &mKey, h.privateKey) + respBody, err = encode(resp, &machineKey, h.privateKey) if err != nil { return nil, err } } // declare the incoming size on the first 4 bytes - data := make([]byte, 4) + data := make([]byte, reservedResponseHeaderSize) binary.LittleEndian.PutUint32(data, uint32(len(respBody))) data = append(data, respBody...) + return data, nil } -func (h *Headscale) getMapKeepAliveResponse(mKey wgkey.Key, req tailcfg.MapRequest, m *Machine) ([]byte, error) { - resp := tailcfg.MapResponse{ +func (h *Headscale) getMapKeepAliveResponse( + machineKey key.MachinePublic, + mapRequest tailcfg.MapRequest, +) ([]byte, error) { + mapResponse := tailcfg.MapResponse{ KeepAlive: true, } var respBody []byte var err error - if req.Compress == "zstd" { - src, _ := json.Marshal(resp) + if mapRequest.Compress == "zstd" { + src, _ := json.Marshal(mapResponse) encoder, _ := zstd.NewWriter(nil) srcCompressed := encoder.EncodeAll(src, nil) - respBody, err = encodeMsg(srcCompressed, &mKey, h.privateKey) - if err != nil { - return nil, err - } + respBody = h.privateKey.SealTo(machineKey, srcCompressed) } else { - respBody, err = encode(resp, &mKey, h.privateKey) + respBody, err = encode(mapResponse, &machineKey, h.privateKey) if err != nil { return nil, err } } - data := make([]byte, 4) + data := make([]byte, reservedResponseHeaderSize) binary.LittleEndian.PutUint32(data, uint32(len(respBody))) data = append(data, respBody...) + return data, nil } +func (h *Headscale) handleMachineLogOut( + ctx *gin.Context, + machineKey key.MachinePublic, + machine Machine, +) { + resp := tailcfg.RegisterResponse{} + + log.Info(). + Str("machine", machine.Name). + Msg("Client requested logout") + + h.ExpireMachine(&machine) + + resp.AuthURL = "" + resp.MachineAuthorized = false + resp.User = *machine.Namespace.toUser() + respBody, err := encode(resp, &machineKey, h.privateKey) + if err != nil { + log.Error(). + Caller(). + Err(err). + Msg("Cannot encode message") + ctx.String(http.StatusInternalServerError, "") + + return + } + ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody) +} + +func (h *Headscale) handleMachineValidRegistration( + ctx *gin.Context, + machineKey key.MachinePublic, + machine Machine, +) { + resp := tailcfg.RegisterResponse{} + + // The machine registration is valid, respond with redirect to /map + log.Debug(). + Str("machine", machine.Name). + Msg("Client is registered and we have the current NodeKey. All clear to /map") + + resp.AuthURL = "" + resp.MachineAuthorized = true + resp.User = *machine.Namespace.toUser() + resp.Login = *machine.Namespace.toLogin() + + respBody, err := encode(resp, &machineKey, h.privateKey) + if err != nil { + log.Error(). + Caller(). + Err(err). + Msg("Cannot encode message") + machineRegistrations.WithLabelValues("update", "web", "error", machine.Namespace.Name). + Inc() + ctx.String(http.StatusInternalServerError, "") + + return + } + machineRegistrations.WithLabelValues("update", "web", "success", machine.Namespace.Name). + Inc() + ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody) +} + +func (h *Headscale) handleMachineExpired( + ctx *gin.Context, + machineKey key.MachinePublic, + registerRequest tailcfg.RegisterRequest, + machine Machine, +) { + resp := tailcfg.RegisterResponse{} + + // The client has registered before, but has expired + log.Debug(). + Str("machine", machine.Name). + Msg("Machine registration has expired. Sending a authurl to register") + + if registerRequest.Auth.AuthKey != "" { + h.handleAuthKey(ctx, machineKey, registerRequest, machine) + + return + } + + if h.cfg.OIDC.Issuer != "" { + resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s", + strings.TrimSuffix(h.cfg.ServerURL, "/"), machineKey.String()) + } else { + resp.AuthURL = fmt.Sprintf("%s/register?key=%s", + strings.TrimSuffix(h.cfg.ServerURL, "/"), machineKey.String()) + } + + respBody, err := encode(resp, &machineKey, h.privateKey) + if err != nil { + log.Error(). + Caller(). + Err(err). + Msg("Cannot encode message") + machineRegistrations.WithLabelValues("reauth", "web", "error", machine.Namespace.Name). + Inc() + ctx.String(http.StatusInternalServerError, "") + + return + } + machineRegistrations.WithLabelValues("reauth", "web", "success", machine.Namespace.Name). + Inc() + ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody) +} + +func (h *Headscale) handleMachineRefreshKey( + ctx *gin.Context, + machineKey key.MachinePublic, + registerRequest tailcfg.RegisterRequest, + machine Machine, +) { + resp := tailcfg.RegisterResponse{} + + log.Debug(). + Str("machine", machine.Name). + Msg("We have the OldNodeKey in the database. This is a key refresh") + machine.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey) + h.db.Save(&machine) + + resp.AuthURL = "" + resp.User = *machine.Namespace.toUser() + respBody, err := encode(resp, &machineKey, h.privateKey) + if err != nil { + log.Error(). + Caller(). + Err(err). + Msg("Cannot encode message") + ctx.String(http.StatusInternalServerError, "Extremely sad!") + + return + } + ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody) +} + +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). + Msg("The node is sending us a new NodeKey, sending auth url") + if h.cfg.OIDC.Issuer != "" { + resp.AuthURL = fmt.Sprintf( + "%s/oidc/register/%s", + strings.TrimSuffix(h.cfg.ServerURL, "/"), + machineKey.String(), + ) + } else { + resp.AuthURL = fmt.Sprintf("%s/register?key=%s", + 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(). + Caller(). + Err(err). + Msg("Cannot encode message") + ctx.String(http.StatusInternalServerError, "") + + return + } + ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody) +} + func (h *Headscale) handleAuthKey( - c *gin.Context, - db *gorm.DB, - idKey wgkey.Key, - req tailcfg.RegisterRequest, - m Machine, + ctx *gin.Context, + machineKey key.MachinePublic, + registerRequest tailcfg.RegisterRequest, + machine Machine, ) { log.Debug(). Str("func", "handleAuthKey"). - Str("machine", req.Hostinfo.Hostname). - Msgf("Processing auth key for %s", req.Hostinfo.Hostname) + Str("machine", registerRequest.Hostinfo.Hostname). + Msgf("Processing auth key for %s", registerRequest.Hostinfo.Hostname) resp := tailcfg.RegisterResponse{} - pak, err := h.checkKeyValidity(req.Auth.AuthKey) + pak, err := h.checkKeyValidity(registerRequest.Auth.AuthKey) if err != nil { log.Error(). + Caller(). Str("func", "handleAuthKey"). - Str("machine", m.Name). + Str("machine", machine.Name). Err(err). Msg("Failed authentication via AuthKey") resp.MachineAuthorized = false - respBody, err := encode(resp, &idKey, h.privateKey) + respBody, err := encode(resp, &machineKey, h.privateKey) if err != nil { log.Error(). + Caller(). Str("func", "handleAuthKey"). - Str("machine", m.Name). + Str("machine", machine.Name). Err(err). Msg("Cannot encode message") - c.String(http.StatusInternalServerError, "") - machineRegistrations.WithLabelValues("new", "authkey", "error", m.Namespace.Name).Inc() + ctx.String(http.StatusInternalServerError, "") + machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name). + Inc() + return } - c.Data(401, "application/json; charset=utf-8", respBody) + ctx.Data(http.StatusUnauthorized, "application/json; charset=utf-8", respBody) log.Error(). + Caller(). Str("func", "handleAuthKey"). - Str("machine", m.Name). + Str("machine", machine.Name). Msg("Failed authentication via AuthKey") - machineRegistrations.WithLabelValues("new", "authkey", "error", m.Namespace.Name).Inc() + machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name). + Inc() + return } - log.Debug(). - Str("func", "handleAuthKey"). - Str("machine", m.Name). - Msg("Authentication key was valid, proceeding to acquire an IP address") - ip, err := h.getAvailableIP() - if err != nil { - log.Error(). + if machine.isRegistered() { + log.Trace(). + Caller(). + Str("machine", machine.Name). + Msg("machine already registered, reauthenticating") + + h.RefreshMachine(&machine, registerRequest.Expiry) + } else { + log.Debug(). Str("func", "handleAuthKey"). - Str("machine", m.Name). - Msg("Failed to find an available IP") - machineRegistrations.WithLabelValues("new", "authkey", "error", m.Namespace.Name).Inc() - return - } - log.Info(). - Str("func", "handleAuthKey"). - Str("machine", m.Name). - Str("ip", ip.String()). - Msgf("Assigning %s to %s", ip, m.Name) + Str("machine", machine.Name). + Msg("Authentication key was valid, proceeding to acquire an IP address") + ip, err := h.getAvailableIP() + if err != nil { + log.Error(). + Caller(). + Str("func", "handleAuthKey"). + Str("machine", machine.Name). + Msg("Failed to find an available IP") + machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name). + Inc() - m.AuthKeyID = uint(pak.ID) - m.IPAddress = ip.String() - m.NamespaceID = pak.NamespaceID - m.NodeKey = wgkey.Key(req.NodeKey).HexString() // we update it just in case - m.Registered = true - m.RegisterMethod = "authKey" - db.Save(&m) + return + } + log.Info(). + Str("func", "handleAuthKey"). + Str("machine", machine.Name). + Str("ip", ip.String()). + Msgf("Assigning %s to %s", ip, machine.Name) + + machine.Expiry = ®isterRequest.Expiry + machine.AuthKeyID = uint(pak.ID) + machine.IPAddress = ip.String() + 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) + } pak.Used = true - db.Save(&pak) + h.db.Save(&pak) resp.MachineAuthorized = true resp.User = *pak.Namespace.toUser() - respBody, err := encode(resp, &idKey, h.privateKey) + respBody, err := encode(resp, &machineKey, h.privateKey) if err != nil { log.Error(). + Caller(). Str("func", "handleAuthKey"). - Str("machine", m.Name). + Str("machine", machine.Name). Err(err). Msg("Cannot encode message") - machineRegistrations.WithLabelValues("new", "authkey", "error", m.Namespace.Name).Inc() - c.String(http.StatusInternalServerError, "Extremely sad!") + machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name). + Inc() + ctx.String(http.StatusInternalServerError, "Extremely sad!") + return } - machineRegistrations.WithLabelValues("new", "authkey", "success", m.Namespace.Name).Inc() - c.Data(200, "application/json; charset=utf-8", respBody) + machineRegistrations.WithLabelValues("new", "authkey", "success", machine.Namespace.Name). + Inc() + ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody) log.Info(). Str("func", "handleAuthKey"). - Str("machine", m.Name). - Str("ip", ip.String()). + Str("machine", machine.Name). + Str("ip", machine.IPAddress). Msg("Successfully authenticated via AuthKey") } diff --git a/app.go b/app.go index 546eb866..5cc1b5c9 100644 --- a/app.go +++ b/app.go @@ -1,36 +1,76 @@ package headscale import ( + "context" + "crypto/tls" "errors" "fmt" + "io" + "io/fs" + "net" "net/http" "net/url" "os" + "os/signal" "sort" "strings" "sync" + "syscall" "time" - "github.com/rs/zerolog/log" - + "github.com/coreos/go-oidc/v3/oidc" "github.com/gin-gonic/gin" + grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + v1 "github.com/juanfont/headscale/gen/go/headscale/v1" + "github.com/patrickmn/go-cache" + zerolog "github.com/philip-bui/grpc-zerolog" + zl "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/soheilhy/cmux" ginprometheus "github.com/zsais/go-gin-prometheus" "golang.org/x/crypto/acme" "golang.org/x/crypto/acme/autocert" + "golang.org/x/oauth2" + "golang.org/x/sync/errgroup" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/peer" + "google.golang.org/grpc/reflection" + "google.golang.org/grpc/status" "gorm.io/gorm" "inet.af/netaddr" "tailscale.com/tailcfg" "tailscale.com/types/dnstype" - "tailscale.com/types/wgkey" + "tailscale.com/types/key" ) -// Config contains the initial Headscale configuration +const ( + AuthPrefix = "Bearer " + Postgres = "postgres" + Sqlite = "sqlite3" + updateInterval = 5000 + HTTPReadTimeout = 30 * time.Second + privateKeyFileMode = 0o600 + + requestedExpiryCacheExpiration = time.Minute * 5 + requestedExpiryCacheCleanupInterval = time.Minute * 10 + + errUnsupportedDatabase = Error("unsupported DB") + errUnsupportedLetsEncryptChallengeType = Error( + "unknown value for Lets Encrypt challenge type", + ) +) + +// Config contains the initial Headscale configuration. type Config struct { ServerURL string Addr string - PrivateKeyPath string EphemeralNodeInactivityTimeout time.Duration IPPrefix netaddr.IPPrefix + PrivateKeyPath string BaseDomain string DERP DERPConfig @@ -55,6 +95,20 @@ type Config struct { ACMEEmail string DNSConfig *tailcfg.DNSConfig + + UnixSocket string + UnixSocketPermission fs.FileMode + + OIDC OIDCConfig + + CLI CLIConfig +} + +type OIDCConfig struct { + Issuer string + ClientID string + ClientSecret string + MatchMap map[string]string } type DERPConfig struct { @@ -64,86 +118,110 @@ type DERPConfig struct { UpdateFrequency time.Duration } -// Headscale represents the base app of the service +type CLIConfig struct { + Address string + APIKey string + Insecure bool + Timeout time.Duration +} + +// Headscale represents the base app of the service. type Headscale struct { cfg Config db *gorm.DB dbString string dbType string dbDebug bool - publicKey *wgkey.Key - privateKey *wgkey.Private + privateKey *key.MachinePrivate DERPMap *tailcfg.DERPMap aclPolicy *ACLPolicy - aclRules *[]tailcfg.FilterRule + aclRules []tailcfg.FilterRule lastStateChange sync.Map + + oidcProvider *oidc.Provider + oauth2Config *oauth2.Config + oidcStateCache *cache.Cache + + requestedExpiryCache *cache.Cache } -// NewHeadscale returns the Headscale app +// NewHeadscale returns the Headscale app. func NewHeadscale(cfg Config) (*Headscale, error) { - content, err := os.ReadFile(cfg.PrivateKeyPath) + privKey, err := readOrCreatePrivateKey(cfg.PrivateKeyPath) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to read or create private key: %w", err) } - privKey, err := wgkey.ParsePrivate(string(content)) - if err != nil { - return nil, err - } - pubKey := privKey.Public() var dbString string switch cfg.DBtype { - case "postgres": - dbString = fmt.Sprintf("host=%s port=%d dbname=%s user=%s password=%s sslmode=disable", cfg.DBhost, - cfg.DBport, cfg.DBname, cfg.DBuser, cfg.DBpass) - case "sqlite3": + case Postgres: + dbString = fmt.Sprintf( + "host=%s port=%d dbname=%s user=%s password=%s sslmode=disable", + cfg.DBhost, + cfg.DBport, + cfg.DBname, + cfg.DBuser, + cfg.DBpass, + ) + case Sqlite: dbString = cfg.DBpath default: - return nil, errors.New("unsupported DB") + return nil, errUnsupportedDatabase } - h := Headscale{ - cfg: cfg, - dbType: cfg.DBtype, - dbString: dbString, - privateKey: privKey, - publicKey: &pubKey, - aclRules: &tailcfg.FilterAllowAll, // default allowall + requestedExpiryCache := cache.New( + requestedExpiryCacheExpiration, + requestedExpiryCacheCleanupInterval, + ) + + app := Headscale{ + cfg: cfg, + dbType: cfg.DBtype, + dbString: dbString, + privateKey: privKey, + aclRules: tailcfg.FilterAllowAll, // default allowall + requestedExpiryCache: requestedExpiryCache, } - err = h.initDB() + err = app.initDB() if err != nil { return nil, err } - if h.cfg.DNSConfig != nil && h.cfg.DNSConfig.Proxied { // if MagicDNS - magicDNSDomains, err := generateMagicDNSRootDomains(h.cfg.IPPrefix, h.cfg.BaseDomain) + if cfg.OIDC.Issuer != "" { + err = app.initOIDC() if err != nil { return nil, err } + } + + if app.cfg.DNSConfig != nil && app.cfg.DNSConfig.Proxied { // if MagicDNS + magicDNSDomains := generateMagicDNSRootDomains( + app.cfg.IPPrefix, + ) // we might have routes already from Split DNS - if h.cfg.DNSConfig.Routes == nil { - h.cfg.DNSConfig.Routes = make(map[string][]dnstype.Resolver) + if app.cfg.DNSConfig.Routes == nil { + app.cfg.DNSConfig.Routes = make(map[string][]dnstype.Resolver) } for _, d := range magicDNSDomains { - h.cfg.DNSConfig.Routes[d.WithoutTrailingDot()] = nil + app.cfg.DNSConfig.Routes[d.WithoutTrailingDot()] = nil } } - return &h, nil + return &app, nil } -// Redirect to our TLS url +// Redirect to our TLS url. func (h *Headscale) redirect(w http.ResponseWriter, req *http.Request) { target := h.cfg.ServerURL + req.URL.RequestURI() http.Redirect(w, req, target, http.StatusFound) } // expireEphemeralNodes deletes ephemeral machine records that have not been -// seen for longer than h.cfg.EphemeralNodeInactivityTimeout +// seen for longer than h.cfg.EphemeralNodeInactivityTimeout. func (h *Headscale) expireEphemeralNodes(milliSeconds int64) { ticker := time.NewTicker(time.Duration(milliSeconds) * time.Millisecond) for range ticker.C { @@ -155,33 +233,46 @@ func (h *Headscale) expireEphemeralNodesWorker() { namespaces, err := h.ListNamespaces() if err != nil { log.Error().Err(err).Msg("Error listing namespaces") + return } - for _, ns := range *namespaces { - machines, err := h.ListMachinesInNamespace(ns.Name) + + for _, namespace := range namespaces { + machines, err := h.ListMachinesInNamespace(namespace.Name) if err != nil { - log.Error().Err(err).Str("namespace", ns.Name).Msg("Error listing machines in namespace") + log.Error(). + Err(err). + Str("namespace", namespace.Name). + Msg("Error listing machines in namespace") + return } - for _, m := range *machines { - if m.AuthKey != nil && m.LastSeen != nil && m.AuthKey.Ephemeral && - time.Now().After(m.LastSeen.Add(h.cfg.EphemeralNodeInactivityTimeout)) { - log.Info().Str("machine", m.Name).Msg("Ephemeral client removed from database") - err = h.db.Unscoped().Delete(m).Error + + for _, machine := range machines { + if machine.AuthKey != nil && machine.LastSeen != nil && + machine.AuthKey.Ephemeral && + time.Now(). + After(machine.LastSeen.Add(h.cfg.EphemeralNodeInactivityTimeout)) { + log.Info(). + Str("machine", machine.Name). + Msg("Ephemeral client removed from database") + + err = h.db.Unscoped().Delete(machine).Error if err != nil { log.Error(). Err(err). - Str("machine", m.Name). + Str("machine", machine.Name). Msg("🤮 Cannot delete ephemeral machine from the database") } } } - h.setLastStateChangeToNow(ns.Name) + + h.setLastStateChangeToNow(namespace.Name) } } // WatchForKVUpdates checks the KV DB table for requests to perform tailnet upgrades -// This is a way to communitate the CLI with the headscale server +// This is a way to communitate the CLI with the headscale server. func (h *Headscale) watchForKVUpdates(milliSeconds int64) { ticker := time.NewTicker(time.Duration(milliSeconds) * time.Millisecond) for range ticker.C { @@ -194,24 +285,234 @@ func (h *Headscale) watchForKVUpdatesWorker() { // more functions will come here in the future } -// Serve launches a GIN server with the Headscale API +func (h *Headscale) grpcAuthenticationInterceptor(ctx context.Context, + req interface{}, + info *grpc.UnaryServerInfo, + handler grpc.UnaryHandler) (interface{}, error) { + // Check if the request is coming from the on-server client. + // This is not secure, but it is to maintain maintainability + // with the "legacy" database-based client + // It is also neede for grpc-gateway to be able to connect to + // the server + client, _ := peer.FromContext(ctx) + + log.Trace(). + Caller(). + Str("client_address", client.Addr.String()). + Msg("Client is trying to authenticate") + + meta, ok := metadata.FromIncomingContext(ctx) + if !ok { + log.Error(). + Caller(). + Str("client_address", client.Addr.String()). + Msg("Retrieving metadata is failed") + + return ctx, status.Errorf( + codes.InvalidArgument, + "Retrieving metadata is failed", + ) + } + + authHeader, ok := meta["authorization"] + if !ok { + log.Error(). + Caller(). + Str("client_address", client.Addr.String()). + Msg("Authorization token is not supplied") + + return ctx, status.Errorf( + codes.Unauthenticated, + "Authorization token is not supplied", + ) + } + + token := authHeader[0] + + if !strings.HasPrefix(token, AuthPrefix) { + log.Error(). + Caller(). + Str("client_address", client.Addr.String()). + Msg(`missing "Bearer " prefix in "Authorization" header`) + + return ctx, status.Error( + codes.Unauthenticated, + `missing "Bearer " prefix in "Authorization" header`, + ) + } + + // TODO(kradalby): Implement API key backend: + // - Table in the DB + // - Key name + // - Encrypted + // - Expiry + // + // Currently all other than localhost traffic is unauthorized, this is intentional to allow + // us to make use of gRPC for our CLI, but not having to implement any of the remote capabilities + // and API key auth + return ctx, status.Error( + codes.Unauthenticated, + "Authentication is not implemented yet", + ) + + // if strings.TrimPrefix(token, AUTH_PREFIX) != a.Token { + // log.Error().Caller().Str("client_address", p.Addr.String()).Msg("invalid token") + // return ctx, status.Error(codes.Unauthenticated, "invalid token") + // } + + // return handler(ctx, req) +} + +func (h *Headscale) httpAuthenticationMiddleware(ctx *gin.Context) { + log.Trace(). + Caller(). + Str("client_address", ctx.ClientIP()). + Msg("HTTP authentication invoked") + + authHeader := ctx.GetHeader("authorization") + + if !strings.HasPrefix(authHeader, AuthPrefix) { + log.Error(). + Caller(). + Str("client_address", ctx.ClientIP()). + Msg(`missing "Bearer " prefix in "Authorization" header`) + ctx.AbortWithStatus(http.StatusUnauthorized) + + return + } + + ctx.AbortWithStatus(http.StatusUnauthorized) + + // TODO(kradalby): Implement API key backend + // Currently all traffic is unauthorized, this is intentional to allow + // us to make use of gRPC for our CLI, but not having to implement any of the remote capabilities + // and API key auth + // + // if strings.TrimPrefix(authHeader, AUTH_PREFIX) != a.Token { + // log.Error().Caller().Str("client_address", c.ClientIP()).Msg("invalid token") + // c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error", "unauthorized"}) + + // return + // } + + // c.Next() +} + +// ensureUnixSocketIsAbsent will check if the given path for headscales unix socket is clear +// and will remove it if it is not. +func (h *Headscale) ensureUnixSocketIsAbsent() error { + // File does not exist, all fine + if _, err := os.Stat(h.cfg.UnixSocket); errors.Is(err, os.ErrNotExist) { + return nil + } + + return os.Remove(h.cfg.UnixSocket) +} + +// Serve launches a GIN server with the Headscale API. func (h *Headscale) Serve() error { - r := gin.Default() - - p := ginprometheus.NewPrometheus("gin") - p.Use(r) - - r.GET("/health", func(c *gin.Context) { c.JSON(200, gin.H{"healthy": "ok"}) }) - r.GET("/key", h.KeyHandler) - r.GET("/register", h.RegisterWebAPI) - r.POST("/machine/:id/map", h.PollNetMapHandler) - r.POST("/machine/:id", h.RegistrationHandler) - r.GET("/apple", h.AppleMobileConfig) - r.GET("/apple/:platform", h.ApplePlatformConfig) var err error - go h.watchForKVUpdates(5000) - go h.expireEphemeralNodes(5000) + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + + defer cancel() + + err = h.ensureUnixSocketIsAbsent() + if err != nil { + return fmt.Errorf("unable to remove old socket file: %w", err) + } + + socketListener, err := net.Listen("unix", h.cfg.UnixSocket) + if err != nil { + return fmt.Errorf("failed to set up gRPC socket: %w", err) + } + + // Change socket permissions + if err := os.Chmod(h.cfg.UnixSocket, h.cfg.UnixSocketPermission); err != nil { + return fmt.Errorf("failed change permission of gRPC socket: %w", err) + } + + // Handle common process-killing signals so we can gracefully shut down: + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) + go func(c chan os.Signal) { + // Wait for a SIGINT or SIGKILL: + sig := <-c + log.Printf("Caught signal %s: shutting down.", sig) + // Stop listening (and unlink the socket if unix type): + socketListener.Close() + // And we're done: + os.Exit(0) + }(sigc) + + networkListener, err := net.Listen("tcp", h.cfg.Addr) + if err != nil { + return fmt.Errorf("failed to bind to TCP address: %w", err) + } + + // Create the cmux object that will multiplex 2 protocols on the same port. + // The two following listeners will be served on the same port below gracefully. + networkMutex := cmux.New(networkListener) + // Match gRPC requests here + grpcListener := networkMutex.MatchWithWriters( + cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc"), + cmux.HTTP2MatchHeaderFieldSendSettings( + "content-type", + "application/grpc+proto", + ), + ) + // Otherwise match regular http requests. + httpListener := networkMutex.Match(cmux.Any()) + + grpcGatewayMux := runtime.NewServeMux() + + // Make the grpc-gateway connect to grpc over socket + grpcGatewayConn, err := grpc.Dial( + h.cfg.UnixSocket, + []grpc.DialOption{ + grpc.WithInsecure(), + grpc.WithContextDialer(GrpcSocketDialer), + }..., + ) + if err != nil { + return err + } + + // Connect to the gRPC server over localhost to skip + // the authentication. + err = v1.RegisterHeadscaleServiceHandler(ctx, grpcGatewayMux, grpcGatewayConn) + if err != nil { + return err + } + + router := gin.Default() + + prometheus := ginprometheus.NewPrometheus("gin") + prometheus.Use(router) + + router.GET( + "/health", + func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"healthy": "ok"}) }, + ) + router.GET("/key", h.KeyHandler) + router.GET("/register", h.RegisterWebAPI) + router.POST("/machine/:id/map", h.PollNetMapHandler) + 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/:platform", h.ApplePlatformConfig) + router.GET("/swagger", SwaggerUI) + router.GET("/swagger/v1/openapiv2.json", SwaggerAPIv1) + + api := router.Group("/api") + api.Use(h.httpAuthenticationMiddleware) + { + api.Any("/v1/*any", gin.WrapF(grpcGatewayMux.ServeHTTP)) + } + + router.NoRoute(stdoutHandler) // Fetch an initial DERP Map before we start serving h.DERPMap = GetDERPMap(h.cfg.DERP) @@ -222,10 +523,14 @@ func (h *Headscale) Serve() error { go h.scheduledDERPMapUpdateWorker(derpMapCancelChannel) } - s := &http.Server{ + // I HATE THIS + go h.watchForKVUpdates(updateInterval) + go h.expireEphemeralNodes(updateInterval) + + httpServer := &http.Server{ Addr: h.cfg.Addr, - Handler: r, - ReadTimeout: 30 * time.Second, + Handler: router, + ReadTimeout: HTTPReadTimeout, // Go does not handle timeouts in HTTP very well, and there is // no good way to handle streaming timeouts, therefore we need to // keep this at unlimited and be careful to clean up connections @@ -233,12 +538,78 @@ func (h *Headscale) Serve() error { WriteTimeout: 0, } + if zl.GlobalLevel() == zl.TraceLevel { + zerolog.RespLog = true + } else { + zerolog.RespLog = false + } + + grpcOptions := []grpc.ServerOption{ + grpc.UnaryInterceptor( + grpc_middleware.ChainUnaryServer( + h.grpcAuthenticationInterceptor, + zerolog.NewUnaryServerInterceptor(), + ), + ), + } + + tlsConfig, err := h.getTLSSettings() + if err != nil { + log.Error().Err(err).Msg("Failed to set up TLS configuration") + + return err + } + + if tlsConfig != nil { + httpServer.TLSConfig = tlsConfig + + grpcOptions = append(grpcOptions, grpc.Creds(credentials.NewTLS(tlsConfig))) + } + + grpcServer := grpc.NewServer(grpcOptions...) + + // Start the local gRPC server without TLS and without authentication + grpcSocket := grpc.NewServer(zerolog.UnaryInterceptor()) + + v1.RegisterHeadscaleServiceServer(grpcServer, newHeadscaleV1APIServer(h)) + v1.RegisterHeadscaleServiceServer(grpcSocket, newHeadscaleV1APIServer(h)) + reflection.Register(grpcServer) + reflection.Register(grpcSocket) + + errorGroup := new(errgroup.Group) + + errorGroup.Go(func() error { return grpcSocket.Serve(socketListener) }) + + // TODO(kradalby): Verify if we need the same TLS setup for gRPC as HTTP + errorGroup.Go(func() error { return grpcServer.Serve(grpcListener) }) + + if tlsConfig != nil { + errorGroup.Go(func() error { + tlsl := tls.NewListener(httpListener, tlsConfig) + + return httpServer.Serve(tlsl) + }) + } else { + errorGroup.Go(func() error { return httpServer.Serve(httpListener) }) + } + + errorGroup.Go(func() error { return networkMutex.Serve() }) + + log.Info(). + Msgf("listening and serving (multiplexed HTTP and gRPC) on: %s", h.cfg.Addr) + + return errorGroup.Wait() +} + +func (h *Headscale) getTLSSettings() (*tls.Config, error) { + var err error if h.cfg.TLSLetsEncryptHostname != "" { if !strings.HasPrefix(h.cfg.ServerURL, "https://") { - log.Warn().Msg("Listening with TLS but ServerURL does not start with https://") + log.Warn(). + Msg("Listening with TLS but ServerURL does not start with https://") } - m := autocert.Manager{ + certManager := autocert.Manager{ Prompt: autocert.AcceptTOS, HostPolicy: autocert.HostWhitelist(h.cfg.TLSLetsEncryptHostname), Cache: autocert.DirCache(h.cfg.TLSLetsEncryptCacheDir), @@ -248,38 +619,48 @@ func (h *Headscale) Serve() error { Email: h.cfg.ACMEEmail, } - s.TLSConfig = m.TLSConfig() - - if h.cfg.TLSLetsEncryptChallengeType == "TLS-ALPN-01" { + switch h.cfg.TLSLetsEncryptChallengeType { + case "TLS-ALPN-01": // Configuration via autocert with TLS-ALPN-01 (https://tools.ietf.org/html/rfc8737) // The RFC requires that the validation is done on port 443; in other words, headscale // must be reachable on port 443. - err = s.ListenAndServeTLS("", "") - } else if h.cfg.TLSLetsEncryptChallengeType == "HTTP-01" { + return certManager.TLSConfig(), nil + + case "HTTP-01": // Configuration via autocert with HTTP-01. This requires listening on // port 80 for the certificate validation in addition to the headscale // service, which can be configured to run on any other port. go func() { log.Fatal(). - Err(http.ListenAndServe(h.cfg.TLSLetsEncryptListen, m.HTTPHandler(http.HandlerFunc(h.redirect)))). + Err(http.ListenAndServe(h.cfg.TLSLetsEncryptListen, certManager.HTTPHandler(http.HandlerFunc(h.redirect)))). Msg("failed to set up a HTTP server") }() - err = s.ListenAndServeTLS("", "") - } else { - return errors.New("unknown value for TLSLetsEncryptChallengeType") + + return certManager.TLSConfig(), nil + + default: + return nil, errUnsupportedLetsEncryptChallengeType } } else if h.cfg.TLSCertPath == "" { if !strings.HasPrefix(h.cfg.ServerURL, "http://") { log.Warn().Msg("Listening without TLS but ServerURL does not start with http://") } - err = s.ListenAndServe() + + return nil, err } else { if !strings.HasPrefix(h.cfg.ServerURL, "https://") { log.Warn().Msg("Listening with TLS but ServerURL does not start with https://") } - err = s.ListenAndServeTLS(h.cfg.TLSCertPath, h.cfg.TLSKeyPath) + tlsConfig := &tls.Config{ + ClientAuth: tls.RequireAnyClientCert, + 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 } - return err } func (h *Headscale) setLastStateChangeToNow(namespace string) { @@ -311,3 +692,58 @@ func (h *Headscale) getLastStateChange(namespaces ...string) time.Time { return times[0] } } + +func stdoutHandler(ctx *gin.Context) { + body, _ := io.ReadAll(ctx.Request.Body) + + log.Trace(). + Interface("header", ctx.Request.Header). + Interface("proto", ctx.Request.Proto). + Interface("url", ctx.Request.URL). + Bytes("body", body). + Msg("Request did not match") +} + +func readOrCreatePrivateKey(path string) (*key.MachinePrivate, error) { + privateKey, err := os.ReadFile(path) + if errors.Is(err, os.ErrNotExist) { + log.Info().Str("path", path).Msg("No private key file at path, creating...") + + machineKey := key.NewMachine() + + machineKeyStr, err := machineKey.MarshalText() + if err != nil { + return nil, fmt.Errorf( + "failed to convert private key to string for saving: %w", + err, + ) + } + err = os.WriteFile(path, machineKeyStr, privateKeyFileMode) + if err != nil { + return nil, fmt.Errorf( + "failed to save private key to disk: %w", + err, + ) + } + + return &machineKey, nil + } else if err != nil { + return nil, fmt.Errorf("failed to read private key file: %w", err) + } + + trimmedPrivateKey := strings.TrimSpace(string(privateKey)) + privateKeyEnsurePrefix := PrivateKeyEnsurePrefix(trimmedPrivateKey) + + var machineKey key.MachinePrivate + if err = machineKey.UnmarshalText([]byte(privateKeyEnsurePrefix)); err != nil { + log.Info(). + Str("path", path). + Msg("This might be due to a legacy (headscale pre-0.12) private key. " + + "If the key is in WireGuard format, delete the key and restart headscale. " + + "A new key will automatically be generated. All Tailscale clients will have to be restarted") + + return nil, fmt.Errorf("failed to parse private key: %w", err) + } + + return &machineKey, nil +} diff --git a/app_test.go b/app_test.go index 5e53f1cc..bff13933 100644 --- a/app_test.go +++ b/app_test.go @@ -5,6 +5,7 @@ import ( "os" "testing" + "github.com/patrickmn/go-cache" "gopkg.in/check.v1" "inet.af/netaddr" ) @@ -17,8 +18,10 @@ var _ = check.Suite(&Suite{}) type Suite struct{} -var tmpDir string -var h Headscale +var ( + tmpDir string + app Headscale +) func (s *Suite) SetUpTest(c *check.C) { s.ResetDB(c) @@ -41,18 +44,22 @@ func (s *Suite) ResetDB(c *check.C) { IPPrefix: netaddr.MustParseIPPrefix("10.27.0.0/23"), } - h = Headscale{ + app = Headscale{ cfg: cfg, dbType: "sqlite3", dbString: tmpDir + "/headscale_test.db", + requestedExpiryCache: cache.New( + requestedExpiryCacheExpiration, + requestedExpiryCacheCleanupInterval, + ), } - err = h.initDB() + err = app.initDB() if err != nil { c.Fatal(err) } - db, err := h.openDB() + db, err := app.openDB() if err != nil { c.Fatal(err) } - h.db = db + app.db = db } diff --git a/apple_mobileconfig.go b/apple_mobileconfig.go index ba9f9575..c96918dc 100644 --- a/apple_mobileconfig.go +++ b/apple_mobileconfig.go @@ -2,19 +2,18 @@ package headscale import ( "bytes" + "html/template" "net/http" - "text/template" - - "github.com/rs/zerolog/log" "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(c *gin.Context) { - t := template.Must(template.New("apple").Parse(` +// Listens in /register. +func (h *Headscale) AppleMobileConfig(ctx *gin.Context) { + appleTemplate := template.Must(template.New("apple").Parse(`

Apple configuration profiles

@@ -56,7 +55,7 @@ func (h *Headscale) AppleMobileConfig(c *gin.Context) {

Or

Use your terminal to configure the default setting for Tailscale by issuing:

- defaults write io.tailscale.ipn.macos ControlURL {{.Url}} + defaults write io.tailscale.ipn.macos ControlURL {{.URL}}

Restart Tailscale.app and log in.

@@ -64,24 +63,29 @@ func (h *Headscale) AppleMobileConfig(c *gin.Context) { `)) config := map[string]interface{}{ - "Url": h.cfg.ServerURL, + "URL": h.cfg.ServerURL, } var payload bytes.Buffer - if err := t.Execute(&payload, config); err != nil { + if err := appleTemplate.Execute(&payload, config); err != nil { log.Error(). Str("handler", "AppleMobileConfig"). Err(err). Msg("Could not render Apple index template") - c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Could not render Apple index template")) + ctx.Data( + http.StatusInternalServerError, + "text/html; charset=utf-8", + []byte("Could not render Apple index template"), + ) + return } - c.Data(http.StatusOK, "text/html; charset=utf-8", payload.Bytes()) + ctx.Data(http.StatusOK, "text/html; charset=utf-8", payload.Bytes()) } -func (h *Headscale) ApplePlatformConfig(c *gin.Context) { - platform := c.Param("platform") +func (h *Headscale) ApplePlatformConfig(ctx *gin.Context) { + platform := ctx.Param("platform") id, err := uuid.NewV4() if err != nil { @@ -89,23 +93,33 @@ func (h *Headscale) ApplePlatformConfig(c *gin.Context) { Str("handler", "ApplePlatformConfig"). Err(err). Msg("Failed not create UUID") - c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Failed to create UUID")) + ctx.Data( + http.StatusInternalServerError, + "text/html; charset=utf-8", + []byte("Failed to create UUID"), + ) + return } - contentId, err := uuid.NewV4() + contentID, err := uuid.NewV4() if err != nil { log.Error(). Str("handler", "ApplePlatformConfig"). Err(err). Msg("Failed not create UUID") - c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Failed to create UUID")) + ctx.Data( + http.StatusInternalServerError, + "text/html; charset=utf-8", + []byte("Failed to create UUID"), + ) + return } platformConfig := AppleMobilePlatformConfig{ - UUID: contentId, - Url: h.cfg.ServerURL, + UUID: contentID, + URL: h.cfg.ServerURL, } var payload bytes.Buffer @@ -117,7 +131,12 @@ func (h *Headscale) ApplePlatformConfig(c *gin.Context) { Str("handler", "ApplePlatformConfig"). Err(err). Msg("Could not render Apple macOS template") - c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Could not render Apple macOS template")) + ctx.Data( + http.StatusInternalServerError, + "text/html; charset=utf-8", + []byte("Could not render Apple macOS template"), + ) + return } case "ios": @@ -126,17 +145,27 @@ func (h *Headscale) ApplePlatformConfig(c *gin.Context) { Str("handler", "ApplePlatformConfig"). Err(err). Msg("Could not render Apple iOS template") - c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Could not render Apple iOS template")) + ctx.Data( + http.StatusInternalServerError, + "text/html; charset=utf-8", + []byte("Could not render Apple iOS template"), + ) + return } default: - c.Data(http.StatusOK, "text/html; charset=utf-8", []byte("Invalid platform, only ios and macos is supported")) + ctx.Data( + http.StatusOK, + "text/html; charset=utf-8", + []byte("Invalid platform, only ios and macos is supported"), + ) + return } config := AppleMobileConfig{ UUID: id, - Url: h.cfg.ServerURL, + URL: h.cfg.ServerURL, Payload: payload.String(), } @@ -146,25 +175,35 @@ func (h *Headscale) ApplePlatformConfig(c *gin.Context) { Str("handler", "ApplePlatformConfig"). Err(err). Msg("Could not render Apple platform template") - c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Could not render Apple platform template")) + ctx.Data( + http.StatusInternalServerError, + "text/html; charset=utf-8", + []byte("Could not render Apple platform template"), + ) + return } - c.Data(http.StatusOK, "application/x-apple-aspen-config; charset=utf-8", content.Bytes()) + ctx.Data( + http.StatusOK, + "application/x-apple-aspen-config; charset=utf-8", + content.Bytes(), + ) } type AppleMobileConfig struct { UUID uuid.UUID - Url string + URL string Payload string } type AppleMobilePlatformConfig struct { UUID uuid.UUID - Url string + URL string } -var commonTemplate = template.Must(template.New("mobileconfig").Parse(` +var commonTemplate = template.Must( + template.New("mobileconfig").Parse(` @@ -173,7 +212,7 @@ var commonTemplate = template.Must(template.New("mobileconfig").Parse(`PayloadDisplayName Headscale PayloadDescription - Configure Tailscale login server to: {{.Url}} + Configure Tailscale login server to: {{.URL}} PayloadIdentifier com.github.juanfont.headscale PayloadRemovalDisallowed @@ -187,7 +226,8 @@ var commonTemplate = template.Must(template.New("mobileconfig").Parse(` -`)) +`), +) var iosTemplate = template.Must(template.New("iosTemplate").Parse(` @@ -203,7 +243,7 @@ var iosTemplate = template.Must(template.New("iosTemplate").Parse(` ControlURL - {{.Url}} + {{.URL}} `)) @@ -221,6 +261,6 @@ var macosTemplate = template.Must(template.New("macosTemplate").Parse(` ControlURL - {{.Url}} + {{.URL}} `)) diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 00000000..d7b832ab --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,21 @@ +version: v1 +plugins: + - name: go + out: gen/go + opt: + - paths=source_relative + - name: go-grpc + out: gen/go + opt: + - paths=source_relative + - name: grpc-gateway + out: gen/go + opt: + - paths=source_relative + - generate_unbound_methods=true + # - name: gorm + # out: gen/go + # opt: + # - paths=source_relative,enums=string,gateway=true + - name: openapiv2 + out: gen/openapiv2 diff --git a/cli.go b/cli.go deleted file mode 100644 index 9c5b66e5..00000000 --- a/cli.go +++ /dev/null @@ -1,40 +0,0 @@ -package headscale - -import ( - "errors" - - "gorm.io/gorm" - "tailscale.com/types/wgkey" -) - -// RegisterMachine is executed from the CLI to register a new Machine using its MachineKey -func (h *Headscale) RegisterMachine(key string, namespace string) (*Machine, error) { - ns, err := h.GetNamespace(namespace) - if err != nil { - return nil, err - } - mKey, err := wgkey.ParseHex(key) - if err != nil { - return nil, err - } - - m := Machine{} - if result := h.db.First(&m, "machine_key = ?", mKey.HexString()); errors.Is(result.Error, gorm.ErrRecordNotFound) { - return nil, errors.New("Machine not found") - } - - if m.isAlreadyRegistered() { - return nil, errors.New("Machine already registered") - } - - ip, err := h.getAvailableIP() - if err != nil { - return nil, err - } - m.IPAddress = ip.String() - m.NamespaceID = ns.ID - m.Registered = true - m.RegisterMethod = "cli" - h.db.Save(&m) - return &m, nil -} diff --git a/cli_test.go b/cli_test.go index 528a115e..ef7e2993 100644 --- a/cli_test.go +++ b/cli_test.go @@ -1,31 +1,39 @@ package headscale import ( + "time" + "gopkg.in/check.v1" ) func (s *Suite) TestRegisterMachine(c *check.C) { - n, err := h.CreateNamespace("test") + namespace, err := app.CreateNamespace("test") c.Assert(err, check.IsNil) - m := Machine{ + now := time.Now().UTC() + + machine := Machine{ ID: 0, MachineKey: "8ce002a935f8c394e55e78fbbb410576575ff8ec5cfa2e627e4b807f1be15b0e", NodeKey: "bar", DiscoKey: "faa", Name: "testmachine", - NamespaceID: n.ID, + NamespaceID: namespace.ID, IPAddress: "10.0.0.1", + Expiry: &now, } - h.db.Save(&m) + app.db.Save(&machine) - _, err = h.GetMachine("test", "testmachine") + _, err = app.GetMachine("test", "testmachine") c.Assert(err, check.IsNil) - m2, err := h.RegisterMachine("8ce002a935f8c394e55e78fbbb410576575ff8ec5cfa2e627e4b807f1be15b0e", n.Name) + machineAfterRegistering, err := app.RegisterMachine( + "8ce002a935f8c394e55e78fbbb410576575ff8ec5cfa2e627e4b807f1be15b0e", + namespace.Name, + ) c.Assert(err, check.IsNil) - c.Assert(m2.Registered, check.Equals, true) + c.Assert(machineAfterRegistering.Registered, check.Equals, true) - _, err = m2.GetHostInfo() + _, err = machineAfterRegistering.GetHostInfo() c.Assert(err, check.IsNil) } diff --git a/cmd/headscale/cli/debug.go b/cmd/headscale/cli/debug.go new file mode 100644 index 00000000..8010a9be --- /dev/null +++ b/cmd/headscale/cli/debug.go @@ -0,0 +1,132 @@ +package cli + +import ( + "fmt" + + v1 "github.com/juanfont/headscale/gen/go/headscale/v1" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" + "google.golang.org/grpc/status" +) + +const ( + keyLength = 64 + errPreAuthKeyTooShort = Error("key too short, must be 64 hexadecimal characters") +) + +// Error is used to compare errors as per https://dave.cheney.net/2016/04/07/constant-errors +type Error string + +func (e Error) Error() string { return string(e) } + +func init() { + rootCmd.AddCommand(debugCmd) + + createNodeCmd.Flags().StringP("name", "", "", "Name") + err := createNodeCmd.MarkFlagRequired("name") + if err != nil { + log.Fatal().Err(err).Msg("") + } + createNodeCmd.Flags().StringP("namespace", "n", "", "Namespace") + err = createNodeCmd.MarkFlagRequired("namespace") + if err != nil { + log.Fatal().Err(err).Msg("") + } + createNodeCmd.Flags().StringP("key", "k", "", "Key") + err = createNodeCmd.MarkFlagRequired("key") + if err != nil { + log.Fatal().Err(err).Msg("") + } + createNodeCmd.Flags(). + StringSliceP("route", "r", []string{}, "List (or repeated flags) of routes to advertise") + + debugCmd.AddCommand(createNodeCmd) +} + +var debugCmd = &cobra.Command{ + Use: "debug", + Short: "debug and testing commands", + Long: "debug contains extra commands used for debugging and testing headscale", +} + +var createNodeCmd = &cobra.Command{ + Use: "create-node", + Short: "Create a node (machine) that can be registered with `nodes register <>` command", + Run: func(cmd *cobra.Command, args []string) { + output, _ := cmd.Flags().GetString("output") + + namespace, err := cmd.Flags().GetString("namespace") + if err != nil { + ErrorOutput(err, fmt.Sprintf("Error getting namespace: %s", err), output) + + return + } + + ctx, client, conn, cancel := getHeadscaleCLIClient() + defer cancel() + defer conn.Close() + + name, err := cmd.Flags().GetString("name") + if err != nil { + ErrorOutput( + err, + fmt.Sprintf("Error getting node from flag: %s", err), + output, + ) + + return + } + + machineKey, err := cmd.Flags().GetString("key") + if err != nil { + ErrorOutput( + err, + fmt.Sprintf("Error getting key from flag: %s", err), + output, + ) + + return + } + if len(machineKey) != keyLength { + err = errPreAuthKeyTooShort + ErrorOutput( + err, + fmt.Sprintf("Error: %s", err), + output, + ) + + return + } + + routes, err := cmd.Flags().GetStringSlice("route") + if err != nil { + ErrorOutput( + err, + fmt.Sprintf("Error getting routes from flag: %s", err), + output, + ) + + return + } + + request := &v1.DebugCreateMachineRequest{ + Key: machineKey, + Name: name, + Namespace: namespace, + Routes: routes, + } + + response, err := client.DebugCreateMachine(ctx, request) + if err != nil { + ErrorOutput( + err, + fmt.Sprintf("Cannot create machine: %s", status.Convert(err).Message()), + output, + ) + + return + } + + SuccessOutput(response.Machine, "Machine created", output) + }, +} diff --git a/cmd/headscale/cli/generate.go b/cmd/headscale/cli/generate.go new file mode 100644 index 00000000..24844146 --- /dev/null +++ b/cmd/headscale/cli/generate.go @@ -0,0 +1,41 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + "tailscale.com/types/key" +) + +func init() { + rootCmd.AddCommand(generateCmd) + generateCmd.AddCommand(generatePrivateKeyCmd) +} + +var generateCmd = &cobra.Command{ + Use: "generate", + Short: "Generate commands", +} + +var generatePrivateKeyCmd = &cobra.Command{ + Use: "private-key", + Short: "Generate a private key for the headscale server", + Run: func(cmd *cobra.Command, args []string) { + output, _ := cmd.Flags().GetString("output") + machineKey := key.NewMachine() + + machineKeyStr, err := machineKey.MarshalText() + if err != nil { + ErrorOutput( + err, + fmt.Sprintf("Error getting machine key from flag: %s", err), + output, + ) + } + + SuccessOutput(map[string]string{ + "private_key": string(machineKeyStr), + }, + string(machineKeyStr), output) + }, +} diff --git a/cmd/headscale/cli/namespaces.go b/cmd/headscale/cli/namespaces.go index 42870370..361e9be3 100644 --- a/cmd/headscale/cli/namespaces.go +++ b/cmd/headscale/cli/namespaces.go @@ -2,12 +2,14 @@ package cli import ( "fmt" - "log" - "strconv" - "strings" + survey "github.com/AlecAivazis/survey/v2" + "github.com/juanfont/headscale" + v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/pterm/pterm" + "github.com/rs/zerolog/log" "github.com/spf13/cobra" + "google.golang.org/grpc/status" ) func init() { @@ -18,6 +20,10 @@ func init() { namespaceCmd.AddCommand(renameNamespaceCmd) } +const ( + errMissingParameter = headscale.Error("missing parameters") +) + var namespaceCmd = &cobra.Command{ Use: "namespaces", Short: "Manage the namespaces of Headscale", @@ -28,26 +34,40 @@ var createNamespaceCmd = &cobra.Command{ Short: "Creates a new namespace", Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { - return fmt.Errorf("Missing parameters") + return errMissingParameter } + return nil }, Run: func(cmd *cobra.Command, args []string) { - o, _ := cmd.Flags().GetString("output") - h, err := getHeadscaleApp() + output, _ := cmd.Flags().GetString("output") + + namespaceName := args[0] + + ctx, client, conn, cancel := getHeadscaleCLIClient() + defer cancel() + defer conn.Close() + + log.Trace().Interface("client", client).Msg("Obtained gRPC client") + + request := &v1.CreateNamespaceRequest{Name: namespaceName} + + log.Trace().Interface("request", request).Msg("Sending CreateNamespace request") + response, err := client.CreateNamespace(ctx, request) if err != nil { - log.Fatalf("Error initializing: %s", err) - } - namespace, err := h.CreateNamespace(args[0]) - if strings.HasPrefix(o, "json") { - JsonOutput(namespace, err, o) + ErrorOutput( + err, + fmt.Sprintf( + "Cannot create namespace: %s", + status.Convert(err).Message(), + ), + output, + ) + return } - if err != nil { - fmt.Printf("Error creating namespace: %s\n", err) - return - } - fmt.Printf("Namespace created\n") + + SuccessOutput(response.Namespace, "Namespace created", output) }, } @@ -56,26 +76,70 @@ var destroyNamespaceCmd = &cobra.Command{ Short: "Destroys a namespace", Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { - return fmt.Errorf("Missing parameters") + return errMissingParameter } + return nil }, Run: func(cmd *cobra.Command, args []string) { - o, _ := cmd.Flags().GetString("output") - h, err := getHeadscaleApp() - if err != nil { - log.Fatalf("Error initializing: %s", err) + output, _ := cmd.Flags().GetString("output") + + namespaceName := args[0] + + request := &v1.GetNamespaceRequest{ + Name: namespaceName, } - err = h.DestroyNamespace(args[0]) - if strings.HasPrefix(o, "json") { - JsonOutput(map[string]string{"Result": "Namespace destroyed"}, err, o) + + ctx, client, conn, cancel := getHeadscaleCLIClient() + defer cancel() + defer conn.Close() + + _, err := client.GetNamespace(ctx, request) + if err != nil { + ErrorOutput( + err, + fmt.Sprintf("Error: %s", status.Convert(err).Message()), + output, + ) + return } - if err != nil { - fmt.Printf("Error destroying namespace: %s\n", err) - return + + confirm := false + force, _ := cmd.Flags().GetBool("force") + if !force { + prompt := &survey.Confirm{ + Message: fmt.Sprintf( + "Do you want to remove the namespace '%s' and any associated preauthkeys?", + namespaceName, + ), + } + err := survey.AskOne(prompt, &confirm) + if err != nil { + return + } + } + + if confirm || force { + request := &v1.DeleteNamespaceRequest{Name: namespaceName} + + response, err := client.DeleteNamespace(ctx, request) + if err != nil { + ErrorOutput( + err, + fmt.Sprintf( + "Cannot destroy namespace: %s", + status.Convert(err).Message(), + ), + output, + ) + + return + } + SuccessOutput(response, "Namespace destroyed", output) + } else { + SuccessOutput(map[string]string{"Result": "Namespace not destroyed"}, "Namespace not destroyed", output) } - fmt.Printf("Namespace destroyed\n") }, } @@ -83,28 +147,51 @@ var listNamespacesCmd = &cobra.Command{ Use: "list", Short: "List all the namespaces", Run: func(cmd *cobra.Command, args []string) { - o, _ := cmd.Flags().GetString("output") - h, err := getHeadscaleApp() + output, _ := cmd.Flags().GetString("output") + + ctx, client, conn, cancel := getHeadscaleCLIClient() + defer cancel() + defer conn.Close() + + request := &v1.ListNamespacesRequest{} + + response, err := client.ListNamespaces(ctx, request) if err != nil { - log.Fatalf("Error initializing: %s", err) - } - namespaces, err := h.ListNamespaces() - if strings.HasPrefix(o, "json") { - JsonOutput(namespaces, err, o) - return - } - if err != nil { - fmt.Println(err) + ErrorOutput( + err, + fmt.Sprintf("Cannot get namespaces: %s", status.Convert(err).Message()), + output, + ) + return } - d := pterm.TableData{{"ID", "Name", "Created"}} - for _, n := range *namespaces { - d = append(d, []string{strconv.FormatUint(uint64(n.ID), 10), n.Name, n.CreatedAt.Format("2006-01-02 15:04:05")}) + if output != "" { + SuccessOutput(response.Namespaces, "", output) + + return } - err = pterm.DefaultTable.WithHasHeader().WithData(d).Render() + + tableData := pterm.TableData{{"ID", "Name", "Created"}} + for _, namespace := range response.GetNamespaces() { + tableData = append( + tableData, + []string{ + namespace.GetId(), + namespace.GetName(), + namespace.GetCreatedAt().AsTime().Format("2006-01-02 15:04:05"), + }, + ) + } + err = pterm.DefaultTable.WithHasHeader().WithData(tableData).Render() if err != nil { - log.Fatal(err) + ErrorOutput( + err, + fmt.Sprintf("Failed to render pterm table: %s", err), + output, + ) + + return } }, } @@ -113,26 +200,39 @@ var renameNamespaceCmd = &cobra.Command{ Use: "rename OLD_NAME NEW_NAME", Short: "Renames a namespace", Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 2 { - return fmt.Errorf("Missing parameters") + expectedArguments := 2 + if len(args) < expectedArguments { + return errMissingParameter } + return nil }, Run: func(cmd *cobra.Command, args []string) { - o, _ := cmd.Flags().GetString("output") - h, err := getHeadscaleApp() - if err != nil { - log.Fatalf("Error initializing: %s", err) + output, _ := cmd.Flags().GetString("output") + + ctx, client, conn, cancel := getHeadscaleCLIClient() + defer cancel() + defer conn.Close() + + request := &v1.RenameNamespaceRequest{ + OldName: args[0], + NewName: args[1], } - err = h.RenameNamespace(args[0], args[1]) - if strings.HasPrefix(o, "json") { - JsonOutput(map[string]string{"Result": "Namespace renamed"}, err, o) + + response, err := client.RenameNamespace(ctx, request) + if err != nil { + ErrorOutput( + err, + fmt.Sprintf( + "Cannot rename namespace: %s", + status.Convert(err).Message(), + ), + output, + ) + return } - if err != nil { - fmt.Printf("Error renaming namespace: %s\n", err) - return - } - fmt.Printf("Namespace renamed\n") + + SuccessOutput(response.Namespace, "Namespace renamed", output) }, } diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go index c44aa5ed..26ead6dc 100644 --- a/cmd/headscale/cli/nodes.go +++ b/cmd/headscale/cli/nodes.go @@ -4,28 +4,70 @@ import ( "fmt" "log" "strconv" - "strings" "time" survey "github.com/AlecAivazis/survey/v2" "github.com/juanfont/headscale" + v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/pterm/pterm" "github.com/spf13/cobra" - "tailscale.com/tailcfg" - "tailscale.com/types/wgkey" + "google.golang.org/grpc/status" + "tailscale.com/types/key" ) func init() { rootCmd.AddCommand(nodeCmd) - nodeCmd.PersistentFlags().StringP("namespace", "n", "", "Namespace") - err := nodeCmd.MarkPersistentFlagRequired("namespace") + listNodesCmd.Flags().StringP("namespace", "n", "", "Filter by namespace") + nodeCmd.AddCommand(listNodesCmd) + + registerNodeCmd.Flags().StringP("namespace", "n", "", "Namespace") + err := registerNodeCmd.MarkFlagRequired("namespace") + if err != nil { + log.Fatalf(err.Error()) + } + registerNodeCmd.Flags().StringP("key", "k", "", "Key") + err = registerNodeCmd.MarkFlagRequired("key") if err != nil { log.Fatalf(err.Error()) } - nodeCmd.AddCommand(listNodesCmd) nodeCmd.AddCommand(registerNodeCmd) + + expireNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)") + err = expireNodeCmd.MarkFlagRequired("identifier") + if err != nil { + log.Fatalf(err.Error()) + } + nodeCmd.AddCommand(expireNodeCmd) + + deleteNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)") + err = deleteNodeCmd.MarkFlagRequired("identifier") + if err != nil { + 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) } @@ -35,120 +77,208 @@ var nodeCmd = &cobra.Command{ } var registerNodeCmd = &cobra.Command{ - Use: "register machineID", + Use: "register", Short: "Registers a machine to your network", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return fmt.Errorf("missing parameters") - } - return nil - }, Run: func(cmd *cobra.Command, args []string) { - n, err := cmd.Flags().GetString("namespace") + output, _ := cmd.Flags().GetString("output") + namespace, err := cmd.Flags().GetString("namespace") if err != nil { - log.Fatalf("Error getting namespace: %s", err) - } - o, _ := cmd.Flags().GetString("output") + ErrorOutput(err, fmt.Sprintf("Error getting namespace: %s", err), output) - h, err := getHeadscaleApp() - if err != nil { - log.Fatalf("Error initializing: %s", err) - } - m, err := h.RegisterMachine(args[0], n) - if strings.HasPrefix(o, "json") { - JsonOutput(m, err, o) return } + + ctx, client, conn, cancel := getHeadscaleCLIClient() + defer cancel() + defer conn.Close() + + machineKey, err := cmd.Flags().GetString("key") if err != nil { - fmt.Printf("Cannot register machine: %s\n", err) + ErrorOutput( + err, + fmt.Sprintf("Error getting machine key from flag: %s", err), + output, + ) + return } - fmt.Printf("Machine registered\n") + + request := &v1.RegisterMachineRequest{ + Key: machineKey, + Namespace: namespace, + } + + response, err := client.RegisterMachine(ctx, request) + if err != nil { + ErrorOutput( + err, + fmt.Sprintf( + "Cannot register machine: %s\n", + status.Convert(err).Message(), + ), + output, + ) + + return + } + + SuccessOutput(response.Machine, "Machine register", output) }, } var listNodesCmd = &cobra.Command{ Use: "list", - Short: "List the nodes in a given namespace", + Short: "List nodes", Run: func(cmd *cobra.Command, args []string) { - n, err := cmd.Flags().GetString("namespace") + output, _ := cmd.Flags().GetString("output") + namespace, err := cmd.Flags().GetString("namespace") if err != nil { - log.Fatalf("Error getting namespace: %s", err) - } - o, _ := cmd.Flags().GetString("output") + ErrorOutput(err, fmt.Sprintf("Error getting namespace: %s", err), output) - h, err := getHeadscaleApp() - if err != nil { - log.Fatalf("Error initializing: %s", err) - } - - namespace, err := h.GetNamespace(n) - if err != nil { - log.Fatalf("Error fetching namespace: %s", err) - } - - machines, err := h.ListMachinesInNamespace(n) - if err != nil { - log.Fatalf("Error fetching machines: %s", err) - } - - sharedMachines, err := h.ListSharedMachinesInNamespace(n) - if err != nil { - log.Fatalf("Error fetching shared machines: %s", err) - } - - allMachines := append(*machines, *sharedMachines...) - - if strings.HasPrefix(o, "json") { - JsonOutput(allMachines, err, o) return } - if err != nil { - log.Fatalf("Error getting nodes: %s", err) + ctx, client, conn, cancel := getHeadscaleCLIClient() + defer cancel() + defer conn.Close() + + request := &v1.ListMachinesRequest{ + Namespace: namespace, } - d, err := nodesToPtables(*namespace, allMachines) + response, err := client.ListMachines(ctx, request) if err != nil { - log.Fatalf("Error converting to table: %s", err) + ErrorOutput( + err, + fmt.Sprintf("Cannot get nodes: %s", status.Convert(err).Message()), + output, + ) + + return } - err = pterm.DefaultTable.WithHasHeader().WithData(d).Render() + if output != "" { + SuccessOutput(response.Machines, "", output) + + return + } + + tableData, err := nodesToPtables(namespace, response.Machines) if err != nil { - log.Fatal(err) + ErrorOutput(err, fmt.Sprintf("Error converting to table: %s", err), output) + + return + } + + err = pterm.DefaultTable.WithHasHeader().WithData(tableData).Render() + if err != nil { + ErrorOutput( + err, + fmt.Sprintf("Failed to render pterm table: %s", err), + output, + ) + + return } }, } -var deleteNodeCmd = &cobra.Command{ - Use: "delete ID", - Short: "Delete a node", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return fmt.Errorf("missing parameters") - } - return nil - }, +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"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") - h, err := getHeadscaleApp() + + identifier, err := cmd.Flags().GetUint64("identifier") if err != nil { - log.Fatalf("Error initializing: %s", err) + ErrorOutput( + err, + fmt.Sprintf("Error converting ID to integer: %s", err), + output, + ) + + return } - id, err := strconv.Atoi(args[0]) - if err != nil { - log.Fatalf("Error converting ID to integer: %s", err) + + ctx, client, conn, cancel := getHeadscaleCLIClient() + defer cancel() + defer conn.Close() + + request := &v1.ExpireMachineRequest{ + MachineId: identifier, } - m, err := h.GetMachineByID(uint64(id)) + + response, err := client.ExpireMachine(ctx, request) if err != nil { - log.Fatalf("Error getting node: %s", err) + ErrorOutput( + err, + fmt.Sprintf( + "Cannot expire machine: %s\n", + status.Convert(err).Message(), + ), + output, + ) + + return + } + + SuccessOutput(response.Machine, "Machine expired", output) + }, +} + +var deleteNodeCmd = &cobra.Command{ + Use: "delete", + Short: "Delete a node", + Run: func(cmd *cobra.Command, args []string) { + output, _ := cmd.Flags().GetString("output") + + identifier, err := cmd.Flags().GetUint64("identifier") + if err != nil { + ErrorOutput( + err, + fmt.Sprintf("Error converting ID to integer: %s", err), + output, + ) + + return + } + + ctx, client, conn, cancel := getHeadscaleCLIClient() + defer cancel() + defer conn.Close() + + getRequest := &v1.GetMachineRequest{ + MachineId: identifier, + } + + getResponse, err := client.GetMachine(ctx, getRequest) + if err != nil { + ErrorOutput( + err, + fmt.Sprintf( + "Error getting node node: %s", + status.Convert(err).Message(), + ), + output, + ) + + return + } + + deleteRequest := &v1.DeleteMachineRequest{ + MachineId: identifier, } confirm := false force, _ := cmd.Flags().GetBool("force") if !force { prompt := &survey.Confirm{ - Message: fmt.Sprintf("Do you want to remove the node %s?", m.Name), + Message: fmt.Sprintf( + "Do you want to remove the node %s?", + getResponse.GetMachine().Name, + ), } err = survey.AskOne(prompt, &confirm) if err != nil { @@ -157,162 +287,250 @@ var deleteNodeCmd = &cobra.Command{ } if confirm || force { - err = h.DeleteMachine(m) - if strings.HasPrefix(output, "json") { - JsonOutput(map[string]string{"Result": "Node deleted"}, err, output) + response, err := client.DeleteMachine(ctx, deleteRequest) + if output != "" { + SuccessOutput(response, "", output) + return } if err != nil { - log.Fatalf("Error deleting node: %s", err) - } - fmt.Printf("Node deleted\n") - } else { - if strings.HasPrefix(output, "json") { - JsonOutput(map[string]string{"Result": "Node not deleted"}, err, output) + ErrorOutput( + err, + fmt.Sprintf( + "Error deleting node: %s", + status.Convert(err).Message(), + ), + output, + ) + return } - fmt.Printf("Node not deleted\n") + SuccessOutput( + map[string]string{"Result": "Node deleted"}, + "Node deleted", + output, + ) + } else { + SuccessOutput(map[string]string{"Result": "Node not deleted"}, "Node not deleted", output) } }, } +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 ID namespace", + Use: "share", Short: "Shares a node from the current namespace to the specified one", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 2 { - return fmt.Errorf("missing parameters") - } - return nil - }, Run: func(cmd *cobra.Command, args []string) { - namespace, err := cmd.Flags().GetString("namespace") + output, machine, namespace, err := sharingWorker(cmd) if err != nil { - log.Fatalf("Error getting namespace: %s", err) - } - output, _ := cmd.Flags().GetString("output") + ErrorOutput( + err, + fmt.Sprintf("Failed to fetch namespace or machine: %s", err), + output, + ) - h, err := getHeadscaleApp() - if err != nil { - log.Fatalf("Error initializing: %s", err) - } - - _, err = h.GetNamespace(namespace) - if err != nil { - log.Fatalf("Error fetching origin namespace: %s", err) - } - - destinationNamespace, err := h.GetNamespace(args[1]) - if err != nil { - log.Fatalf("Error fetching destination namespace: %s", err) - } - - id, err := strconv.Atoi(args[0]) - if err != nil { - log.Fatalf("Error converting ID to integer: %s", err) - } - machine, err := h.GetMachineByID(uint64(id)) - if err != nil { - log.Fatalf("Error getting node: %s", err) - } - - err = h.AddSharedMachineToNamespace(machine, destinationNamespace) - if strings.HasPrefix(output, "json") { - JsonOutput(map[string]string{"Result": "Node shared"}, err, output) - return - } - if err != nil { - fmt.Printf("Error sharing node: %s\n", err) return } - fmt.Println("Node shared!") + 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 ID", + Use: "unshare", Short: "Unshares a node from the specified namespace", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return fmt.Errorf("missing parameters") - } - return nil - }, Run: func(cmd *cobra.Command, args []string) { - namespace, err := cmd.Flags().GetString("namespace") + output, machine, namespace, err := sharingWorker(cmd) if err != nil { - log.Fatalf("Error getting namespace: %s", err) - } - output, _ := cmd.Flags().GetString("output") + ErrorOutput( + err, + fmt.Sprintf("Failed to fetch namespace or machine: %s", err), + output, + ) - h, err := getHeadscaleApp() - if err != nil { - log.Fatalf("Error initializing: %s", err) - } - - n, err := h.GetNamespace(namespace) - if err != nil { - log.Fatalf("Error fetching namespace: %s", err) - } - - id, err := strconv.Atoi(args[0]) - if err != nil { - log.Fatalf("Error converting ID to integer: %s", err) - } - machine, err := h.GetMachineByID(uint64(id)) - if err != nil { - log.Fatalf("Error getting node: %s", err) - } - - err = h.RemoveSharedMachineFromNamespace(machine, n) - if strings.HasPrefix(output, "json") { - JsonOutput(map[string]string{"Result": "Node unshared"}, err, output) - return - } - if err != nil { - fmt.Printf("Error unsharing node: %s\n", err) return } - fmt.Println("Node unshared!") + 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 headscale.Namespace, machines []headscale.Machine) (pterm.TableData, error) { - d := pterm.TableData{{"ID", "Name", "NodeKey", "Namespace", "IP address", "Ephemeral", "Last seen", "Online"}} +func nodesToPtables( + currentNamespace string, + machines []*v1.Machine, +) (pterm.TableData, error) { + tableData := pterm.TableData{ + { + "ID", + "Name", + "NodeKey", + "Namespace", + "IP address", + "Ephemeral", + "Last seen", + "Online", + "Expired", + }, + } for _, machine := range machines { var ephemeral bool - if machine.AuthKey != nil && machine.AuthKey.Ephemeral { + if machine.PreAuthKey != nil && machine.PreAuthKey.Ephemeral { ephemeral = true } + var lastSeen time.Time var lastSeenTime string if machine.LastSeen != nil { - lastSeen = *machine.LastSeen + lastSeen = machine.LastSeen.AsTime() lastSeenTime = lastSeen.Format("2006-01-02 15:04:05") } - nKey, err := wgkey.ParseHex(machine.NodeKey) + + var expiry time.Time + if machine.Expiry != nil { + expiry = machine.Expiry.AsTime() + } + + var nodeKey key.NodePublic + err := nodeKey.UnmarshalText( + []byte(headscale.NodePublicKeyEnsurePrefix(machine.NodeKey)), + ) if err != nil { return nil, err } - nodeKey := tailcfg.NodeKey(nKey) var online string - if lastSeen.After(time.Now().Add(-5 * time.Minute)) { // TODO: Find a better way to reliably show if online - online = pterm.LightGreen("true") + if lastSeen.After( + time.Now().Add(-5 * time.Minute), + ) { // TODO: Find a better way to reliably show if online + online = pterm.LightGreen("online") } else { - online = pterm.LightRed("false") + online = pterm.LightRed("offline") + } + + var expired string + if expiry.IsZero() || expiry.After(time.Now()) { + expired = pterm.LightGreen("no") + } else { + expired = pterm.LightRed("yes") } var namespace string - if currentNamespace.ID == machine.NamespaceID { + if currentNamespace == "" || (currentNamespace == machine.Namespace.Name) { namespace = pterm.LightMagenta(machine.Namespace.Name) } else { + // Shared into this namespace namespace = pterm.LightYellow(machine.Namespace.Name) } - d = append(d, []string{strconv.FormatUint(machine.ID, 10), machine.Name, nodeKey.ShortString(), namespace, machine.IPAddress, strconv.FormatBool(ephemeral), lastSeenTime, online}) + tableData = append( + tableData, + []string{ + strconv.FormatUint(machine.Id, headscale.Base10), + machine.Name, + nodeKey.ShortString(), + namespace, + machine.IpAddress, + strconv.FormatBool(ephemeral), + lastSeenTime, + online, + expired, + }, + ) } - return d, nil + + return tableData, nil } diff --git a/cmd/headscale/cli/preauthkeys.go b/cmd/headscale/cli/preauthkeys.go index d7cebec1..5342085c 100644 --- a/cmd/headscale/cli/preauthkeys.go +++ b/cmd/headscale/cli/preauthkeys.go @@ -2,14 +2,18 @@ package cli import ( "fmt" - "log" "strconv" - "strings" "time" - "github.com/hako/durafmt" + v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/pterm/pterm" + "github.com/rs/zerolog/log" "github.com/spf13/cobra" + "google.golang.org/protobuf/types/known/timestamppb" +) + +const ( + DefaultPreAuthKeyExpiry = 1 * time.Hour ) func init() { @@ -17,14 +21,17 @@ func init() { preauthkeysCmd.PersistentFlags().StringP("namespace", "n", "", "Namespace") err := preauthkeysCmd.MarkPersistentFlagRequired("namespace") if err != nil { - log.Fatalf(err.Error()) + log.Fatal().Err(err).Msg("") } preauthkeysCmd.AddCommand(listPreAuthKeys) preauthkeysCmd.AddCommand(createPreAuthKeyCmd) preauthkeysCmd.AddCommand(expirePreAuthKeyCmd) - createPreAuthKeyCmd.PersistentFlags().Bool("reusable", false, "Make the preauthkey reusable") - createPreAuthKeyCmd.PersistentFlags().Bool("ephemeral", false, "Preauthkey for ephemeral nodes") - createPreAuthKeyCmd.Flags().StringP("expiration", "e", "", "Human-readable expiration of the key (30m, 24h, 365d...)") + createPreAuthKeyCmd.PersistentFlags(). + Bool("reusable", false, "Make the preauthkey reusable") + createPreAuthKeyCmd.PersistentFlags(). + Bool("ephemeral", false, "Preauthkey for ephemeral nodes") + createPreAuthKeyCmd.Flags(). + DurationP("expiration", "e", DefaultPreAuthKeyExpiry, "Human-readable expiration of the key (30m, 24h, 365d...)") } var preauthkeysCmd = &cobra.Command{ @@ -36,55 +43,76 @@ var listPreAuthKeys = &cobra.Command{ Use: "list", Short: "List the preauthkeys for this namespace", Run: func(cmd *cobra.Command, args []string) { - n, err := cmd.Flags().GetString("namespace") - if err != nil { - log.Fatalf("Error getting namespace: %s", err) - } - o, _ := cmd.Flags().GetString("output") + output, _ := cmd.Flags().GetString("output") - h, err := getHeadscaleApp() + namespace, err := cmd.Flags().GetString("namespace") if err != nil { - log.Fatalf("Error initializing: %s", err) - } - keys, err := h.GetPreAuthKeys(n) - if strings.HasPrefix(o, "json") { - JsonOutput(keys, err, o) + ErrorOutput(err, fmt.Sprintf("Error getting namespace: %s", err), output) + return } + ctx, client, conn, cancel := getHeadscaleCLIClient() + defer cancel() + defer conn.Close() + + request := &v1.ListPreAuthKeysRequest{ + Namespace: namespace, + } + + response, err := client.ListPreAuthKeys(ctx, request) if err != nil { - fmt.Printf("Error getting the list of keys: %s\n", err) + ErrorOutput( + err, + fmt.Sprintf("Error getting the list of keys: %s", err), + output, + ) + return } - d := pterm.TableData{{"ID", "Key", "Reusable", "Ephemeral", "Used", "Expiration", "Created"}} - for _, k := range *keys { + if output != "" { + SuccessOutput(response.PreAuthKeys, "", output) + + return + } + + tableData := pterm.TableData{ + {"ID", "Key", "Reusable", "Ephemeral", "Used", "Expiration", "Created"}, + } + for _, key := range response.PreAuthKeys { expiration := "-" - if k.Expiration != nil { - expiration = k.Expiration.Format("2006-01-02 15:04:05") + if key.GetExpiration() != nil { + expiration = key.Expiration.AsTime().Format("2006-01-02 15:04:05") } var reusable string - if k.Ephemeral { + if key.GetEphemeral() { reusable = "N/A" } else { - reusable = fmt.Sprintf("%v", k.Reusable) + reusable = fmt.Sprintf("%v", key.GetReusable()) } - d = append(d, []string{ - strconv.FormatUint(k.ID, 10), - k.Key, + tableData = append(tableData, []string{ + key.GetId(), + key.GetKey(), reusable, - strconv.FormatBool(k.Ephemeral), - fmt.Sprintf("%v", k.Used), + strconv.FormatBool(key.GetEphemeral()), + strconv.FormatBool(key.GetUsed()), expiration, - k.CreatedAt.Format("2006-01-02 15:04:05"), + key.GetCreatedAt().AsTime().Format("2006-01-02 15:04:05"), }) } - err = pterm.DefaultTable.WithHasHeader().WithData(d).Render() + err = pterm.DefaultTable.WithHasHeader().WithData(tableData).Render() if err != nil { - log.Fatal(err) + ErrorOutput( + err, + fmt.Sprintf("Failed to render pterm table: %s", err), + output, + ) + + return } }, } @@ -93,40 +121,53 @@ var createPreAuthKeyCmd = &cobra.Command{ Use: "create", Short: "Creates a new preauthkey in the specified namespace", Run: func(cmd *cobra.Command, args []string) { - n, err := cmd.Flags().GetString("namespace") - if err != nil { - log.Fatalf("Error getting namespace: %s", err) - } - o, _ := cmd.Flags().GetString("output") + output, _ := cmd.Flags().GetString("output") - h, err := getHeadscaleApp() + namespace, err := cmd.Flags().GetString("namespace") if err != nil { - log.Fatalf("Error initializing: %s", err) + ErrorOutput(err, fmt.Sprintf("Error getting namespace: %s", err), output) + + return } + reusable, _ := cmd.Flags().GetBool("reusable") ephemeral, _ := cmd.Flags().GetBool("ephemeral") - e, _ := cmd.Flags().GetString("expiration") - var expiration *time.Time - if e != "" { - duration, err := durafmt.ParseStringShort(e) - if err != nil { - log.Fatalf("Error parsing expiration: %s", err) - } - exp := time.Now().UTC().Add(duration.Duration()) - expiration = &exp + log.Trace(). + Bool("reusable", reusable). + Bool("ephemeral", ephemeral). + Str("namespace", namespace). + Msg("Preparing to create preauthkey") + + request := &v1.CreatePreAuthKeyRequest{ + Namespace: namespace, + Reusable: reusable, + Ephemeral: ephemeral, } - k, err := h.CreatePreAuthKey(n, reusable, ephemeral, expiration) - if strings.HasPrefix(o, "json") { - JsonOutput(k, err, o) - return - } + duration, _ := cmd.Flags().GetDuration("expiration") + expiration := time.Now().UTC().Add(duration) + + log.Trace().Dur("expiration", duration).Msg("expiration has been set") + + request.Expiration = timestamppb.New(expiration) + + ctx, client, conn, cancel := getHeadscaleCLIClient() + defer cancel() + defer conn.Close() + + response, err := client.CreatePreAuthKey(ctx, request) if err != nil { - fmt.Println(err) + ErrorOutput( + err, + fmt.Sprintf("Cannot create Pre Auth Key: %s\n", err), + output, + ) + return } - fmt.Printf("%s\n", k.Key) + + SuccessOutput(response.PreAuthKey, response.PreAuthKey.Key, output) }, } @@ -135,40 +176,40 @@ var expirePreAuthKeyCmd = &cobra.Command{ Short: "Expire a preauthkey", Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { - return fmt.Errorf("missing parameters") + return errMissingParameter } + return nil }, Run: func(cmd *cobra.Command, args []string) { - n, err := cmd.Flags().GetString("namespace") + output, _ := cmd.Flags().GetString("output") + namespace, err := cmd.Flags().GetString("namespace") if err != nil { - log.Fatalf("Error getting namespace: %s", err) - } - o, _ := cmd.Flags().GetString("output") + ErrorOutput(err, fmt.Sprintf("Error getting namespace: %s", err), output) - h, err := getHeadscaleApp() - if err != nil { - log.Fatalf("Error initializing: %s", err) - } - - k, err := h.GetPreAuthKey(n, args[0]) - if err != nil { - if strings.HasPrefix(o, "json") { - JsonOutput(k, err, o) - return - } - log.Fatalf("Error getting the key: %s", err) - } - - err = h.MarkExpirePreAuthKey(k) - if strings.HasPrefix(o, "json") { - JsonOutput(k, err, o) return } + + ctx, client, conn, cancel := getHeadscaleCLIClient() + defer cancel() + defer conn.Close() + + request := &v1.ExpirePreAuthKeyRequest{ + Namespace: namespace, + Key: args[0], + } + + response, err := client.ExpirePreAuthKey(ctx, request) if err != nil { - fmt.Println(err) + ErrorOutput( + err, + fmt.Sprintf("Cannot expire Pre Auth Key: %s\n", err), + output, + ) + return } - fmt.Println("Expired") + + SuccessOutput(response, "Key expired", output) }, } diff --git a/cmd/headscale/cli/root.go b/cmd/headscale/cli/root.go index 794cd0d0..99b15140 100644 --- a/cmd/headscale/cli/root.go +++ b/cmd/headscale/cli/root.go @@ -8,8 +8,10 @@ import ( ) func init() { - rootCmd.PersistentFlags().StringP("output", "o", "", "Output format. Empty for human-readable, 'json' or 'json-line'") - rootCmd.PersistentFlags().Bool("force", false, "Disable prompts and forces the execution") + rootCmd.PersistentFlags(). + StringP("output", "o", "", "Output format. Empty for human-readable, 'json', 'json-line' or 'yaml'") + rootCmd.PersistentFlags(). + Bool("force", false, "Disable prompts and forces the execution") } var rootCmd = &cobra.Command{ diff --git a/cmd/headscale/cli/routes.go b/cmd/headscale/cli/routes.go index 72010864..ced1a0bf 100644 --- a/cmd/headscale/cli/routes.go +++ b/cmd/headscale/cli/routes.go @@ -3,24 +3,35 @@ package cli import ( "fmt" "log" - "strings" + "strconv" + v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/pterm/pterm" "github.com/spf13/cobra" + "google.golang.org/grpc/status" ) func init() { rootCmd.AddCommand(routesCmd) - routesCmd.PersistentFlags().StringP("namespace", "n", "", "Namespace") - err := routesCmd.MarkPersistentFlagRequired("namespace") + + listRoutesCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)") + err := listRoutesCmd.MarkFlagRequired("identifier") + if err != nil { + log.Fatalf(err.Error()) + } + routesCmd.AddCommand(listRoutesCmd) + + enableRouteCmd.Flags(). + StringSliceP("route", "r", []string{}, "List (or repeated flags) of routes to enable") + enableRouteCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)") + err = enableRouteCmd.MarkFlagRequired("identifier") if err != nil { log.Fatalf(err.Error()) } - enableRouteCmd.Flags().BoolP("all", "a", false, "Enable all routes advertised by the node") - - routesCmd.AddCommand(listRoutesCmd) routesCmd.AddCommand(enableRouteCmd) + + nodeCmd.AddCommand(routesCmd) } var routesCmd = &cobra.Command{ @@ -29,119 +40,168 @@ var routesCmd = &cobra.Command{ } var listRoutesCmd = &cobra.Command{ - Use: "list NODE", - Short: "List the routes exposed by this node", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return fmt.Errorf("Missing parameters") - } - return nil - }, + Use: "list", + Short: "List routes advertised and enabled by a given node", Run: func(cmd *cobra.Command, args []string) { - n, err := cmd.Flags().GetString("namespace") - if err != nil { - log.Fatalf("Error getting namespace: %s", err) - } - o, _ := cmd.Flags().GetString("output") + output, _ := cmd.Flags().GetString("output") - h, err := getHeadscaleApp() + machineID, err := cmd.Flags().GetUint64("identifier") if err != nil { - log.Fatalf("Error initializing: %s", err) - } + ErrorOutput( + err, + fmt.Sprintf("Error getting machine id from flag: %s", err), + output, + ) - availableRoutes, err := h.GetAdvertisedNodeRoutes(n, args[0]) - if err != nil { - fmt.Println(err) return } - if strings.HasPrefix(o, "json") { - // TODO: Add enable/disabled information to this interface - JsonOutput(availableRoutes, err, o) + ctx, client, conn, cancel := getHeadscaleCLIClient() + defer cancel() + defer conn.Close() + + request := &v1.GetMachineRouteRequest{ + MachineId: machineID, + } + + response, err := client.GetMachineRoute(ctx, request) + if err != nil { + ErrorOutput( + err, + fmt.Sprintf("Cannot get nodes: %s", status.Convert(err).Message()), + output, + ) + return } - d := h.RoutesToPtables(n, args[0], *availableRoutes) + if output != "" { + SuccessOutput(response.Routes, "", output) - err = pterm.DefaultTable.WithHasHeader().WithData(d).Render() + return + } + + tableData := routesToPtables(response.Routes) if err != nil { - log.Fatal(err) + ErrorOutput(err, fmt.Sprintf("Error converting to table: %s", err), output) + + return + } + + err = pterm.DefaultTable.WithHasHeader().WithData(tableData).Render() + if err != nil { + ErrorOutput( + err, + fmt.Sprintf("Failed to render pterm table: %s", err), + output, + ) + + return } }, } var enableRouteCmd = &cobra.Command{ - Use: "enable node-name route", - Short: "Allows exposing a route declared by this node to the rest of the nodes", - Args: func(cmd *cobra.Command, args []string) error { - all, err := cmd.Flags().GetBool("all") - if err != nil { - log.Fatalf("Error getting namespace: %s", err) - } - - if all { - if len(args) < 1 { - return fmt.Errorf("Missing parameters") - } - return nil - } else { - if len(args) < 2 { - return fmt.Errorf("Missing parameters") - } - return nil - } - }, + Use: "enable", + Short: "Set the enabled routes for a given node", + Long: `This command will take a list of routes that will _replace_ +the current set of routes on a given node. +If you would like to disable a route, simply run the command again, but +omit the route you do not want to enable. + `, Run: func(cmd *cobra.Command, args []string) { - n, err := cmd.Flags().GetString("namespace") + output, _ := cmd.Flags().GetString("output") + + machineID, err := cmd.Flags().GetUint64("identifier") if err != nil { - log.Fatalf("Error getting namespace: %s", err) + ErrorOutput( + err, + fmt.Sprintf("Error getting machine id from flag: %s", err), + output, + ) + + return } - o, _ := cmd.Flags().GetString("output") - - all, err := cmd.Flags().GetBool("all") + routes, err := cmd.Flags().GetStringSlice("route") if err != nil { - log.Fatalf("Error getting namespace: %s", err) + ErrorOutput( + err, + fmt.Sprintf("Error getting routes from flag: %s", err), + output, + ) + + return } - h, err := getHeadscaleApp() - if err != nil { - log.Fatalf("Error initializing: %s", err) + ctx, client, conn, cancel := getHeadscaleCLIClient() + defer cancel() + defer conn.Close() + + request := &v1.EnableMachineRoutesRequest{ + MachineId: machineID, + Routes: routes, } - if all { - availableRoutes, err := h.GetAdvertisedNodeRoutes(n, args[0]) - if err != nil { - fmt.Println(err) - return - } + response, err := client.EnableMachineRoutes(ctx, request) + if err != nil { + ErrorOutput( + err, + fmt.Sprintf( + "Cannot register machine: %s\n", + status.Convert(err).Message(), + ), + output, + ) - for _, availableRoute := range *availableRoutes { - err = h.EnableNodeRoute(n, args[0], availableRoute.String()) - if err != nil { - fmt.Println(err) - return - } + return + } - if strings.HasPrefix(o, "json") { - JsonOutput(availableRoute, err, o) - } else { - fmt.Printf("Enabled route %s\n", availableRoute) - } - } - } else { - err = h.EnableNodeRoute(n, args[0], args[1]) + if output != "" { + SuccessOutput(response.Routes, "", output) - if strings.HasPrefix(o, "json") { - JsonOutput(args[1], err, o) - return - } + return + } - if err != nil { - fmt.Println(err) - return - } - fmt.Printf("Enabled route %s\n", args[1]) + tableData := routesToPtables(response.Routes) + if err != nil { + ErrorOutput(err, fmt.Sprintf("Error converting to table: %s", err), output) + + return + } + + err = pterm.DefaultTable.WithHasHeader().WithData(tableData).Render() + if err != nil { + ErrorOutput( + err, + fmt.Sprintf("Failed to render pterm table: %s", err), + output, + ) + + return } }, } + +// routesToPtables converts the list of routes to a nice table. +func routesToPtables(routes *v1.Routes) pterm.TableData { + tableData := pterm.TableData{{"Route", "Enabled"}} + + for _, route := range routes.GetAdvertisedRoutes() { + enabled := isStringInSlice(routes.EnabledRoutes, route) + + tableData = append(tableData, []string{route, strconv.FormatBool(enabled)}) + } + + return tableData +} + +func isStringInSlice(strs []string, s string) bool { + for _, s2 := range strs { + if s == s2 { + return true + } + } + + return false +} diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index 0768e1eb..c2016271 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -1,26 +1,33 @@ package cli import ( + "context" "encoding/json" "errors" "fmt" + "io/fs" "net/url" "os" "path/filepath" + "regexp" + "strconv" "strings" "time" "github.com/juanfont/headscale" + v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/rs/zerolog/log" "github.com/spf13/viper" + "google.golang.org/grpc" + "gopkg.in/yaml.v2" "inet.af/netaddr" "tailscale.com/tailcfg" "tailscale.com/types/dnstype" ) -type ErrorOutput struct { - Error string -} +const ( + PermissionFallback = 0o700 +) func LoadConfig(path string) error { viper.SetConfigName("config") @@ -32,6 +39,9 @@ func LoadConfig(path string) error { // For testing viper.AddConfigPath(path) } + + viper.SetEnvPrefix("headscale") + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) viper.AutomaticEnv() viper.SetDefault("tls_letsencrypt_cache_dir", "/var/www/.cache") @@ -43,9 +53,14 @@ func LoadConfig(path string) error { viper.SetDefault("dns_config", nil) - err := viper.ReadInConfig() - if err != nil { - return fmt.Errorf("Fatal error reading config file: %s \n", err) + viper.SetDefault("unix_socket", "/var/run/headscale.sock") + viper.SetDefault("unix_socket_permission", "0o770") + + viper.SetDefault("cli.insecure", false) + viper.SetDefault("cli.timeout", "5s") + + if err := viper.ReadInConfig(); err != nil { + return fmt.Errorf("fatal error reading config file: %w", err) } // Collect any validation errors and return them all at once @@ -73,6 +88,7 @@ func LoadConfig(path string) error { errorText += "Fatal config error: server_url must start with https:// or http://\n" } if errorText != "" { + //nolint return errors.New(strings.TrimSuffix(errorText, "\n")) } else { return nil @@ -140,9 +156,14 @@ func GetDNSConfig() (*tailcfg.DNSConfig, string) { if viper.IsSet("dns_config.restricted_nameservers") { if len(dnsConfig.Nameservers) > 0 { dnsConfig.Routes = make(map[string][]dnstype.Resolver) - restrictedDNS := viper.GetStringMapStringSlice("dns_config.restricted_nameservers") + restrictedDNS := viper.GetStringMapStringSlice( + "dns_config.restricted_nameservers", + ) for domain, restrictedNameservers := range restrictedDNS { - restrictedResolvers := make([]dnstype.Resolver, len(restrictedNameservers)) + restrictedResolvers := make( + []dnstype.Resolver, + len(restrictedNameservers), + ) for index, nameserverStr := range restrictedNameservers { nameserver, err := netaddr.ParseIP(nameserverStr) if err != nil { @@ -199,35 +220,26 @@ func absPath(path string) string { path = filepath.Join(dir, path) } } + return path } -func getHeadscaleApp() (*headscale.Headscale, error) { - // Minimum inactivity time out is keepalive timeout (60s) plus a few seconds - // to avoid races - minInactivityTimeout, _ := time.ParseDuration("65s") - if viper.GetDuration("ephemeral_node_inactivity_timeout") <= minInactivityTimeout { - err := fmt.Errorf( - "ephemeral_node_inactivity_timeout (%s) is set too low, must be more than %s\n", - viper.GetString("ephemeral_node_inactivity_timeout"), - minInactivityTimeout, - ) - return nil, err - } - +func getHeadscaleConfig() headscale.Config { dnsConfig, baseDomain := GetDNSConfig() derpConfig := GetDERPConfig() - cfg := headscale.Config{ + return headscale.Config{ ServerURL: viper.GetString("server_url"), Addr: viper.GetString("listen_addr"), - PrivateKeyPath: absPath(viper.GetString("private_key_path")), IPPrefix: netaddr.MustParseIPPrefix(viper.GetString("ip_prefix")), + PrivateKeyPath: absPath(viper.GetString("private_key_path")), BaseDomain: baseDomain, DERP: derpConfig, - EphemeralNodeInactivityTimeout: viper.GetDuration("ephemeral_node_inactivity_timeout"), + EphemeralNodeInactivityTimeout: viper.GetDuration( + "ephemeral_node_inactivity_timeout", + ), DBtype: viper.GetString("db_type"), DBpath: absPath(viper.GetString("db_path")), @@ -237,9 +249,11 @@ func getHeadscaleApp() (*headscale.Headscale, error) { DBuser: viper.GetString("db_user"), DBpass: viper.GetString("db_pass"), - TLSLetsEncryptHostname: viper.GetString("tls_letsencrypt_hostname"), - TLSLetsEncryptListen: viper.GetString("tls_letsencrypt_listen"), - TLSLetsEncryptCacheDir: absPath(viper.GetString("tls_letsencrypt_cache_dir")), + TLSLetsEncryptHostname: viper.GetString("tls_letsencrypt_hostname"), + TLSLetsEncryptListen: viper.GetString("tls_letsencrypt_listen"), + TLSLetsEncryptCacheDir: absPath( + viper.GetString("tls_letsencrypt_cache_dir"), + ), TLSLetsEncryptChallengeType: viper.GetString("tls_letsencrypt_challenge_type"), TLSCertPath: absPath(viper.GetString("tls_cert_path")), @@ -249,9 +263,46 @@ func getHeadscaleApp() (*headscale.Headscale, error) { ACMEEmail: viper.GetString("acme_email"), ACMEURL: viper.GetString("acme_url"), + + UnixSocket: viper.GetString("unix_socket"), + 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"), + }, + + CLI: headscale.CLIConfig{ + Address: viper.GetString("cli.address"), + APIKey: viper.GetString("cli.api_key"), + Insecure: viper.GetBool("cli.insecure"), + Timeout: viper.GetDuration("cli.timeout"), + }, + } +} + +func getHeadscaleApp() (*headscale.Headscale, error) { + // Minimum inactivity time out is keepalive timeout (60s) plus a few seconds + // to avoid races + minInactivityTimeout, _ := time.ParseDuration("65s") + if viper.GetDuration("ephemeral_node_inactivity_timeout") <= minInactivityTimeout { + // TODO: Find a better way to return this text + //nolint + err := fmt.Errorf( + "ephemeral_node_inactivity_timeout (%s) is set too low, must be more than %s", + viper.GetString("ephemeral_node_inactivity_timeout"), + minInactivityTimeout, + ) + + return nil, err } - h, err := headscale.NewHeadscale(cfg) + cfg := getHeadscaleConfig() + + cfg.OIDC.MatchMap = loadOIDCMatchMap() + + app, err := headscale.NewHeadscale(cfg) if err != nil { return nil, err } @@ -260,7 +311,7 @@ func getHeadscaleApp() (*headscale.Headscale, error) { if viper.GetString("acl_policy_path") != "" { aclPath := absPath(viper.GetString("acl_policy_path")) - err = h.LoadACLPolicy(aclPath) + err = app.LoadACLPolicy(aclPath) if err != nil { log.Error(). Str("path", aclPath). @@ -269,46 +320,150 @@ func getHeadscaleApp() (*headscale.Headscale, error) { } } - return h, nil + return app, nil } -func JsonOutput(result interface{}, errResult error, outputFormat string) { +func getHeadscaleCLIClient() (context.Context, v1.HeadscaleServiceClient, *grpc.ClientConn, context.CancelFunc) { + cfg := getHeadscaleConfig() + + log.Debug(). + Dur("timeout", cfg.CLI.Timeout). + Msgf("Setting timeout") + + ctx, cancel := context.WithTimeout(context.Background(), cfg.CLI.Timeout) + + grpcOptions := []grpc.DialOption{ + grpc.WithBlock(), + } + + address := cfg.CLI.Address + + // If the address is not set, we assume that we are on the server hosting headscale. + if address == "" { + log.Debug(). + Str("socket", cfg.UnixSocket). + Msgf("HEADSCALE_CLI_ADDRESS environment is not set, connecting to unix socket.") + + address = cfg.UnixSocket + + grpcOptions = append( + grpcOptions, + grpc.WithInsecure(), + grpc.WithContextDialer(headscale.GrpcSocketDialer), + ) + } else { + // If we are not connecting to a local server, require an API key for authentication + apiKey := cfg.CLI.APIKey + if apiKey == "" { + log.Fatal().Msgf("HEADSCALE_CLI_API_KEY environment variable needs to be set.") + } + grpcOptions = append(grpcOptions, + grpc.WithPerRPCCredentials(tokenAuth{ + token: apiKey, + }), + ) + + if cfg.CLI.Insecure { + grpcOptions = append(grpcOptions, grpc.WithInsecure()) + } + } + + log.Trace().Caller().Str("address", address).Msg("Connecting via gRPC") + conn, err := grpc.DialContext(ctx, address, grpcOptions...) + if err != nil { + log.Fatal().Err(err).Msgf("Could not connect: %v", err) + } + + client := v1.NewHeadscaleServiceClient(conn) + + return ctx, client, conn, cancel +} + +func SuccessOutput(result interface{}, override string, outputFormat string) { var j []byte var err error switch outputFormat { case "json": - if errResult != nil { - j, err = json.MarshalIndent(ErrorOutput{errResult.Error()}, "", "\t") - if err != nil { - log.Fatal().Err(err) - } - } else { - j, err = json.MarshalIndent(result, "", "\t") - if err != nil { - log.Fatal().Err(err) - } + j, err = json.MarshalIndent(result, "", "\t") + if err != nil { + log.Fatal().Err(err) } case "json-line": - if errResult != nil { - j, err = json.Marshal(ErrorOutput{errResult.Error()}) - if err != nil { - log.Fatal().Err(err) - } - } else { - j, err = json.Marshal(result) - if err != nil { - log.Fatal().Err(err) - } + j, err = json.Marshal(result) + if err != nil { + log.Fatal().Err(err) } + case "yaml": + j, err = yaml.Marshal(result) + if err != nil { + log.Fatal().Err(err) + } + default: + //nolint + fmt.Println(override) + + return } + + //nolint fmt.Println(string(j)) } -func HasJsonOutputFlag() bool { +func ErrorOutput(errResult error, override string, outputFormat string) { + type errOutput struct { + Error string `json:"error"` + } + + SuccessOutput(errOutput{errResult.Error()}, override, outputFormat) +} + +func HasMachineOutputFlag() bool { for _, arg := range os.Args { - if arg == "json" || arg == "json-line" { + if arg == "json" || arg == "json-line" || arg == "yaml" { return true } } + return false } + +type tokenAuth struct { + token string +} + +// Return value is mapped to request headers. +func (t tokenAuth) GetRequestMetadata( + ctx context.Context, + in ...string, +) (map[string]string, error) { + return map[string]string{ + "authorization": "Bearer " + t.token, + }, nil +} + +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) + + mode, err := strconv.ParseUint(modeStr, headscale.Base8, headscale.BitSize64) + if err != nil { + return PermissionFallback + } + + return fs.FileMode(mode) +} diff --git a/cmd/headscale/cli/version.go b/cmd/headscale/cli/version.go index c018b142..2b440af3 100644 --- a/cmd/headscale/cli/version.go +++ b/cmd/headscale/cli/version.go @@ -1,9 +1,6 @@ package cli import ( - "fmt" - "strings" - "github.com/spf13/cobra" ) @@ -18,11 +15,7 @@ var versionCmd = &cobra.Command{ Short: "Print the version.", Long: "The version of headscale.", Run: func(cmd *cobra.Command, args []string) { - o, _ := cmd.Flags().GetString("output") - if strings.HasPrefix(o, "json") { - JsonOutput(map[string]string{"version": Version}, nil, o) - return - } - fmt.Println(Version) + output, _ := cmd.Flags().GetString("output") + SuccessOutput(map[string]string{"version": Version}, Version, output) }, } diff --git a/cmd/headscale/headscale.go b/cmd/headscale/headscale.go index 6b1a8437..d6bf2166 100644 --- a/cmd/headscale/headscale.go +++ b/cmd/headscale/headscale.go @@ -23,6 +23,8 @@ func main() { colors = true case termcolor.LevelBasic: colors = true + case termcolor.LevelNone: + colors = false default: // no color, return text as is. colors = false @@ -41,38 +43,41 @@ func main() { NoColor: !colors, }) - err := cli.LoadConfig("") - if err != nil { + if err := cli.LoadConfig(""); err != nil { log.Fatal().Err(err) } + machineOutput := cli.HasMachineOutputFlag() + logLevel := viper.GetString("log_level") - switch logLevel { - case "trace": - zerolog.SetGlobalLevel(zerolog.TraceLevel) - case "debug": - zerolog.SetGlobalLevel(zerolog.DebugLevel) - case "info": - zerolog.SetGlobalLevel(zerolog.InfoLevel) - case "warn": - zerolog.SetGlobalLevel(zerolog.WarnLevel) - case "error": - zerolog.SetGlobalLevel(zerolog.ErrorLevel) - default: + level, err := zerolog.ParseLevel(logLevel) + if err != nil { zerolog.SetGlobalLevel(zerolog.DebugLevel) + } else { + zerolog.SetGlobalLevel(level) } - jsonOutput := cli.HasJsonOutputFlag() - if !viper.GetBool("disable_check_updates") && !jsonOutput { - if (runtime.GOOS == "linux" || runtime.GOOS == "darwin") && cli.Version != "dev" { + // If the user has requested a "machine" readable format, + // then disable login so the output remains valid. + if machineOutput { + zerolog.SetGlobalLevel(zerolog.Disabled) + } + + if !viper.GetBool("disable_check_updates") && !machineOutput { + if (runtime.GOOS == "linux" || runtime.GOOS == "darwin") && + cli.Version != "dev" { githubTag := &latest.GithubTag{ Owner: "juanfont", Repository: "headscale", } res, err := latest.Check(githubTag, cli.Version) if err == nil && res.Outdated { - fmt.Printf("An updated version of Headscale has been found (%s vs. your current %s). Check it out https://github.com/juanfont/headscale/releases\n", - res.Current, cli.Version) + //nolint + fmt.Printf( + "An updated version of Headscale has been found (%s vs. your current %s). Check it out https://github.com/juanfont/headscale/releases\n", + res.Current, + cli.Version, + ) } } } diff --git a/cmd/headscale/headscale_test.go b/cmd/headscale/headscale_test.go index e3a5713f..218e458e 100644 --- a/cmd/headscale/headscale_test.go +++ b/cmd/headscale/headscale_test.go @@ -1,7 +1,7 @@ package main import ( - "fmt" + "io/fs" "io/ioutil" "os" "path/filepath" @@ -40,7 +40,10 @@ func (*Suite) TestConfigLoading(c *check.C) { } // Symlink the example config file - err = os.Symlink(filepath.Clean(path+"/../../config-example.yaml"), filepath.Join(tmpDir, "config.yaml")) + err = os.Symlink( + filepath.Clean(path+"/../../config-example.yaml"), + filepath.Join(tmpDir, "config.yaml"), + ) if err != nil { c.Fatal(err) } @@ -52,13 +55,13 @@ 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.GetStringSlice("derp.paths")[0], check.Equals, "derp-example.yaml") c.Assert(viper.GetString("db_type"), check.Equals, "sqlite3") - c.Assert(viper.GetString("db_path"), check.Equals, "db.sqlite") + c.Assert(viper.GetString("db_path"), check.Equals, "/var/lib/headscale/db.sqlite") c.Assert(viper.GetString("tls_letsencrypt_hostname"), check.Equals, "") c.Assert(viper.GetString("tls_letsencrypt_listen"), check.Equals, ":http") c.Assert(viper.GetString("tls_letsencrypt_challenge_type"), check.Equals, "HTTP-01") c.Assert(viper.GetStringSlice("dns_config.nameservers")[0], check.Equals, "1.1.1.1") + c.Assert(cli.GetFileMode("unix_socket_permission"), check.Equals, fs.FileMode(0o770)) } func (*Suite) TestDNSConfigLoading(c *check.C) { @@ -74,7 +77,10 @@ func (*Suite) TestDNSConfigLoading(c *check.C) { } // Symlink the example config file - err = os.Symlink(filepath.Clean(path+"/../../config-example.yaml"), filepath.Join(tmpDir, "config.yaml")) + err = os.Symlink( + filepath.Clean(path+"/../../config-example.yaml"), + filepath.Join(tmpDir, "config.yaml"), + ) if err != nil { c.Fatal(err) } @@ -94,7 +100,7 @@ func (*Suite) TestDNSConfigLoading(c *check.C) { func writeConfig(c *check.C, tmpDir string, configYaml []byte) { // Populate a custom config file configFile := filepath.Join(tmpDir, "config.yaml") - err := ioutil.WriteFile(configFile, configYaml, 0o644) + err := ioutil.WriteFile(configFile, configYaml, 0o600) if err != nil { c.Fatalf("Couldn't write file %s", configFile) } @@ -106,7 +112,6 @@ func (*Suite) TestTLSConfigValidation(c *check.C) { c.Fatal(err) } // defer os.RemoveAll(tmpDir) - fmt.Println(tmpDir) configYaml := []byte( "---\ntls_letsencrypt_hostname: \"example.com\"\ntls_letsencrypt_challenge_type: \"\"\ntls_cert_path: \"abc.pem\"", @@ -128,8 +133,11 @@ func (*Suite) TestTLSConfigValidation(c *check.C) { check.Matches, ".*Fatal config error: the only supported values for tls_letsencrypt_challenge_type are.*", ) - c.Assert(tmp, check.Matches, ".*Fatal config error: server_url must start with https:// or http://.*") - fmt.Println(tmp) + c.Assert( + tmp, + check.Matches, + ".*Fatal config error: server_url must start with https:// or http://.*", + ) // Check configuration validation errors (2) configYaml = []byte( diff --git a/config-example.yaml b/config-example.yaml index 59370eb5..3a867a36 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -1,38 +1,65 @@ --- +# headscale will look for a configuration file named `config.yaml` (or `config.json`) in the following order: +# +# - `/etc/headscale` +# - `~/.headscale` +# - current working directory + # The url clients will connect to. -# Typically this will be a domain. +# Typically this will be a domain like: +# +# https://myheadscale.example.com:443 +# server_url: http://127.0.0.1:8080 # Address to listen to / bind to on the server +# listen_addr: 0.0.0.0:8080 -# Path to WireGuard private key file -private_key_path: private.key +# Private key used encrypt the traffic between headscale +# and Tailscale clients. +# The private key file which will be +# autogenerated if it's missing +private_key_path: /var/lib/headscale/private.key +# DERP is a relay system that Tailscale uses when a direct +# connection cannot be established. +# https://tailscale.com/blog/how-tailscale-works/#encrypted-tcp-relays-derp +# +# headscale needs a list of DERP servers that can be presented +# to the clients. derp: # List of externally available DERP maps encoded in JSON urls: - https://controlplane.tailscale.com/derpmap/default # Locally available DERP map files encoded in YAML - paths: - - derp-example.yaml + # + # This option is mostly interesting for people hosting + # their own DERP servers: + # https://tailscale.com/kb/1118/custom-derp-servers/ + # + # paths: + # - /etc/headscale/derp-example.yaml + paths: [] # If enabled, a worker will be set up to periodically # refresh the given sources and update the derpmap # will be set up. auto_update_enabled: true - # How often should we check for updates? + # How often should we check for DERP updates? update_frequency: 24h -# Disables the automatic check for updates on startup +# Disables the automatic check for headscale updates on startup disable_check_updates: false + +# Time before an inactive ephemeral node is deleted? ephemeral_node_inactivity_timeout: 30m # SQLite config db_type: sqlite3 -db_path: db.sqlite +db_path: /var/lib/headscale/db.sqlite # # Postgres config # db_type: postgres @@ -42,25 +69,98 @@ db_path: db.sqlite # db_user: foo # db_pass: bar +### TLS configuration +# +## Let's encrypt / ACME +# +# headscale supports automatically requesting and setting up +# TLS for a domain with Let's Encrypt. +# +# URL to ACME directory acme_url: https://acme-v02.api.letsencrypt.org/directory + +# Email to register with ACME provider acme_email: "" +# Domain name to request a TLS certificate for: tls_letsencrypt_hostname: "" -tls_letsencrypt_listen: ":http" -tls_letsencrypt_cache_dir: ".cache" -tls_letsencrypt_challenge_type: HTTP-01 +# 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 +# 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 +# verification endpoint, and it will be listning on: +# :http = port 80 +tls_letsencrypt_listen: ":http" + +## Use already defined certificates: tls_cert_path: "" tls_key_path: "" +log_level: info + # Path to a file containg ACL policies. +# Recommended path: /etc/headscale/acl.hujson acl_policy_path: "" +## DNS +# +# headscale supports Tailscale's DNS configuration and MagicDNS. +# Please have a look to their KB to better understand the concepts: +# +# - https://tailscale.com/kb/1054/dns/ +# - https://tailscale.com/kb/1081/magicdns/ +# - https://tailscale.com/blog/2021-09-private-dns-with-magicdns/ +# dns_config: - # Upstream DNS servers + # List of DNS servers to expose to clients. nameservers: - 1.1.1.1 + + # Split DNS (see https://tailscale.com/kb/1054/dns/), + # list of search domains and the DNS to query for each one. + # + # restricted_nameservers: + # foo.bar.com: + # - 1.1.1.1 + # darp.headscale.net: + # - 1.1.1.1 + # - 8.8.8.8 + + # Search domains to inject. domains: [] + # Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/). + # Only works if there is at least a nameserver defined. magic_dns: true + + # Defines the base domain to create the hostnames for MagicDNS. + # `base_domain` must be a FQDNs, without the trailing dot. + # The FQDN of the hosts will be + # `hostname.namespace.base_domain` (e.g., _myhost.mynamespace.example.com_). base_domain: example.com + +# Unix socket used for the CLI to connect without authentication +# Note: for local development, you probably want to change this to: +# unix_socket: ./headscale.sock +unix_socket: /var/run/headscale.sock +unix_socket_permission: "0770" +# +# headscale supports experimental OpenID connect support, +# it is still being tested and might have some bugs, please +# help us test it. +# OpenID Connect +# oidc: +# issuer: "https://your-oidc.issuer.com/path" +# 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 diff --git a/db.go b/db.go index 42c5eee9..51363256 100644 --- a/db.go +++ b/db.go @@ -9,7 +9,10 @@ import ( "gorm.io/gorm/logger" ) -const dbVersion = "1" +const ( + dbVersion = "1" + errValueNotFound = Error("not found") +) // KV is a key-value store in a psql table. For future use... type KV struct { @@ -24,7 +27,7 @@ func (h *Headscale) initDB() error { } h.db = db - if h.dbType == "postgres" { + if h.dbType == Postgres { db.Exec("create extension if not exists \"uuid-ossp\";") } err = db.AutoMigrate(&Machine{}) @@ -50,6 +53,7 @@ func (h *Headscale) initDB() error { } err = h.setValue("db_version", dbVersion) + return err } @@ -65,12 +69,12 @@ func (h *Headscale) openDB() (*gorm.DB, error) { } switch h.dbType { - case "sqlite3": + case Sqlite: db, err = gorm.Open(sqlite.Open(h.dbString), &gorm.Config{ DisableForeignKeyConstraintWhenMigrating: true, Logger: log, }) - case "postgres": + case Postgres: db, err = gorm.Open(postgres.Open(h.dbString), &gorm.Config{ DisableForeignKeyConstraintWhenMigrating: true, Logger: log, @@ -84,28 +88,33 @@ func (h *Headscale) openDB() (*gorm.DB, error) { return db, nil } -// getValue returns the value for the given key in KV +// getValue returns the value for the given key in KV. func (h *Headscale) getValue(key string) (string, error) { var row KV - if result := h.db.First(&row, "key = ?", key); errors.Is(result.Error, gorm.ErrRecordNotFound) { - return "", errors.New("not found") + if result := h.db.First(&row, "key = ?", key); errors.Is( + result.Error, + gorm.ErrRecordNotFound, + ) { + return "", errValueNotFound } + return row.Value, nil } -// setValue sets value for the given key in KV +// setValue sets value for the given key in KV. func (h *Headscale) setValue(key string, value string) error { - kv := KV{ + keyValue := KV{ Key: key, Value: value, } - _, err := h.getValue(key) - if err == nil { - h.db.Model(&kv).Where("key = ?", key).Update("value", value) + if _, err := h.getValue(key); err == nil { + h.db.Model(&keyValue).Where("key = ?", key).Update("value", value) + return nil } - h.db.Create(kv) + h.db.Create(keyValue) + return nil } diff --git a/derp-example.yaml b/derp-example.yaml index bbf7cc8d..0ebe32ed 100644 --- a/derp-example.yaml +++ b/derp-example.yaml @@ -1,15 +1,15 @@ # If you plan to somehow use headscale, please deploy your own DERP infra: https://tailscale.com/kb/1118/custom-derp-servers/ -regions: +regions: 900: regionid: 900 regioncode: custom regionname: My Region nodes: - - name: 1a - regionid: 1 - hostname: myderp.mydomain.no - ipv4: 123.123.123.123 - ipv6: "2604:a880:400:d1::828:b001" - stunport: 0 - stunonly: false - derptestport: 0 + - name: 900a + regionid: 900 + hostname: myderp.mydomain.no + ipv4: 123.123.123.123 + ipv6: "2604:a880:400:d1::828:b001" + stunport: 0 + stunonly: false + derptestport: 0 diff --git a/derp.go b/derp.go index 39e63210..63e448db 100644 --- a/derp.go +++ b/derp.go @@ -1,6 +1,7 @@ package headscale import ( + "context" "encoding/json" "io" "io/ioutil" @@ -10,9 +11,7 @@ import ( "time" "github.com/rs/zerolog/log" - "gopkg.in/yaml.v2" - "tailscale.com/tailcfg" ) @@ -28,14 +27,24 @@ func loadDERPMapFromPath(path string) (*tailcfg.DERPMap, error) { return nil, err } err = yaml.Unmarshal(b, &derpMap) + return &derpMap, err } func loadDERPMapFromURL(addr url.URL) (*tailcfg.DERPMap, error) { - client := http.Client{ - Timeout: 10 * time.Second, + ctx, cancel := context.WithTimeout(context.Background(), HTTPReadTimeout) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, "GET", addr.String(), nil) + if err != nil { + return nil, err } - resp, err := client.Get(addr.String()) + + client := http.Client{ + Timeout: HTTPReadTimeout, + } + + resp, err := client.Do(req) if err != nil { return nil, err } @@ -48,6 +57,7 @@ func loadDERPMapFromURL(addr url.URL) (*tailcfg.DERPMap, error) { var derpMap tailcfg.DERPMap err = json.Unmarshal(body, &derpMap) + return &derpMap, err } @@ -55,7 +65,7 @@ func loadDERPMapFromURL(addr url.URL) (*tailcfg.DERPMap, error) { // DERPMap, it will _only_ look at the Regions, an integer. // If a region exists in two of the given DERPMaps, the region // form the _last_ DERPMap will be preserved. -// An empty DERPMap list will result in a DERPMap with no regions +// An empty DERPMap list will result in a DERPMap with no regions. func mergeDERPMaps(derpMaps []*tailcfg.DERPMap) *tailcfg.DERPMap { result := tailcfg.DERPMap{ OmitDefaultRegions: false, @@ -86,6 +96,7 @@ func GetDERPMap(cfg DERPConfig) *tailcfg.DERPMap { Str("path", path). Err(err). Msg("Could not load DERP map from path") + break } @@ -104,6 +115,7 @@ func GetDERPMap(cfg DERPConfig) *tailcfg.DERPMap { Str("url", addr.String()). Err(err). Msg("Could not load DERP map from path") + break } @@ -144,7 +156,7 @@ func (h *Headscale) scheduledDERPMapUpdateWorker(cancelChan <-chan struct{}) { Msg("Failed to fetch namespaces") } - for _, namespace := range *namespaces { + for _, namespace := range namespaces { h.setLastStateChangeToNow(namespace.Name) } } diff --git a/dns.go b/dns.go index c7ca32ac..af6f989d 100644 --- a/dns.go +++ b/dns.go @@ -10,6 +10,10 @@ import ( "tailscale.com/util/dnsname" ) +const ( + ByteSize = 8 +) + // generateMagicDNSRootDomains generates a list of DNS entries to be included in `Routes` in `MapResponse`. // This list of reverse DNS entries instructs the OS on what subnets and domains the Tailscale embedded DNS // server (listening in 100.100.100.100 udp/53) should be used for. @@ -30,7 +34,9 @@ import ( // From the netmask we can find out the wildcard bits (the bits that are not set in the netmask). // This allows us to then calculate the subnets included in the subsequent class block and generate the entries. -func generateMagicDNSRootDomains(ipPrefix netaddr.IPPrefix, baseDomain string) ([]dnsname.FQDN, error) { +func generateMagicDNSRootDomains( + ipPrefix netaddr.IPPrefix, +) []dnsname.FQDN { // TODO(juanfont): we are not handing out IPv6 addresses yet // and in fact this is Tailscale.com's range (note the fd7a:115c:a1e0: range in the fc00::/7 network) ipv6base := dnsname.FQDN("0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa.") @@ -41,15 +47,15 @@ func generateMagicDNSRootDomains(ipPrefix netaddr.IPPrefix, baseDomain string) ( maskBits, _ := netRange.Mask.Size() // lastOctet is the last IP byte covered by the mask - lastOctet := maskBits / 8 + lastOctet := maskBits / ByteSize // wildcardBits is the number of bits not under the mask in the lastOctet - wildcardBits := 8 - maskBits%8 + wildcardBits := ByteSize - maskBits%ByteSize // min is the value in the lastOctet byte of the IP // max is basically 2^wildcardBits - i.e., the value when all the wildcardBits are set to 1 min := uint(netRange.IP[lastOctet]) - max := uint((min + 1< - -2. (Optional, you can also use SQLite) Get yourself a PostgreSQL DB running - - ```shell - docker run --name headscale \ - -e POSTGRES_DB=headscale - -e POSTGRES_USER=foo \ - -e POSTGRES_PASSWORD=bar \ - -p 5432:5432 \ - -d postgres - ``` - -3. Create a WireGuard private key and headscale configuration - - ```shell - wg genkey > private.key - - cp config.yaml.example config.yaml - ``` - -4. Create a namespace - - ```shell - headscale namespaces create myfirstnamespace - ``` - - or docker: - - the db.sqlite mount is only needed if you use sqlite - - ```shell - touch db.sqlite - docker run \ - -v $(pwd)/private.key:/private.key \ - -v $(pwd)/config.json:/config.json \ - -v $(pwd)/derp.yaml:/derp.yaml \ - -v $(pwd)/db.sqlite:/db.sqlite \ - -p 127.0.0.1:8080:8080 \ - headscale/headscale:x.x.x \ - headscale namespaces create myfirstnamespace - ``` - - or if your server is already running in docker: - - ```shell - docker exec headscale create myfirstnamespace - ``` - -5. Run the server - - ```shell - headscale serve - ``` - - or docker: - - the db.sqlite mount is only needed if you use sqlite - - ```shell - docker run \ - -v $(pwd)/private.key:/private.key \ - -v $(pwd)/config.json:/config.json \ - -v $(pwd)/derp.yaml:/derp.yaml \ - -v $(pwd)/db.sqlite:/db.sqlite \ - -p 127.0.0.1:8080:8080 \ - headscale/headscale:x.x.x headscale serve - ``` - -6. If you used tailscale.com before in your nodes, make sure you clear the tailscaled data folder - - ```shell - systemctl stop tailscaled - rm -fr /var/lib/tailscale - systemctl start tailscaled - ``` - -7. Add your first machine - - ```shell - tailscale up --login-server YOUR_HEADSCALE_URL - ``` - -8. Navigate to the URL you will get with `tailscale up`, where you'll find your machine key. - -9. In the server, register your machine to a namespace with the CLI - ```shell - headscale -n myfirstnamespace nodes register YOURMACHINEKEY - ``` - or docker: - ```shell - docker run \ - -v $(pwd)/private.key:/private.key \ - -v $(pwd)/config.json:/config.json \ - -v $(pwd)/derp.yaml:/derp.yaml \ - headscale/headscale:x.x.x \ - headscale -n myfirstnamespace nodes register YOURMACHINEKEY - ``` - or if your server is already running in docker: - ```shell - docker exec headscale -n myfirstnamespace nodes register YOURMACHINEKEY - ``` - -Alternatively, you can use Auth Keys to register your machines: - -1. Create an authkey - - ```shell - headscale -n myfirstnamespace preauthkeys create --reusable --expiration 24h - ``` - - or docker: - - ```shell - docker run \ - -v $(pwd)/private.key:/private.key \ - -v $(pwd)/config.json:/config.json \ - -v$(pwd)/derp.yaml:/derp.yaml \ - -v $(pwd)/db.sqlite:/db.sqlite \ - headscale/headscale:x.x.x \ - headscale -n myfirstnamespace preauthkeys create --reusable --expiration 24h - ``` - - or if your server is already running in docker: - - ```shell - docker exec headscale -n myfirstnamespace preauthkeys create --reusable --expiration 24h - ``` - -2. Use the authkey from your machine to register it - ```shell - tailscale up --login-server YOUR_HEADSCALE_URL --authkey YOURAUTHKEY - ``` - -If you create an authkey with the `--ephemeral` flag, that key will create ephemeral nodes. This implies that `--reusable` is true. - -Please bear in mind that all headscale commands support adding `-o json` or `-o json-line` to get nicely JSON-formatted output. diff --git a/docs/examples/README.md b/docs/examples/README.md new file mode 100644 index 00000000..f9e85ff3 --- /dev/null +++ b/docs/examples/README.md @@ -0,0 +1,5 @@ +# Examples + +This directory contains examples on how to run `headscale` on different platforms. + +All examples are provided by the community and they are not verified by the `headscale` authors. diff --git a/k8s/.gitignore b/docs/examples/kustomize/.gitignore similarity index 100% rename from k8s/.gitignore rename to docs/examples/kustomize/.gitignore diff --git a/k8s/README.md b/docs/examples/kustomize/README.md similarity index 93% rename from k8s/README.md rename to docs/examples/kustomize/README.md index 45574b48..cc57f147 100644 --- a/k8s/README.md +++ b/docs/examples/kustomize/README.md @@ -1,5 +1,7 @@ # Deploying headscale on Kubernetes +**Note:** This is contributed by the community and not verified by the headscale authors. + This directory contains [Kustomize](https://kustomize.io) templates that deploy headscale in various configurations. @@ -24,6 +26,7 @@ Configure DERP servers by editing `base/site/derp.yaml` if needed. You'll somehow need to get `headscale:latest` into your cluster image registry. An easy way to do this with k3s: + - Reconfigure k3s to use docker instead of containerd (`k3s server --docker`) - `docker build -t headscale:latest ..` from here @@ -61,11 +64,11 @@ Use the wrapper script to remotely operate headscale to perform administrative tasks like creating namespaces, authkeys, etc. ``` -[c@nix-slate:~/Projects/headscale/k8s]$ ./headscale.bash +[c@nix-slate:~/Projects/headscale/k8s]$ ./headscale.bash headscale is an open source implementation of the Tailscale control server -https://gitlab.com/juanfont/headscale +https://github.com/juanfont/headscale Usage: headscale [command] diff --git a/k8s/base/configmap.yaml b/docs/examples/kustomize/base/configmap.yaml similarity index 100% rename from k8s/base/configmap.yaml rename to docs/examples/kustomize/base/configmap.yaml diff --git a/docs/examples/kustomize/base/ingress.yaml b/docs/examples/kustomize/base/ingress.yaml new file mode 100644 index 00000000..51da3427 --- /dev/null +++ b/docs/examples/kustomize/base/ingress.yaml @@ -0,0 +1,18 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: headscale + annotations: + kubernetes.io/ingress.class: traefik +spec: + rules: + - host: $(PUBLIC_HOSTNAME) + http: + paths: + - backend: + service: + name: headscale + port: + number: 8080 + path: / + pathType: Prefix diff --git a/docs/examples/kustomize/base/kustomization.yaml b/docs/examples/kustomize/base/kustomization.yaml new file mode 100644 index 00000000..93278f7d --- /dev/null +++ b/docs/examples/kustomize/base/kustomization.yaml @@ -0,0 +1,42 @@ +namespace: headscale +resources: + - configmap.yaml + - ingress.yaml + - service.yaml +generatorOptions: + disableNameSuffixHash: true +configMapGenerator: + - name: headscale-site + files: + - derp.yaml=site/derp.yaml + envs: + - site/public.env + - name: headscale-etc + literals: + - config.json={} +secretGenerator: + - name: headscale + files: + - secrets/private-key +vars: + - name: PUBLIC_PROTO + objRef: + kind: ConfigMap + name: headscale-site + apiVersion: v1 + fieldRef: + fieldPath: data.public-proto + - name: PUBLIC_HOSTNAME + objRef: + kind: ConfigMap + name: headscale-site + apiVersion: v1 + fieldRef: + fieldPath: data.public-hostname + - name: CONTACT_EMAIL + objRef: + kind: ConfigMap + name: headscale-site + apiVersion: v1 + fieldRef: + fieldPath: data.contact-email diff --git a/k8s/base/service.yaml b/docs/examples/kustomize/base/service.yaml similarity index 69% rename from k8s/base/service.yaml rename to docs/examples/kustomize/base/service.yaml index 7fdf738f..39e67253 100644 --- a/k8s/base/service.yaml +++ b/docs/examples/kustomize/base/service.yaml @@ -8,6 +8,6 @@ spec: selector: app: headscale ports: - - name: http - targetPort: http - port: 8080 + - name: http + targetPort: http + port: 8080 diff --git a/k8s/headscale.bash b/docs/examples/kustomize/headscale.bash similarity index 100% rename from k8s/headscale.bash rename to docs/examples/kustomize/headscale.bash diff --git a/k8s/init.bash b/docs/examples/kustomize/init.bash similarity index 100% rename from k8s/init.bash rename to docs/examples/kustomize/init.bash diff --git a/k8s/install-cert-manager.bash b/docs/examples/kustomize/install-cert-manager.bash similarity index 100% rename from k8s/install-cert-manager.bash rename to docs/examples/kustomize/install-cert-manager.bash diff --git a/docs/examples/kustomize/postgres/deployment.yaml b/docs/examples/kustomize/postgres/deployment.yaml new file mode 100644 index 00000000..75e64446 --- /dev/null +++ b/docs/examples/kustomize/postgres/deployment.yaml @@ -0,0 +1,76 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: headscale +spec: + replicas: 2 + selector: + matchLabels: + app: headscale + template: + metadata: + labels: + app: headscale + spec: + containers: + - name: headscale + image: "headscale:latest" + imagePullPolicy: IfNotPresent + command: ["/go/bin/headscale", "serve"] + env: + - name: SERVER_URL + value: $(PUBLIC_PROTO)://$(PUBLIC_HOSTNAME) + - name: LISTEN_ADDR + valueFrom: + configMapKeyRef: + name: headscale-config + key: listen_addr + - name: DERP_MAP_PATH + value: /vol/config/derp.yaml + - name: EPHEMERAL_NODE_INACTIVITY_TIMEOUT + valueFrom: + configMapKeyRef: + name: headscale-config + key: ephemeral_node_inactivity_timeout + - name: DB_TYPE + value: postgres + - name: DB_HOST + value: postgres.headscale.svc.cluster.local + - name: DB_PORT + value: "5432" + - name: DB_USER + value: headscale + - name: DB_PASS + valueFrom: + secretKeyRef: + name: postgresql + key: password + - name: DB_NAME + value: headscale + ports: + - name: http + protocol: TCP + containerPort: 8080 + livenessProbe: + tcpSocket: + port: http + initialDelaySeconds: 30 + timeoutSeconds: 5 + periodSeconds: 15 + volumeMounts: + - name: config + mountPath: /vol/config + - name: secret + mountPath: /vol/secret + - name: etc + mountPath: /etc/headscale + volumes: + - name: config + configMap: + name: headscale-site + - name: etc + configMap: + name: headscale-etc + - name: secret + secret: + secretName: headscale diff --git a/docs/examples/kustomize/postgres/kustomization.yaml b/docs/examples/kustomize/postgres/kustomization.yaml new file mode 100644 index 00000000..e732e3b9 --- /dev/null +++ b/docs/examples/kustomize/postgres/kustomization.yaml @@ -0,0 +1,13 @@ +namespace: headscale +bases: + - ../base +resources: + - deployment.yaml + - postgres-service.yaml + - postgres-statefulset.yaml +generatorOptions: + disableNameSuffixHash: true +secretGenerator: + - name: postgresql + files: + - secrets/password diff --git a/k8s/postgres/postgres-service.yaml b/docs/examples/kustomize/postgres/postgres-service.yaml similarity index 66% rename from k8s/postgres/postgres-service.yaml rename to docs/examples/kustomize/postgres/postgres-service.yaml index e2f486cc..6252e7f9 100644 --- a/k8s/postgres/postgres-service.yaml +++ b/docs/examples/kustomize/postgres/postgres-service.yaml @@ -8,6 +8,6 @@ spec: selector: app: postgres ports: - - name: postgres - targetPort: postgres - port: 5432 + - name: postgres + targetPort: postgres + port: 5432 diff --git a/docs/examples/kustomize/postgres/postgres-statefulset.yaml b/docs/examples/kustomize/postgres/postgres-statefulset.yaml new file mode 100644 index 00000000..b81c9bf0 --- /dev/null +++ b/docs/examples/kustomize/postgres/postgres-statefulset.yaml @@ -0,0 +1,49 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: postgres +spec: + serviceName: postgres + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: "postgres:13" + imagePullPolicy: IfNotPresent + env: + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: postgresql + key: password + - name: POSTGRES_USER + value: headscale + ports: + - name: postgres + protocol: TCP + containerPort: 5432 + livenessProbe: + tcpSocket: + port: 5432 + initialDelaySeconds: 30 + timeoutSeconds: 5 + periodSeconds: 15 + volumeMounts: + - name: pgdata + mountPath: /var/lib/postgresql/data + volumeClaimTemplates: + - metadata: + name: pgdata + spec: + storageClassName: local-path + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi diff --git a/k8s/production-tls/ingress-patch.yaml b/docs/examples/kustomize/production-tls/ingress-patch.yaml similarity index 70% rename from k8s/production-tls/ingress-patch.yaml rename to docs/examples/kustomize/production-tls/ingress-patch.yaml index 387c7364..9e6177fb 100644 --- a/k8s/production-tls/ingress-patch.yaml +++ b/docs/examples/kustomize/production-tls/ingress-patch.yaml @@ -6,6 +6,6 @@ metadata: traefik.ingress.kubernetes.io/router.tls: "true" spec: tls: - - hosts: - - $(PUBLIC_HOSTNAME) - secretName: production-cert + - hosts: + - $(PUBLIC_HOSTNAME) + secretName: production-cert diff --git a/docs/examples/kustomize/production-tls/kustomization.yaml b/docs/examples/kustomize/production-tls/kustomization.yaml new file mode 100644 index 00000000..d3147f5f --- /dev/null +++ b/docs/examples/kustomize/production-tls/kustomization.yaml @@ -0,0 +1,9 @@ +namespace: headscale +bases: + - ../base +resources: + - production-issuer.yaml +patches: + - path: ingress-patch.yaml + target: + kind: Ingress diff --git a/k8s/production-tls/production-issuer.yaml b/docs/examples/kustomize/production-tls/production-issuer.yaml similarity index 87% rename from k8s/production-tls/production-issuer.yaml rename to docs/examples/kustomize/production-tls/production-issuer.yaml index 7ae9131a..f436090b 100644 --- a/k8s/production-tls/production-issuer.yaml +++ b/docs/examples/kustomize/production-tls/production-issuer.yaml @@ -11,6 +11,6 @@ spec: # Secret resource used to store the account's private key. name: letsencrypt-production-acc-key solvers: - - http01: - ingress: - class: traefik + - http01: + ingress: + class: traefik diff --git a/k8s/sqlite/kustomization.yaml b/docs/examples/kustomize/sqlite/kustomization.yaml similarity index 54% rename from k8s/sqlite/kustomization.yaml rename to docs/examples/kustomize/sqlite/kustomization.yaml index 5be451ca..ca799419 100644 --- a/k8s/sqlite/kustomization.yaml +++ b/docs/examples/kustomize/sqlite/kustomization.yaml @@ -1,5 +1,5 @@ namespace: headscale bases: -- ../base + - ../base resources: -- statefulset.yaml + - statefulset.yaml diff --git a/docs/examples/kustomize/sqlite/statefulset.yaml b/docs/examples/kustomize/sqlite/statefulset.yaml new file mode 100644 index 00000000..050bf766 --- /dev/null +++ b/docs/examples/kustomize/sqlite/statefulset.yaml @@ -0,0 +1,77 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: headscale +spec: + serviceName: headscale + replicas: 1 + selector: + matchLabels: + app: headscale + template: + metadata: + labels: + app: headscale + spec: + containers: + - name: headscale + image: "headscale:latest" + imagePullPolicy: IfNotPresent + command: ["/go/bin/headscale", "serve"] + env: + - name: SERVER_URL + value: $(PUBLIC_PROTO)://$(PUBLIC_HOSTNAME) + - name: LISTEN_ADDR + valueFrom: + configMapKeyRef: + name: headscale-config + key: listen_addr + - name: DERP_MAP_PATH + value: /vol/config/derp.yaml + - name: EPHEMERAL_NODE_INACTIVITY_TIMEOUT + valueFrom: + configMapKeyRef: + name: headscale-config + key: ephemeral_node_inactivity_timeout + - name: DB_TYPE + value: sqlite3 + - name: DB_PATH + value: /vol/data/db.sqlite + ports: + - name: http + protocol: TCP + containerPort: 8080 + livenessProbe: + tcpSocket: + port: http + initialDelaySeconds: 30 + timeoutSeconds: 5 + periodSeconds: 15 + volumeMounts: + - name: config + mountPath: /vol/config + - name: data + mountPath: /vol/data + - name: secret + mountPath: /vol/secret + - name: etc + mountPath: /etc/headscale + volumes: + - name: config + configMap: + name: headscale-site + - name: etc + configMap: + name: headscale-etc + - name: secret + secret: + secretName: headscale + volumeClaimTemplates: + - metadata: + name: data + spec: + storageClassName: local-path + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi diff --git a/k8s/staging-tls/ingress-patch.yaml b/docs/examples/kustomize/staging-tls/ingress-patch.yaml similarity index 71% rename from k8s/staging-tls/ingress-patch.yaml rename to docs/examples/kustomize/staging-tls/ingress-patch.yaml index f97974bd..5a1daf0c 100644 --- a/k8s/staging-tls/ingress-patch.yaml +++ b/docs/examples/kustomize/staging-tls/ingress-patch.yaml @@ -6,6 +6,6 @@ metadata: traefik.ingress.kubernetes.io/router.tls: "true" spec: tls: - - hosts: - - $(PUBLIC_HOSTNAME) - secretName: staging-cert + - hosts: + - $(PUBLIC_HOSTNAME) + secretName: staging-cert diff --git a/docs/examples/kustomize/staging-tls/kustomization.yaml b/docs/examples/kustomize/staging-tls/kustomization.yaml new file mode 100644 index 00000000..0900c583 --- /dev/null +++ b/docs/examples/kustomize/staging-tls/kustomization.yaml @@ -0,0 +1,9 @@ +namespace: headscale +bases: + - ../base +resources: + - staging-issuer.yaml +patches: + - path: ingress-patch.yaml + target: + kind: Ingress diff --git a/k8s/staging-tls/staging-issuer.yaml b/docs/examples/kustomize/staging-tls/staging-issuer.yaml similarity index 87% rename from k8s/staging-tls/staging-issuer.yaml rename to docs/examples/kustomize/staging-tls/staging-issuer.yaml index 95325f6e..cf290415 100644 --- a/k8s/staging-tls/staging-issuer.yaml +++ b/docs/examples/kustomize/staging-tls/staging-issuer.yaml @@ -11,6 +11,6 @@ spec: # Secret resource used to store the account's private key. name: letsencrypt-staging-acc-key solvers: - - http01: - ingress: - class: traefik + - http01: + ingress: + class: traefik diff --git a/docs/Glossary.md b/docs/glossary.md similarity index 100% rename from docs/Glossary.md rename to docs/glossary.md diff --git a/docs/running-headscale-container.md b/docs/running-headscale-container.md new file mode 100644 index 00000000..d39f4d49 --- /dev/null +++ b/docs/running-headscale-container.md @@ -0,0 +1,148 @@ +# Running headscale in a container + +**Note:** the container documentation is maintained by the _community_ and there is no guarentee +it is up to date, or working. + +## Goal + +This documentation has the goal of showing a user how-to set up and run `headscale` in a container. +[Docker](https://www.docker.com) is used as the reference container implementation, but there is no reason that it should +not work with alternatives like [Podman](https://podman.io). The Docker image can be found on Docker Hub [here](https://hub.docker.com/r/headscale/headscale). + +## Configure and run `headscale` + +1. Prepare a directory on the host Docker node in your directory of choice, used to hold `headscale` configuration and the [SQLite](https://www.sqlite.org/) database: + +```shell +mkdir ./headscale && cd ./headscale +mkdir ./config +``` + +2. Create an empty SQlite datebase in the headscale directory: + +```shell +touch ./config/db.sqlite +``` + +3. **(Strongly Recommended)** Download a copy of the [example configuration](../config-example.yaml) from the [headscale repository](https://github.com/juanfont/headscale/). + +Using wget: + +```shell +wget -O ./config/config.yaml https://raw.githubusercontent.com/juanfont/headscale/main/config-example.yaml +``` + +Using curl: + +```shell +curl https://raw.githubusercontent.com/juanfont/headscale/main/config-example.yaml -o ./config/config.yaml +``` + +**(Advanced)** If you would like to hand craft a config file **instead** of downloading the example config file, create a blank `headscale` configuration in the headscale directory to edit: + +```shell +touch ./config/config.yaml +``` + +Modify the config file to your preferences before launching Docker container. + +4. Start the headscale server while working in the host headscale directory: + +```shell +docker run \ + --name headscale \ + --detach \ + --rm \ + --volume $(pwd)/config:/etc/headscale/ \ + --publish 127.0.0.1:8080:8080 \ + headscale/headscale: \ + headscale serve + +``` + +This command will mount `config/` under `/etc/headscale`, forward port 8080 out of the container so the +`headscale` instance becomes available and then detach so headscale runs in the background. + +5. Verify `headscale` is running: + +Follow the container logs: + +```shell +docker logs --follow headscale +``` + +Verify running containers: + +```shell +docker ps +``` + +Verify `headscale` is available: + +```shell +curl http://127.0.0.1:8080/metrics +``` + +6. Create a namespace ([tailnet](https://tailscale.com/kb/1136/tailnet/)): + +```shell +docker exec headscale -- headscale namespaces create myfirstnamespace +``` + +### Register a machine (normal login) + +On a client machine, execute the `tailscale` login command: + +```shell +tailscale up --login-server YOUR_HEADSCALE_URL +``` + +To register a machine when running `headscale` in a container, take the headscale command and pass it to the container: + +```shell +docker exec headscale -- \ + headscale --namespace myfirstnamespace nodes register --key +``` + +### Register machine using a pre authenticated key + +Generate a key using the command line: + +```shell +docker exec headscale -- \ + headscale --namespace myfirstnamespace preauthkeys create --reusable --expiration 24h +``` + +This will return a pre-authenticated key that can be used to connect a node to `headscale` during the `tailscale` command: + +```shell +tailscale up --login-server --authkey +``` + +## Debugging headscale running in Docker + +The `headscale/headscale` Docker container is based on a "distroless" image that does not contain a shell or any other debug tools. If you need to debug your application running in the Docker container, you can use the `-debug` variant, for example `headscale/headscale:x.x.x-debug`. + +### Running the debug Docker container + +To run the debug Docker container, use the exact same commands as above, but replace `headscale/headscale:x.x.x` with `headscale/headscale:x.x.x-debug` (`x.x.x` is the version of headscale). The two containers are compatible with each other, so you can alternate between them. + +### Executing commands in the debug container + +The default command in the debug container is to run `headscale`, which is located at `/bin/headscale` inside the container. + +Additionally, the debug container includes a minimalist Busybox shell. + +To launch a shell in the container, use: + +``` +docker run -it headscale/headscale:x.x.x-debug sh +``` + +You can also execute commands directly, such as `ls /bin` in this example: + +``` +docker run headscale/headscale:x.x.x-debug ls /bin +``` + +Using `docker exec` allows you to run commands in an existing container. diff --git a/docs/running-headscale-linux.md b/docs/running-headscale-linux.md new file mode 100644 index 00000000..7a62fcc6 --- /dev/null +++ b/docs/running-headscale-linux.md @@ -0,0 +1,172 @@ +# Running headscale on Linux + +## Goal + +This documentation has the goal of showing a user how-to set up and run `headscale` on Linux. +In additional to the "get up and running section", there is an optional [SystemD section](#running-headscale-in-the-background-with-systemd) +describing how to make `headscale` run properly in a server environment. + +## Configure and run `headscale` + +1. Download the latest [`headscale` binary from GitHub's release page](https://github.com/juanfont/headscale/releases): + +```shell +wget --output-document=/usr/local/bin/headscale \ + https://github.com/juanfont/headscale/releases/download/v/headscale__linux_ +``` + +2. Make `headscale` executable: + +```shell +chmod +x /usr/local/bin/headscale +``` + +3. Prepare a directory to hold `headscale` configuration and the [SQLite](https://www.sqlite.org/) database: + +```shell +# Directory for configuration + +mkdir -p /etc/headscale + +# Directory for Database, and other variable data (like certificates) +mkdir -p /var/lib/headscale +``` + +4. Create an empty SQLite database: + +```shell +touch /var/lib/headscale/db.sqlite +``` + +5. Create a `headscale` configuration: + +```shell +touch /etc/headscale/config.yaml +``` + +It is **strongly recommended** to copy and modifiy the [example configuration](../config-example.yaml) +from the [headscale repository](../) + +6. Start the headscale server: + +```shell + headscale serve +``` + +This command will start `headscale` in the current terminal session. + +--- + +To continue the tutorial, open a new terminal and let it run in the background. +Alternatively use terminal emulators like [tmux](https://github.com/tmux/tmux) or [screen](https://www.gnu.org/software/screen/). + +To run `headscale` in the background, please follow the steps in the [SystemD section](#running-headscale-in-the-background-with-systemd) before continuing. + +7. Verify `headscale` is running: + +Verify `headscale` is available: + +```shell +curl http://127.0.0.1:8080/metrics +``` + +8. Create a namespace ([tailnet](https://tailscale.com/kb/1136/tailnet/)): + +```shell +headscale namespaces create myfirstnamespace +``` + +### Register a machine (normal login) + +On a client machine, execute the `tailscale` login command: + +```shell +tailscale up --login-server YOUR_HEADSCALE_URL +``` + +Register the machine: + +```shell +headscale --namespace myfirstnamespace nodes register --key +``` + +### Register machine using a pre authenticated key + +Generate a key using the command line: + +```shell +headscale --namespace myfirstnamespace preauthkeys create --reusable --expiration 24h +``` + +This will return a pre-authenticated key that can be used to connect a node to `headscale` during the `tailscale` command: + +```shell +tailscale up --login-server --authkey +``` + +## Running `headscale` in the background with SystemD + +This section demonstrates how to run `headscale` as a service in the background with [SystemD](https://www.freedesktop.org/wiki/Software/systemd/). +This should work on most modern Linux distributions. + +1. Create a SystemD service configuration at `/etc/systemd/system/headscale.service` containing: + +```systemd +[Unit] +Description=headscale controller +After=syslog.target +After=network.target + +[Service] +Type=simple +User=headscale +Group=headscale +ExecStart=/usr/local/bin/headscale serve +Restart=always +RestartSec=5 + +# Optional security enhancements +NoNewPrivileges=yes +PrivateTmp=yes +ProtectSystem=strict +ProtectHome=yes +ReadWritePaths=/var/lib/headscale /var/run/headscale +AmbientCapabilities=CAP_NET_BIND_SERVICE +RuntimeDirectory=headscale + +[Install] +WantedBy=multi-user.target +``` + +2. In `/etc/headscale/config.yaml`, override the default `headscale` unix socket with a SystemD friendly path: + +```yaml +unix_socket: /var/run/headscale/headscale.sock +``` + +3. Reload SystemD to load the new configuration file: + +```shell +systemctl daemon-reload +``` + +4. Enable and start the new `headscale` service: + +```shell +systemctl enable headscale +systemctl start headscale +``` + +5. Verify the headscale service: + +```shell +systemctl status headscale +``` + +Verify `headscale` is available: + +```shell +curl http://127.0.0.1:8080/metrics +``` + +`headscale` will now run in the background and start at boot. diff --git a/docs/TLS.md b/docs/tls.md similarity index 95% rename from docs/TLS.md rename to docs/tls.md index 47de1cd7..557cdf01 100644 --- a/docs/TLS.md +++ b/docs/tls.md @@ -1,5 +1,9 @@ # Running the service via TLS (optional) +## Let's Encrypt / ACME + +To get a certificate automatically via [Let's Encrypt](https://letsencrypt.org/), set `tls_letsencrypt_hostname` to the desired certificate hostname. This name must resolve to the IP address(es) headscale is reachable on (i.e., it must correspond to the `server_url` configuration parameter). The certificate and Let's Encrypt account credentials will be stored in the directory configured in `tls_letsencrypt_cache_dir`. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from. The certificate will automatically be renewed as needed. + ```yaml tls_letsencrypt_hostname: "" tls_letsencrypt_listen: ":http" @@ -7,21 +11,21 @@ tls_letsencrypt_cache_dir: ".cache" tls_letsencrypt_challenge_type: HTTP-01 ``` -To get a certificate automatically via [Let's Encrypt](https://letsencrypt.org/), set `tls_letsencrypt_hostname` to the desired certificate hostname. This name must resolve to the IP address(es) headscale is reachable on (i.e., it must correspond to the `server_url` configuration parameter). The certificate and Let's Encrypt account credentials will be stored in the directory configured in `tls_letsencrypt_cache_dir`. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from. The certificate will automatically be renewed as needed. - -```yaml -tls_cert_path: "" -tls_key_path: "" -``` - -headscale can also be configured to expose its web service via TLS. To configure the certificate and key file manually, set the `tls_cert_path` and `tls_cert_path` configuration parameters. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from. - -## Challenge type HTTP-01 +### Challenge type HTTP-01 The default challenge type `HTTP-01` requires that headscale is reachable on port 80 for the Let's Encrypt automated validation, in addition to whatever port is configured in `listen_addr`. By default, headscale listens on port 80 on all local IPs for Let's Encrypt automated validation. If you need to change the ip and/or port used by headscale for the Let's Encrypt validation process, set `tls_letsencrypt_listen` to the appropriate value. This can be handy if you are running headscale as a non-root user (or can't run `setcap`). Keep in mind, however, that Let's Encrypt will _only_ connect to port 80 for the validation callback, so if you change `tls_letsencrypt_listen` you will also need to configure something else (e.g. a firewall rule) to forward the traffic from port 80 to the ip:port combination specified in `tls_letsencrypt_listen`. -## Challenge type TLS-ALPN-01 +### Challenge type TLS-ALPN-01 Alternatively, `tls_letsencrypt_challenge_type` can be set to `TLS-ALPN-01`. In this configuration, headscale listens on the ip:port combination defined in `listen_addr`. Let's Encrypt will _only_ connect to port 443 for the validation callback, so if `listen_addr` is not set to port 443, something else (e.g. a firewall rule) will be required to forward the traffic from port 443 to the ip:port combination specified in `listen_addr`. + +## Bring your own certificate + +headscale can also be configured to expose its web service via TLS. To configure the certificate and key file manually, set the `tls_cert_path` and `tls_cert_path` configuration parameters. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from. + +```yaml +tls_cert_path: "" +tls_key_path: "" +``` diff --git a/gen/go/headscale/v1/device.pb.go b/gen/go/headscale/v1/device.pb.go new file mode 100644 index 00000000..302c2653 --- /dev/null +++ b/gen/go/headscale/v1/device.pb.go @@ -0,0 +1,1115 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: headscale/v1/device.proto + +package v1 + +import ( + 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 ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Latency struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + LatencyMs float32 `protobuf:"fixed32,1,opt,name=latency_ms,json=latencyMs,proto3" json:"latency_ms,omitempty"` + Preferred bool `protobuf:"varint,2,opt,name=preferred,proto3" json:"preferred,omitempty"` +} + +func (x *Latency) Reset() { + *x = Latency{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_device_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Latency) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Latency) ProtoMessage() {} + +func (x *Latency) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_device_proto_msgTypes[0] + 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 Latency.ProtoReflect.Descriptor instead. +func (*Latency) Descriptor() ([]byte, []int) { + return file_headscale_v1_device_proto_rawDescGZIP(), []int{0} +} + +func (x *Latency) GetLatencyMs() float32 { + if x != nil { + return x.LatencyMs + } + return 0 +} + +func (x *Latency) GetPreferred() bool { + if x != nil { + return x.Preferred + } + return false +} + +type ClientSupports struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + HairPinning bool `protobuf:"varint,1,opt,name=hair_pinning,json=hairPinning,proto3" json:"hair_pinning,omitempty"` + Ipv6 bool `protobuf:"varint,2,opt,name=ipv6,proto3" json:"ipv6,omitempty"` + Pcp bool `protobuf:"varint,3,opt,name=pcp,proto3" json:"pcp,omitempty"` + Pmp bool `protobuf:"varint,4,opt,name=pmp,proto3" json:"pmp,omitempty"` + Udp bool `protobuf:"varint,5,opt,name=udp,proto3" json:"udp,omitempty"` + Upnp bool `protobuf:"varint,6,opt,name=upnp,proto3" json:"upnp,omitempty"` +} + +func (x *ClientSupports) Reset() { + *x = ClientSupports{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_device_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClientSupports) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClientSupports) ProtoMessage() {} + +func (x *ClientSupports) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_device_proto_msgTypes[1] + 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 ClientSupports.ProtoReflect.Descriptor instead. +func (*ClientSupports) Descriptor() ([]byte, []int) { + return file_headscale_v1_device_proto_rawDescGZIP(), []int{1} +} + +func (x *ClientSupports) GetHairPinning() bool { + if x != nil { + return x.HairPinning + } + return false +} + +func (x *ClientSupports) GetIpv6() bool { + if x != nil { + return x.Ipv6 + } + return false +} + +func (x *ClientSupports) GetPcp() bool { + if x != nil { + return x.Pcp + } + return false +} + +func (x *ClientSupports) GetPmp() bool { + if x != nil { + return x.Pmp + } + return false +} + +func (x *ClientSupports) GetUdp() bool { + if x != nil { + return x.Udp + } + return false +} + +func (x *ClientSupports) GetUpnp() bool { + if x != nil { + return x.Upnp + } + return false +} + +type ClientConnectivity struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Endpoints []string `protobuf:"bytes,1,rep,name=endpoints,proto3" json:"endpoints,omitempty"` + Derp string `protobuf:"bytes,2,opt,name=derp,proto3" json:"derp,omitempty"` + MappingVariesByDestIp bool `protobuf:"varint,3,opt,name=mapping_varies_by_dest_ip,json=mappingVariesByDestIp,proto3" json:"mapping_varies_by_dest_ip,omitempty"` + Latency map[string]*Latency `protobuf:"bytes,4,rep,name=latency,proto3" json:"latency,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ClientSupports *ClientSupports `protobuf:"bytes,5,opt,name=client_supports,json=clientSupports,proto3" json:"client_supports,omitempty"` +} + +func (x *ClientConnectivity) Reset() { + *x = ClientConnectivity{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_device_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClientConnectivity) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClientConnectivity) ProtoMessage() {} + +func (x *ClientConnectivity) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_device_proto_msgTypes[2] + 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 ClientConnectivity.ProtoReflect.Descriptor instead. +func (*ClientConnectivity) Descriptor() ([]byte, []int) { + return file_headscale_v1_device_proto_rawDescGZIP(), []int{2} +} + +func (x *ClientConnectivity) GetEndpoints() []string { + if x != nil { + return x.Endpoints + } + return nil +} + +func (x *ClientConnectivity) GetDerp() string { + if x != nil { + return x.Derp + } + return "" +} + +func (x *ClientConnectivity) GetMappingVariesByDestIp() bool { + if x != nil { + return x.MappingVariesByDestIp + } + return false +} + +func (x *ClientConnectivity) GetLatency() map[string]*Latency { + if x != nil { + return x.Latency + } + return nil +} + +func (x *ClientConnectivity) GetClientSupports() *ClientSupports { + if x != nil { + return x.ClientSupports + } + return nil +} + +type GetDeviceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *GetDeviceRequest) Reset() { + *x = GetDeviceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_device_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetDeviceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetDeviceRequest) ProtoMessage() {} + +func (x *GetDeviceRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_device_proto_msgTypes[3] + 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 GetDeviceRequest.ProtoReflect.Descriptor instead. +func (*GetDeviceRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_device_proto_rawDescGZIP(), []int{3} +} + +func (x *GetDeviceRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type GetDeviceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Addresses []string `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + User string `protobuf:"bytes,3,opt,name=user,proto3" json:"user,omitempty"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` + Hostname string `protobuf:"bytes,5,opt,name=hostname,proto3" json:"hostname,omitempty"` + ClientVersion string `protobuf:"bytes,6,opt,name=client_version,json=clientVersion,proto3" json:"client_version,omitempty"` + UpdateAvailable bool `protobuf:"varint,7,opt,name=update_available,json=updateAvailable,proto3" json:"update_available,omitempty"` + Os string `protobuf:"bytes,8,opt,name=os,proto3" json:"os,omitempty"` + Created *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=created,proto3" json:"created,omitempty"` + LastSeen *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` + KeyExpiryDisabled bool `protobuf:"varint,11,opt,name=key_expiry_disabled,json=keyExpiryDisabled,proto3" json:"key_expiry_disabled,omitempty"` + Expires *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=expires,proto3" json:"expires,omitempty"` + Authorized bool `protobuf:"varint,13,opt,name=authorized,proto3" json:"authorized,omitempty"` + IsExternal bool `protobuf:"varint,14,opt,name=is_external,json=isExternal,proto3" json:"is_external,omitempty"` + MachineKey string `protobuf:"bytes,15,opt,name=machine_key,json=machineKey,proto3" json:"machine_key,omitempty"` + NodeKey string `protobuf:"bytes,16,opt,name=node_key,json=nodeKey,proto3" json:"node_key,omitempty"` + BlocksIncomingConnections bool `protobuf:"varint,17,opt,name=blocks_incoming_connections,json=blocksIncomingConnections,proto3" json:"blocks_incoming_connections,omitempty"` + EnabledRoutes []string `protobuf:"bytes,18,rep,name=enabled_routes,json=enabledRoutes,proto3" json:"enabled_routes,omitempty"` + AdvertisedRoutes []string `protobuf:"bytes,19,rep,name=advertised_routes,json=advertisedRoutes,proto3" json:"advertised_routes,omitempty"` + ClientConnectivity *ClientConnectivity `protobuf:"bytes,20,opt,name=client_connectivity,json=clientConnectivity,proto3" json:"client_connectivity,omitempty"` +} + +func (x *GetDeviceResponse) Reset() { + *x = GetDeviceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_device_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetDeviceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetDeviceResponse) ProtoMessage() {} + +func (x *GetDeviceResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_device_proto_msgTypes[4] + 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 GetDeviceResponse.ProtoReflect.Descriptor instead. +func (*GetDeviceResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_device_proto_rawDescGZIP(), []int{4} +} + +func (x *GetDeviceResponse) GetAddresses() []string { + if x != nil { + return x.Addresses + } + return nil +} + +func (x *GetDeviceResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *GetDeviceResponse) GetUser() string { + if x != nil { + return x.User + } + return "" +} + +func (x *GetDeviceResponse) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *GetDeviceResponse) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +func (x *GetDeviceResponse) GetClientVersion() string { + if x != nil { + return x.ClientVersion + } + return "" +} + +func (x *GetDeviceResponse) GetUpdateAvailable() bool { + if x != nil { + return x.UpdateAvailable + } + return false +} + +func (x *GetDeviceResponse) GetOs() string { + if x != nil { + return x.Os + } + return "" +} + +func (x *GetDeviceResponse) GetCreated() *timestamppb.Timestamp { + if x != nil { + return x.Created + } + return nil +} + +func (x *GetDeviceResponse) GetLastSeen() *timestamppb.Timestamp { + if x != nil { + return x.LastSeen + } + return nil +} + +func (x *GetDeviceResponse) GetKeyExpiryDisabled() bool { + if x != nil { + return x.KeyExpiryDisabled + } + return false +} + +func (x *GetDeviceResponse) GetExpires() *timestamppb.Timestamp { + if x != nil { + return x.Expires + } + return nil +} + +func (x *GetDeviceResponse) GetAuthorized() bool { + if x != nil { + return x.Authorized + } + return false +} + +func (x *GetDeviceResponse) GetIsExternal() bool { + if x != nil { + return x.IsExternal + } + return false +} + +func (x *GetDeviceResponse) GetMachineKey() string { + if x != nil { + return x.MachineKey + } + return "" +} + +func (x *GetDeviceResponse) GetNodeKey() string { + if x != nil { + return x.NodeKey + } + return "" +} + +func (x *GetDeviceResponse) GetBlocksIncomingConnections() bool { + if x != nil { + return x.BlocksIncomingConnections + } + return false +} + +func (x *GetDeviceResponse) GetEnabledRoutes() []string { + if x != nil { + return x.EnabledRoutes + } + return nil +} + +func (x *GetDeviceResponse) GetAdvertisedRoutes() []string { + if x != nil { + return x.AdvertisedRoutes + } + return nil +} + +func (x *GetDeviceResponse) GetClientConnectivity() *ClientConnectivity { + if x != nil { + return x.ClientConnectivity + } + return nil +} + +type DeleteDeviceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *DeleteDeviceRequest) Reset() { + *x = DeleteDeviceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_device_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteDeviceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteDeviceRequest) ProtoMessage() {} + +func (x *DeleteDeviceRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_device_proto_msgTypes[5] + 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 DeleteDeviceRequest.ProtoReflect.Descriptor instead. +func (*DeleteDeviceRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_device_proto_rawDescGZIP(), []int{5} +} + +func (x *DeleteDeviceRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type DeleteDeviceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DeleteDeviceResponse) Reset() { + *x = DeleteDeviceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_device_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteDeviceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteDeviceResponse) ProtoMessage() {} + +func (x *DeleteDeviceResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_device_proto_msgTypes[6] + 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 DeleteDeviceResponse.ProtoReflect.Descriptor instead. +func (*DeleteDeviceResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_device_proto_rawDescGZIP(), []int{6} +} + +type GetDeviceRoutesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *GetDeviceRoutesRequest) Reset() { + *x = GetDeviceRoutesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_device_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetDeviceRoutesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetDeviceRoutesRequest) ProtoMessage() {} + +func (x *GetDeviceRoutesRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_device_proto_msgTypes[7] + 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 GetDeviceRoutesRequest.ProtoReflect.Descriptor instead. +func (*GetDeviceRoutesRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_device_proto_rawDescGZIP(), []int{7} +} + +func (x *GetDeviceRoutesRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type GetDeviceRoutesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EnabledRoutes []string `protobuf:"bytes,1,rep,name=enabled_routes,json=enabledRoutes,proto3" json:"enabled_routes,omitempty"` + AdvertisedRoutes []string `protobuf:"bytes,2,rep,name=advertised_routes,json=advertisedRoutes,proto3" json:"advertised_routes,omitempty"` +} + +func (x *GetDeviceRoutesResponse) Reset() { + *x = GetDeviceRoutesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_device_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetDeviceRoutesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetDeviceRoutesResponse) ProtoMessage() {} + +func (x *GetDeviceRoutesResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_device_proto_msgTypes[8] + 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 GetDeviceRoutesResponse.ProtoReflect.Descriptor instead. +func (*GetDeviceRoutesResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_device_proto_rawDescGZIP(), []int{8} +} + +func (x *GetDeviceRoutesResponse) GetEnabledRoutes() []string { + if x != nil { + return x.EnabledRoutes + } + return nil +} + +func (x *GetDeviceRoutesResponse) GetAdvertisedRoutes() []string { + if x != nil { + return x.AdvertisedRoutes + } + return nil +} + +type EnableDeviceRoutesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Routes []string `protobuf:"bytes,2,rep,name=routes,proto3" json:"routes,omitempty"` +} + +func (x *EnableDeviceRoutesRequest) Reset() { + *x = EnableDeviceRoutesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_device_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EnableDeviceRoutesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EnableDeviceRoutesRequest) ProtoMessage() {} + +func (x *EnableDeviceRoutesRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_device_proto_msgTypes[9] + 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 EnableDeviceRoutesRequest.ProtoReflect.Descriptor instead. +func (*EnableDeviceRoutesRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_device_proto_rawDescGZIP(), []int{9} +} + +func (x *EnableDeviceRoutesRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *EnableDeviceRoutesRequest) GetRoutes() []string { + if x != nil { + return x.Routes + } + return nil +} + +type EnableDeviceRoutesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EnabledRoutes []string `protobuf:"bytes,1,rep,name=enabled_routes,json=enabledRoutes,proto3" json:"enabled_routes,omitempty"` + AdvertisedRoutes []string `protobuf:"bytes,2,rep,name=advertised_routes,json=advertisedRoutes,proto3" json:"advertised_routes,omitempty"` +} + +func (x *EnableDeviceRoutesResponse) Reset() { + *x = EnableDeviceRoutesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_device_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EnableDeviceRoutesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EnableDeviceRoutesResponse) ProtoMessage() {} + +func (x *EnableDeviceRoutesResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_device_proto_msgTypes[10] + 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 EnableDeviceRoutesResponse.ProtoReflect.Descriptor instead. +func (*EnableDeviceRoutesResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_device_proto_rawDescGZIP(), []int{10} +} + +func (x *EnableDeviceRoutesResponse) GetEnabledRoutes() []string { + if x != nil { + return x.EnabledRoutes + } + return nil +} + +func (x *EnableDeviceRoutesResponse) GetAdvertisedRoutes() []string { + if x != nil { + return x.AdvertisedRoutes + } + return nil +} + +var File_headscale_v1_device_proto protoreflect.FileDescriptor + +var file_headscale_v1_device_proto_rawDesc = []byte{ + 0x0a, 0x19, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x68, 0x65, 0x61, + 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x46, 0x0a, 0x07, 0x4c, 0x61, + 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, + 0x5f, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x02, 0x52, 0x09, 0x6c, 0x61, 0x74, 0x65, 0x6e, + 0x63, 0x79, 0x4d, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, + 0x65, 0x64, 0x22, 0x91, 0x01, 0x0a, 0x0e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x61, 0x69, 0x72, 0x5f, 0x70, 0x69, + 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x68, 0x61, 0x69, + 0x72, 0x50, 0x69, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x69, 0x70, 0x76, 0x36, 0x12, 0x10, 0x0a, 0x03, + 0x70, 0x63, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x70, 0x63, 0x70, 0x12, 0x10, + 0x0a, 0x03, 0x70, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x70, 0x6d, 0x70, + 0x12, 0x10, 0x0a, 0x03, 0x75, 0x64, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x75, + 0x64, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x70, 0x6e, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x04, 0x75, 0x70, 0x6e, 0x70, 0x22, 0xe3, 0x02, 0x0a, 0x12, 0x43, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x12, 0x1c, 0x0a, + 0x09, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x09, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, + 0x65, 0x72, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x72, 0x70, 0x12, + 0x38, 0x0a, 0x19, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x65, + 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x15, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x72, 0x69, 0x65, + 0x73, 0x42, 0x79, 0x44, 0x65, 0x73, 0x74, 0x49, 0x70, 0x12, 0x47, 0x0a, 0x07, 0x6c, 0x61, 0x74, + 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x68, 0x65, 0x61, + 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x2e, 0x4c, 0x61, 0x74, + 0x65, 0x6e, 0x63, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, + 0x63, 0x79, 0x12, 0x45, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x68, 0x65, + 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x0e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x1a, 0x51, 0x0a, 0x0c, 0x4c, 0x61, 0x74, + 0x65, 0x6e, 0x63, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, + 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, + 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x22, 0x0a, 0x10, + 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x22, 0xa0, 0x06, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x65, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x29, 0x0a, 0x10, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x73, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, 0x73, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 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, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 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, 0x2e, 0x0a, 0x13, 0x6b, 0x65, 0x79, + 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x6b, 0x65, 0x79, 0x45, 0x78, 0x70, 0x69, 0x72, + 0x79, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x65, 0x73, 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, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x12, + 0x1e, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x0d, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x12, + 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x0e, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x4b, 0x65, + 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x10, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x3e, 0x0a, 0x1b, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x5f, + 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x19, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, + 0x67, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0e, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x12, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, 0x65, + 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, + 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, + 0x12, 0x51, 0x0a, 0x13, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, + 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x52, + 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x76, + 0x69, 0x74, 0x79, 0x22, 0x25, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x16, 0x0a, 0x14, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x28, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x6d, 0x0a, 0x17, + 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2b, + 0x0a, 0x11, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x61, 0x64, 0x76, 0x65, 0x72, + 0x74, 0x69, 0x73, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x19, 0x45, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, + 0x22, 0x70, 0x0a, 0x1a, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, + 0x0a, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, + 0x73, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x10, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x73, 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_device_proto_rawDescOnce sync.Once + file_headscale_v1_device_proto_rawDescData = file_headscale_v1_device_proto_rawDesc +) + +func file_headscale_v1_device_proto_rawDescGZIP() []byte { + file_headscale_v1_device_proto_rawDescOnce.Do(func() { + file_headscale_v1_device_proto_rawDescData = protoimpl.X.CompressGZIP(file_headscale_v1_device_proto_rawDescData) + }) + return file_headscale_v1_device_proto_rawDescData +} + +var file_headscale_v1_device_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_headscale_v1_device_proto_goTypes = []interface{}{ + (*Latency)(nil), // 0: headscale.v1.Latency + (*ClientSupports)(nil), // 1: headscale.v1.ClientSupports + (*ClientConnectivity)(nil), // 2: headscale.v1.ClientConnectivity + (*GetDeviceRequest)(nil), // 3: headscale.v1.GetDeviceRequest + (*GetDeviceResponse)(nil), // 4: headscale.v1.GetDeviceResponse + (*DeleteDeviceRequest)(nil), // 5: headscale.v1.DeleteDeviceRequest + (*DeleteDeviceResponse)(nil), // 6: headscale.v1.DeleteDeviceResponse + (*GetDeviceRoutesRequest)(nil), // 7: headscale.v1.GetDeviceRoutesRequest + (*GetDeviceRoutesResponse)(nil), // 8: headscale.v1.GetDeviceRoutesResponse + (*EnableDeviceRoutesRequest)(nil), // 9: headscale.v1.EnableDeviceRoutesRequest + (*EnableDeviceRoutesResponse)(nil), // 10: headscale.v1.EnableDeviceRoutesResponse + nil, // 11: headscale.v1.ClientConnectivity.LatencyEntry + (*timestamppb.Timestamp)(nil), // 12: google.protobuf.Timestamp +} +var file_headscale_v1_device_proto_depIdxs = []int32{ + 11, // 0: headscale.v1.ClientConnectivity.latency:type_name -> headscale.v1.ClientConnectivity.LatencyEntry + 1, // 1: headscale.v1.ClientConnectivity.client_supports:type_name -> headscale.v1.ClientSupports + 12, // 2: headscale.v1.GetDeviceResponse.created:type_name -> google.protobuf.Timestamp + 12, // 3: headscale.v1.GetDeviceResponse.last_seen:type_name -> google.protobuf.Timestamp + 12, // 4: headscale.v1.GetDeviceResponse.expires:type_name -> google.protobuf.Timestamp + 2, // 5: headscale.v1.GetDeviceResponse.client_connectivity:type_name -> headscale.v1.ClientConnectivity + 0, // 6: headscale.v1.ClientConnectivity.LatencyEntry.value:type_name -> headscale.v1.Latency + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_headscale_v1_device_proto_init() } +func file_headscale_v1_device_proto_init() { + if File_headscale_v1_device_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_headscale_v1_device_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Latency); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_device_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClientSupports); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_device_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClientConnectivity); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_device_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetDeviceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_device_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetDeviceResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_device_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteDeviceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_device_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteDeviceResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_device_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetDeviceRoutesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_device_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetDeviceRoutesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_device_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EnableDeviceRoutesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_device_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EnableDeviceRoutesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_headscale_v1_device_proto_rawDesc, + NumEnums: 0, + NumMessages: 12, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_headscale_v1_device_proto_goTypes, + DependencyIndexes: file_headscale_v1_device_proto_depIdxs, + MessageInfos: file_headscale_v1_device_proto_msgTypes, + }.Build() + File_headscale_v1_device_proto = out.File + file_headscale_v1_device_proto_rawDesc = nil + file_headscale_v1_device_proto_goTypes = nil + file_headscale_v1_device_proto_depIdxs = nil +} diff --git a/gen/go/headscale/v1/headscale.pb.go b/gen/go/headscale/v1/headscale.pb.go new file mode 100644 index 00000000..dd27265d --- /dev/null +++ b/gen/go/headscale/v1/headscale.pb.go @@ -0,0 +1,303 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: headscale/v1/headscale.proto + +package v1 + +import ( + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +var File_headscale_v1_headscale_proto protoreflect.FileDescriptor + +var file_headscale_v1_headscale_proto_rawDesc = []byte{ + 0x0a, 0x1c, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x68, + 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, + 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x1c, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x68, 0x65, 0x61, 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, 0x1a, 0x1a, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, + 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x70, 0x72, + 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, 0x32, 0xf4, + 0x12, 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, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, + 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x7c, 0x0a, 0x0f, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, + 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x96, 0x01, 0x0a, 0x0f, 0x52, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x24, + 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x6e, 0x61, 0x6d, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 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, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6f, 0x6c, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x7d, 0x2f, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x2f, 0x7b, 0x6e, 0x65, 0x77, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x7d, 0x12, 0x80, 0x01, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, + 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, + 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x2a, 0x18, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, + 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x76, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, + 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x80, + 0x01, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, + 0x4b, 0x65, 0x79, 0x12, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x68, 0x65, 0x61, + 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x65, 0x61, 0x75, 0x74, 0x68, 0x6b, 0x65, 0x79, 0x3a, 0x01, + 0x2a, 0x12, 0x87, 0x01, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x50, 0x72, 0x65, 0x41, + 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, + 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x50, 0x72, 0x65, 0x41, + 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, + 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, + 0x69, 0x72, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x65, 0x61, 0x75, 0x74, 0x68, 0x6b, 0x65, + 0x79, 0x2f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x7a, 0x0a, 0x0f, 0x4c, + 0x69, 0x73, 0x74, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x24, + 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, + 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x65, + 0x61, 0x75, 0x74, 0x68, 0x6b, 0x65, 0x79, 0x12, 0x89, 0x01, 0x0a, 0x12, 0x44, 0x65, 0x62, 0x75, + 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x27, + 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 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, 0x1a, 0x28, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, + 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 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, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x76, 0x31, 0x2f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, + 0x3a, 0x01, 0x2a, 0x12, 0x75, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, + 0x65, 0x12, 0x1f, 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, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x20, 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, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 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, 0x12, 0x80, 0x01, 0x0a, 0x0f, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x24, + 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, + 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1a, 0x22, 0x18, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, + 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x7e, 0x0a, + 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x22, + 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x2a, + 0x1c, 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, 0x12, 0x85, 0x01, + 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, + 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, + 0x78, 0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, + 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, 0x65, + 0x78, 0x70, 0x69, 0x72, 0x65, 0x12, 0x6e, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, + 0x68, 0x69, 0x6e, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x2e, 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, 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{}{ + (*GetNamespaceRequest)(nil), // 0: headscale.v1.GetNamespaceRequest + (*CreateNamespaceRequest)(nil), // 1: headscale.v1.CreateNamespaceRequest + (*RenameNamespaceRequest)(nil), // 2: headscale.v1.RenameNamespaceRequest + (*DeleteNamespaceRequest)(nil), // 3: headscale.v1.DeleteNamespaceRequest + (*ListNamespacesRequest)(nil), // 4: headscale.v1.ListNamespacesRequest + (*CreatePreAuthKeyRequest)(nil), // 5: headscale.v1.CreatePreAuthKeyRequest + (*ExpirePreAuthKeyRequest)(nil), // 6: headscale.v1.ExpirePreAuthKeyRequest + (*ListPreAuthKeysRequest)(nil), // 7: headscale.v1.ListPreAuthKeysRequest + (*DebugCreateMachineRequest)(nil), // 8: headscale.v1.DebugCreateMachineRequest + (*GetMachineRequest)(nil), // 9: headscale.v1.GetMachineRequest + (*RegisterMachineRequest)(nil), // 10: headscale.v1.RegisterMachineRequest + (*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 + (*GetNamespaceResponse)(nil), // 18: headscale.v1.GetNamespaceResponse + (*CreateNamespaceResponse)(nil), // 19: headscale.v1.CreateNamespaceResponse + (*RenameNamespaceResponse)(nil), // 20: headscale.v1.RenameNamespaceResponse + (*DeleteNamespaceResponse)(nil), // 21: headscale.v1.DeleteNamespaceResponse + (*ListNamespacesResponse)(nil), // 22: headscale.v1.ListNamespacesResponse + (*CreatePreAuthKeyResponse)(nil), // 23: headscale.v1.CreatePreAuthKeyResponse + (*ExpirePreAuthKeyResponse)(nil), // 24: headscale.v1.ExpirePreAuthKeyResponse + (*ListPreAuthKeysResponse)(nil), // 25: headscale.v1.ListPreAuthKeysResponse + (*DebugCreateMachineResponse)(nil), // 26: headscale.v1.DebugCreateMachineResponse + (*GetMachineResponse)(nil), // 27: headscale.v1.GetMachineResponse + (*RegisterMachineResponse)(nil), // 28: headscale.v1.RegisterMachineResponse + (*DeleteMachineResponse)(nil), // 29: headscale.v1.DeleteMachineResponse + (*ExpireMachineResponse)(nil), // 30: headscale.v1.ExpireMachineResponse + (*ListMachinesResponse)(nil), // 31: headscale.v1.ListMachinesResponse + (*ShareMachineResponse)(nil), // 32: headscale.v1.ShareMachineResponse + (*UnshareMachineResponse)(nil), // 33: headscale.v1.UnshareMachineResponse + (*GetMachineRouteResponse)(nil), // 34: headscale.v1.GetMachineRouteResponse + (*EnableMachineRoutesResponse)(nil), // 35: headscale.v1.EnableMachineRoutesResponse +} +var file_headscale_v1_headscale_proto_depIdxs = []int32{ + 0, // 0: headscale.v1.HeadscaleService.GetNamespace:input_type -> headscale.v1.GetNamespaceRequest + 1, // 1: headscale.v1.HeadscaleService.CreateNamespace:input_type -> headscale.v1.CreateNamespaceRequest + 2, // 2: headscale.v1.HeadscaleService.RenameNamespace:input_type -> headscale.v1.RenameNamespaceRequest + 3, // 3: headscale.v1.HeadscaleService.DeleteNamespace:input_type -> headscale.v1.DeleteNamespaceRequest + 4, // 4: headscale.v1.HeadscaleService.ListNamespaces:input_type -> headscale.v1.ListNamespacesRequest + 5, // 5: headscale.v1.HeadscaleService.CreatePreAuthKey:input_type -> headscale.v1.CreatePreAuthKeyRequest + 6, // 6: headscale.v1.HeadscaleService.ExpirePreAuthKey:input_type -> headscale.v1.ExpirePreAuthKeyRequest + 7, // 7: headscale.v1.HeadscaleService.ListPreAuthKeys:input_type -> headscale.v1.ListPreAuthKeysRequest + 8, // 8: headscale.v1.HeadscaleService.DebugCreateMachine:input_type -> headscale.v1.DebugCreateMachineRequest + 9, // 9: headscale.v1.HeadscaleService.GetMachine:input_type -> headscale.v1.GetMachineRequest + 10, // 10: headscale.v1.HeadscaleService.RegisterMachine:input_type -> headscale.v1.RegisterMachineRequest + 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.GetNamespace:output_type -> headscale.v1.GetNamespaceResponse + 19, // 19: headscale.v1.HeadscaleService.CreateNamespace:output_type -> headscale.v1.CreateNamespaceResponse + 20, // 20: headscale.v1.HeadscaleService.RenameNamespace:output_type -> headscale.v1.RenameNamespaceResponse + 21, // 21: headscale.v1.HeadscaleService.DeleteNamespace:output_type -> headscale.v1.DeleteNamespaceResponse + 22, // 22: headscale.v1.HeadscaleService.ListNamespaces:output_type -> headscale.v1.ListNamespacesResponse + 23, // 23: headscale.v1.HeadscaleService.CreatePreAuthKey:output_type -> headscale.v1.CreatePreAuthKeyResponse + 24, // 24: headscale.v1.HeadscaleService.ExpirePreAuthKey:output_type -> headscale.v1.ExpirePreAuthKeyResponse + 25, // 25: headscale.v1.HeadscaleService.ListPreAuthKeys:output_type -> headscale.v1.ListPreAuthKeysResponse + 26, // 26: headscale.v1.HeadscaleService.DebugCreateMachine:output_type -> headscale.v1.DebugCreateMachineResponse + 27, // 27: headscale.v1.HeadscaleService.GetMachine:output_type -> headscale.v1.GetMachineResponse + 28, // 28: headscale.v1.HeadscaleService.RegisterMachine:output_type -> headscale.v1.RegisterMachineResponse + 29, // 29: headscale.v1.HeadscaleService.DeleteMachine:output_type -> headscale.v1.DeleteMachineResponse + 30, // 30: headscale.v1.HeadscaleService.ExpireMachine:output_type -> headscale.v1.ExpireMachineResponse + 31, // 31: headscale.v1.HeadscaleService.ListMachines:output_type -> headscale.v1.ListMachinesResponse + 32, // 32: headscale.v1.HeadscaleService.ShareMachine:output_type -> headscale.v1.ShareMachineResponse + 33, // 33: headscale.v1.HeadscaleService.UnshareMachine:output_type -> headscale.v1.UnshareMachineResponse + 34, // 34: headscale.v1.HeadscaleService.GetMachineRoute:output_type -> headscale.v1.GetMachineRouteResponse + 35, // 35: headscale.v1.HeadscaleService.EnableMachineRoutes:output_type -> headscale.v1.EnableMachineRoutesResponse + 18, // [18:36] is the sub-list for method output_type + 0, // [0:18] 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 +} + +func init() { file_headscale_v1_headscale_proto_init() } +func file_headscale_v1_headscale_proto_init() { + if File_headscale_v1_headscale_proto != nil { + return + } + file_headscale_v1_namespace_proto_init() + file_headscale_v1_preauthkey_proto_init() + file_headscale_v1_machine_proto_init() + file_headscale_v1_routes_proto_init() + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_headscale_v1_headscale_proto_rawDesc, + NumEnums: 0, + NumMessages: 0, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_headscale_v1_headscale_proto_goTypes, + DependencyIndexes: file_headscale_v1_headscale_proto_depIdxs, + }.Build() + File_headscale_v1_headscale_proto = out.File + file_headscale_v1_headscale_proto_rawDesc = nil + file_headscale_v1_headscale_proto_goTypes = nil + file_headscale_v1_headscale_proto_depIdxs = nil +} diff --git a/gen/go/headscale/v1/headscale.pb.gw.go b/gen/go/headscale/v1/headscale.pb.gw.go new file mode 100644 index 00000000..41502c8f --- /dev/null +++ b/gen/go/headscale/v1/headscale.pb.gw.go @@ -0,0 +1,1792 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: headscale/v1/headscale.proto + +/* +Package v1 is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package v1 + +import ( + "context" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = metadata.Join + +func request_HeadscaleService_GetNamespace_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetNamespaceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + + msg, err := client.GetNamespace(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_GetNamespace_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetNamespaceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + + msg, err := server.GetNamespace(ctx, &protoReq) + return msg, metadata, err + +} + +func request_HeadscaleService_CreateNamespace_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreateNamespaceRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.CreateNamespace(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_CreateNamespace_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreateNamespaceRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.CreateNamespace(ctx, &protoReq) + return msg, metadata, err + +} + +func request_HeadscaleService_RenameNamespace_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq RenameNamespaceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["old_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "old_name") + } + + protoReq.OldName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "old_name", err) + } + + val, ok = pathParams["new_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "new_name") + } + + protoReq.NewName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "new_name", err) + } + + msg, err := client.RenameNamespace(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_RenameNamespace_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq RenameNamespaceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["old_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "old_name") + } + + protoReq.OldName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "old_name", err) + } + + val, ok = pathParams["new_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "new_name") + } + + protoReq.NewName, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "new_name", err) + } + + msg, err := server.RenameNamespace(ctx, &protoReq) + return msg, metadata, err + +} + +func request_HeadscaleService_DeleteNamespace_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteNamespaceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + + msg, err := client.DeleteNamespace(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_DeleteNamespace_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteNamespaceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + + msg, err := server.DeleteNamespace(ctx, &protoReq) + return msg, metadata, err + +} + +func request_HeadscaleService_ListNamespaces_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListNamespacesRequest + var metadata runtime.ServerMetadata + + msg, err := client.ListNamespaces(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_ListNamespaces_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListNamespacesRequest + var metadata runtime.ServerMetadata + + msg, err := server.ListNamespaces(ctx, &protoReq) + return msg, metadata, err + +} + +func request_HeadscaleService_CreatePreAuthKey_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreatePreAuthKeyRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.CreatePreAuthKey(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_CreatePreAuthKey_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreatePreAuthKeyRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.CreatePreAuthKey(ctx, &protoReq) + return msg, metadata, err + +} + +func request_HeadscaleService_ExpirePreAuthKey_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ExpirePreAuthKeyRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ExpirePreAuthKey(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_ExpirePreAuthKey_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ExpirePreAuthKeyRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ExpirePreAuthKey(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_HeadscaleService_ListPreAuthKeys_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_HeadscaleService_ListPreAuthKeys_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListPreAuthKeysRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HeadscaleService_ListPreAuthKeys_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ListPreAuthKeys(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_ListPreAuthKeys_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListPreAuthKeysRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HeadscaleService_ListPreAuthKeys_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ListPreAuthKeys(ctx, &protoReq) + return msg, metadata, err + +} + +func request_HeadscaleService_DebugCreateMachine_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DebugCreateMachineRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.DebugCreateMachine(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_DebugCreateMachine_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DebugCreateMachineRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.DebugCreateMachine(ctx, &protoReq) + return msg, metadata, err + +} + +func request_HeadscaleService_GetMachine_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetMachineRequest + 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) + } + + msg, err := client.GetMachine(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_GetMachine_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetMachineRequest + 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) + } + + msg, err := server.GetMachine(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_HeadscaleService_RegisterMachine_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_HeadscaleService_RegisterMachine_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq RegisterMachineRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HeadscaleService_RegisterMachine_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.RegisterMachine(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_RegisterMachine_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq RegisterMachineRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HeadscaleService_RegisterMachine_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.RegisterMachine(ctx, &protoReq) + return msg, metadata, err + +} + +func request_HeadscaleService_DeleteMachine_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteMachineRequest + 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) + } + + msg, err := client.DeleteMachine(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_DeleteMachine_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteMachineRequest + 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) + } + + msg, err := server.DeleteMachine(ctx, &protoReq) + return msg, metadata, err + +} + +func request_HeadscaleService_ExpireMachine_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ExpireMachineRequest + 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) + } + + msg, err := client.ExpireMachine(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_ExpireMachine_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ExpireMachineRequest + 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) + } + + msg, err := server.ExpireMachine(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_HeadscaleService_ListMachines_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_HeadscaleService_ListMachines_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListMachinesRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HeadscaleService_ListMachines_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ListMachines(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_ListMachines_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListMachinesRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HeadscaleService_ListMachines_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ListMachines(ctx, &protoReq) + return msg, metadata, err + +} + +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 + + 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) + } + + msg, err := client.GetMachineRoute(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_GetMachineRoute_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetMachineRouteRequest + 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) + } + + msg, err := server.GetMachineRoute(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_HeadscaleService_EnableMachineRoutes_0 = &utilities.DoubleArray{Encoding: map[string]int{"machine_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_HeadscaleService_EnableMachineRoutes_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq EnableMachineRoutesRequest + 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) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HeadscaleService_EnableMachineRoutes_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.EnableMachineRoutes(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_EnableMachineRoutes_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq EnableMachineRoutesRequest + 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) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HeadscaleService_EnableMachineRoutes_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.EnableMachineRoutes(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterHeadscaleServiceHandlerServer registers the http handlers for service HeadscaleService to "mux". +// UnaryRPC :call HeadscaleServiceServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterHeadscaleServiceHandlerFromEndpoint instead. +func RegisterHeadscaleServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server HeadscaleServiceServer) error { + + mux.Handle("GET", pattern_HeadscaleService_GetNamespace_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/GetNamespace", runtime.WithHTTPPathPattern("/api/v1/namespace/{name}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_GetNamespace_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_GetNamespace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_CreateNamespace_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/CreateNamespace", runtime.WithHTTPPathPattern("/api/v1/namespace")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_CreateNamespace_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_CreateNamespace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_RenameNamespace_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/RenameNamespace", runtime.WithHTTPPathPattern("/api/v1/namespace/{old_name}/rename/{new_name}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_RenameNamespace_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_RenameNamespace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_HeadscaleService_DeleteNamespace_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/DeleteNamespace", runtime.WithHTTPPathPattern("/api/v1/namespace/{name}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_DeleteNamespace_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_DeleteNamespace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_HeadscaleService_ListNamespaces_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/ListNamespaces", runtime.WithHTTPPathPattern("/api/v1/namespace")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_ListNamespaces_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_ListNamespaces_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_CreatePreAuthKey_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/CreatePreAuthKey", runtime.WithHTTPPathPattern("/api/v1/preauthkey")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_CreatePreAuthKey_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_CreatePreAuthKey_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_ExpirePreAuthKey_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/ExpirePreAuthKey", runtime.WithHTTPPathPattern("/api/v1/preauthkey/expire")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_ExpirePreAuthKey_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_ExpirePreAuthKey_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_HeadscaleService_ListPreAuthKeys_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/ListPreAuthKeys", runtime.WithHTTPPathPattern("/api/v1/preauthkey")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_ListPreAuthKeys_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_ListPreAuthKeys_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_DebugCreateMachine_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/DebugCreateMachine", runtime.WithHTTPPathPattern("/api/v1/debug/machine")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_DebugCreateMachine_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_DebugCreateMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_HeadscaleService_GetMachine_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/GetMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_GetMachine_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_GetMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_RegisterMachine_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/RegisterMachine", runtime.WithHTTPPathPattern("/api/v1/machine/register")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_RegisterMachine_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_RegisterMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_HeadscaleService_DeleteMachine_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/DeleteMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_DeleteMachine_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_DeleteMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_ExpireMachine_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/ExpireMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}/expire")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_ExpireMachine_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_ExpireMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_HeadscaleService_ListMachines_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/ListMachines", runtime.WithHTTPPathPattern("/api/v1/machine")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_ListMachines_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_ListMachines_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + 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() + 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/GetMachineRoute", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}/routes")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_GetMachineRoute_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_GetMachineRoute_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_EnableMachineRoutes_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/EnableMachineRoutes", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}/routes")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_EnableMachineRoutes_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_EnableMachineRoutes_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterHeadscaleServiceHandlerFromEndpoint is same as RegisterHeadscaleServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterHeadscaleServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterHeadscaleServiceHandler(ctx, mux, conn) +} + +// RegisterHeadscaleServiceHandler registers the http handlers for service HeadscaleService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterHeadscaleServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterHeadscaleServiceHandlerClient(ctx, mux, NewHeadscaleServiceClient(conn)) +} + +// RegisterHeadscaleServiceHandlerClient registers the http handlers for service HeadscaleService +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "HeadscaleServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "HeadscaleServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "HeadscaleServiceClient" to call the correct interceptors. +func RegisterHeadscaleServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client HeadscaleServiceClient) error { + + mux.Handle("GET", pattern_HeadscaleService_GetNamespace_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/GetNamespace", runtime.WithHTTPPathPattern("/api/v1/namespace/{name}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_GetNamespace_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_GetNamespace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_CreateNamespace_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/CreateNamespace", runtime.WithHTTPPathPattern("/api/v1/namespace")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_CreateNamespace_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_CreateNamespace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_RenameNamespace_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/RenameNamespace", runtime.WithHTTPPathPattern("/api/v1/namespace/{old_name}/rename/{new_name}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_RenameNamespace_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_RenameNamespace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_HeadscaleService_DeleteNamespace_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/DeleteNamespace", runtime.WithHTTPPathPattern("/api/v1/namespace/{name}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_DeleteNamespace_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_DeleteNamespace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_HeadscaleService_ListNamespaces_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/ListNamespaces", runtime.WithHTTPPathPattern("/api/v1/namespace")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_ListNamespaces_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_ListNamespaces_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_CreatePreAuthKey_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/CreatePreAuthKey", runtime.WithHTTPPathPattern("/api/v1/preauthkey")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_CreatePreAuthKey_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_CreatePreAuthKey_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_ExpirePreAuthKey_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/ExpirePreAuthKey", runtime.WithHTTPPathPattern("/api/v1/preauthkey/expire")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_ExpirePreAuthKey_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_ExpirePreAuthKey_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_HeadscaleService_ListPreAuthKeys_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/ListPreAuthKeys", runtime.WithHTTPPathPattern("/api/v1/preauthkey")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_ListPreAuthKeys_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_ListPreAuthKeys_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_DebugCreateMachine_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/DebugCreateMachine", runtime.WithHTTPPathPattern("/api/v1/debug/machine")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_DebugCreateMachine_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_DebugCreateMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_HeadscaleService_GetMachine_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/GetMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_GetMachine_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_GetMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_RegisterMachine_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/RegisterMachine", runtime.WithHTTPPathPattern("/api/v1/machine/register")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_RegisterMachine_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_RegisterMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_HeadscaleService_DeleteMachine_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/DeleteMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_DeleteMachine_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_DeleteMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_ExpireMachine_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/ExpireMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}/expire")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_ExpireMachine_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_ExpireMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_HeadscaleService_ListMachines_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/ListMachines", runtime.WithHTTPPathPattern("/api/v1/machine")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_ListMachines_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_ListMachines_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + 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() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/headscale.v1.HeadscaleService/GetMachineRoute", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}/routes")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_GetMachineRoute_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_GetMachineRoute_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_EnableMachineRoutes_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/EnableMachineRoutes", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}/routes")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_EnableMachineRoutes_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_EnableMachineRoutes_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_HeadscaleService_GetNamespace_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "namespace", "name"}, "")) + + pattern_HeadscaleService_CreateNamespace_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "namespace"}, "")) + + pattern_HeadscaleService_RenameNamespace_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", "namespace", "old_name", "rename", "new_name"}, "")) + + pattern_HeadscaleService_DeleteNamespace_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "namespace", "name"}, "")) + + pattern_HeadscaleService_ListNamespaces_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "namespace"}, "")) + + pattern_HeadscaleService_CreatePreAuthKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "preauthkey"}, "")) + + pattern_HeadscaleService_ExpirePreAuthKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "preauthkey", "expire"}, "")) + + pattern_HeadscaleService_ListPreAuthKeys_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "preauthkey"}, "")) + + pattern_HeadscaleService_DebugCreateMachine_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "debug", "machine"}, "")) + + pattern_HeadscaleService_GetMachine_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "machine", "machine_id"}, "")) + + pattern_HeadscaleService_RegisterMachine_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "machine", "register"}, "")) + + pattern_HeadscaleService_DeleteMachine_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "machine", "machine_id"}, "")) + + pattern_HeadscaleService_ExpireMachine_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", "expire"}, "")) + + 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"}, "")) +) + +var ( + forward_HeadscaleService_GetNamespace_0 = runtime.ForwardResponseMessage + + forward_HeadscaleService_CreateNamespace_0 = runtime.ForwardResponseMessage + + forward_HeadscaleService_RenameNamespace_0 = runtime.ForwardResponseMessage + + forward_HeadscaleService_DeleteNamespace_0 = runtime.ForwardResponseMessage + + forward_HeadscaleService_ListNamespaces_0 = runtime.ForwardResponseMessage + + forward_HeadscaleService_CreatePreAuthKey_0 = runtime.ForwardResponseMessage + + forward_HeadscaleService_ExpirePreAuthKey_0 = runtime.ForwardResponseMessage + + forward_HeadscaleService_ListPreAuthKeys_0 = runtime.ForwardResponseMessage + + forward_HeadscaleService_DebugCreateMachine_0 = runtime.ForwardResponseMessage + + forward_HeadscaleService_GetMachine_0 = runtime.ForwardResponseMessage + + forward_HeadscaleService_RegisterMachine_0 = runtime.ForwardResponseMessage + + forward_HeadscaleService_DeleteMachine_0 = runtime.ForwardResponseMessage + + forward_HeadscaleService_ExpireMachine_0 = runtime.ForwardResponseMessage + + 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 new file mode 100644 index 00000000..ab6cb70c --- /dev/null +++ b/gen/go/headscale/v1/headscale_grpc.pb.go @@ -0,0 +1,721 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package v1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// HeadscaleServiceClient is the client API for HeadscaleService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type HeadscaleServiceClient interface { + // --- Namespace start --- + GetNamespace(ctx context.Context, in *GetNamespaceRequest, opts ...grpc.CallOption) (*GetNamespaceResponse, error) + CreateNamespace(ctx context.Context, in *CreateNamespaceRequest, opts ...grpc.CallOption) (*CreateNamespaceResponse, error) + RenameNamespace(ctx context.Context, in *RenameNamespaceRequest, opts ...grpc.CallOption) (*RenameNamespaceResponse, error) + DeleteNamespace(ctx context.Context, in *DeleteNamespaceRequest, opts ...grpc.CallOption) (*DeleteNamespaceResponse, error) + ListNamespaces(ctx context.Context, in *ListNamespacesRequest, opts ...grpc.CallOption) (*ListNamespacesResponse, error) + // --- PreAuthKeys start --- + CreatePreAuthKey(ctx context.Context, in *CreatePreAuthKeyRequest, opts ...grpc.CallOption) (*CreatePreAuthKeyResponse, error) + ExpirePreAuthKey(ctx context.Context, in *ExpirePreAuthKeyRequest, opts ...grpc.CallOption) (*ExpirePreAuthKeyResponse, error) + ListPreAuthKeys(ctx context.Context, in *ListPreAuthKeysRequest, opts ...grpc.CallOption) (*ListPreAuthKeysResponse, error) + // --- Machine start --- + DebugCreateMachine(ctx context.Context, in *DebugCreateMachineRequest, opts ...grpc.CallOption) (*DebugCreateMachineResponse, error) + GetMachine(ctx context.Context, in *GetMachineRequest, opts ...grpc.CallOption) (*GetMachineResponse, error) + RegisterMachine(ctx context.Context, in *RegisterMachineRequest, opts ...grpc.CallOption) (*RegisterMachineResponse, error) + 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) +} + +type headscaleServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewHeadscaleServiceClient(cc grpc.ClientConnInterface) HeadscaleServiceClient { + return &headscaleServiceClient{cc} +} + +func (c *headscaleServiceClient) GetNamespace(ctx context.Context, in *GetNamespaceRequest, opts ...grpc.CallOption) (*GetNamespaceResponse, error) { + out := new(GetNamespaceResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/GetNamespace", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) CreateNamespace(ctx context.Context, in *CreateNamespaceRequest, opts ...grpc.CallOption) (*CreateNamespaceResponse, error) { + out := new(CreateNamespaceResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/CreateNamespace", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) RenameNamespace(ctx context.Context, in *RenameNamespaceRequest, opts ...grpc.CallOption) (*RenameNamespaceResponse, error) { + out := new(RenameNamespaceResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/RenameNamespace", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) DeleteNamespace(ctx context.Context, in *DeleteNamespaceRequest, opts ...grpc.CallOption) (*DeleteNamespaceResponse, error) { + out := new(DeleteNamespaceResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/DeleteNamespace", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) ListNamespaces(ctx context.Context, in *ListNamespacesRequest, opts ...grpc.CallOption) (*ListNamespacesResponse, error) { + out := new(ListNamespacesResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ListNamespaces", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) CreatePreAuthKey(ctx context.Context, in *CreatePreAuthKeyRequest, opts ...grpc.CallOption) (*CreatePreAuthKeyResponse, error) { + out := new(CreatePreAuthKeyResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/CreatePreAuthKey", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) ExpirePreAuthKey(ctx context.Context, in *ExpirePreAuthKeyRequest, opts ...grpc.CallOption) (*ExpirePreAuthKeyResponse, error) { + out := new(ExpirePreAuthKeyResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ExpirePreAuthKey", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) ListPreAuthKeys(ctx context.Context, in *ListPreAuthKeysRequest, opts ...grpc.CallOption) (*ListPreAuthKeysResponse, error) { + out := new(ListPreAuthKeysResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ListPreAuthKeys", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) DebugCreateMachine(ctx context.Context, in *DebugCreateMachineRequest, opts ...grpc.CallOption) (*DebugCreateMachineResponse, error) { + out := new(DebugCreateMachineResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/DebugCreateMachine", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) GetMachine(ctx context.Context, in *GetMachineRequest, opts ...grpc.CallOption) (*GetMachineResponse, error) { + out := new(GetMachineResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/GetMachine", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) RegisterMachine(ctx context.Context, in *RegisterMachineRequest, opts ...grpc.CallOption) (*RegisterMachineResponse, error) { + out := new(RegisterMachineResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/RegisterMachine", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) DeleteMachine(ctx context.Context, in *DeleteMachineRequest, opts ...grpc.CallOption) (*DeleteMachineResponse, error) { + out := new(DeleteMachineResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/DeleteMachine", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) ExpireMachine(ctx context.Context, in *ExpireMachineRequest, opts ...grpc.CallOption) (*ExpireMachineResponse, error) { + out := new(ExpireMachineResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ExpireMachine", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) ListMachines(ctx context.Context, in *ListMachinesRequest, opts ...grpc.CallOption) (*ListMachinesResponse, error) { + out := new(ListMachinesResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ListMachines", in, out, opts...) + if err != nil { + return nil, err + } + 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...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) EnableMachineRoutes(ctx context.Context, in *EnableMachineRoutesRequest, opts ...grpc.CallOption) (*EnableMachineRoutesResponse, error) { + out := new(EnableMachineRoutesResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/EnableMachineRoutes", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// HeadscaleServiceServer is the server API for HeadscaleService service. +// All implementations must embed UnimplementedHeadscaleServiceServer +// for forward compatibility +type HeadscaleServiceServer interface { + // --- Namespace start --- + GetNamespace(context.Context, *GetNamespaceRequest) (*GetNamespaceResponse, error) + CreateNamespace(context.Context, *CreateNamespaceRequest) (*CreateNamespaceResponse, error) + RenameNamespace(context.Context, *RenameNamespaceRequest) (*RenameNamespaceResponse, error) + DeleteNamespace(context.Context, *DeleteNamespaceRequest) (*DeleteNamespaceResponse, error) + ListNamespaces(context.Context, *ListNamespacesRequest) (*ListNamespacesResponse, error) + // --- PreAuthKeys start --- + CreatePreAuthKey(context.Context, *CreatePreAuthKeyRequest) (*CreatePreAuthKeyResponse, error) + ExpirePreAuthKey(context.Context, *ExpirePreAuthKeyRequest) (*ExpirePreAuthKeyResponse, error) + ListPreAuthKeys(context.Context, *ListPreAuthKeysRequest) (*ListPreAuthKeysResponse, error) + // --- Machine start --- + DebugCreateMachine(context.Context, *DebugCreateMachineRequest) (*DebugCreateMachineResponse, error) + GetMachine(context.Context, *GetMachineRequest) (*GetMachineResponse, error) + RegisterMachine(context.Context, *RegisterMachineRequest) (*RegisterMachineResponse, error) + 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) + mustEmbedUnimplementedHeadscaleServiceServer() +} + +// UnimplementedHeadscaleServiceServer must be embedded to have forward compatible implementations. +type UnimplementedHeadscaleServiceServer struct { +} + +func (UnimplementedHeadscaleServiceServer) GetNamespace(context.Context, *GetNamespaceRequest) (*GetNamespaceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetNamespace not implemented") +} +func (UnimplementedHeadscaleServiceServer) CreateNamespace(context.Context, *CreateNamespaceRequest) (*CreateNamespaceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateNamespace not implemented") +} +func (UnimplementedHeadscaleServiceServer) RenameNamespace(context.Context, *RenameNamespaceRequest) (*RenameNamespaceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RenameNamespace not implemented") +} +func (UnimplementedHeadscaleServiceServer) DeleteNamespace(context.Context, *DeleteNamespaceRequest) (*DeleteNamespaceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteNamespace not implemented") +} +func (UnimplementedHeadscaleServiceServer) ListNamespaces(context.Context, *ListNamespacesRequest) (*ListNamespacesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListNamespaces not implemented") +} +func (UnimplementedHeadscaleServiceServer) CreatePreAuthKey(context.Context, *CreatePreAuthKeyRequest) (*CreatePreAuthKeyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreatePreAuthKey not implemented") +} +func (UnimplementedHeadscaleServiceServer) ExpirePreAuthKey(context.Context, *ExpirePreAuthKeyRequest) (*ExpirePreAuthKeyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ExpirePreAuthKey not implemented") +} +func (UnimplementedHeadscaleServiceServer) ListPreAuthKeys(context.Context, *ListPreAuthKeysRequest) (*ListPreAuthKeysResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListPreAuthKeys not implemented") +} +func (UnimplementedHeadscaleServiceServer) DebugCreateMachine(context.Context, *DebugCreateMachineRequest) (*DebugCreateMachineResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DebugCreateMachine not implemented") +} +func (UnimplementedHeadscaleServiceServer) GetMachine(context.Context, *GetMachineRequest) (*GetMachineResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetMachine not implemented") +} +func (UnimplementedHeadscaleServiceServer) RegisterMachine(context.Context, *RegisterMachineRequest) (*RegisterMachineResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RegisterMachine not implemented") +} +func (UnimplementedHeadscaleServiceServer) DeleteMachine(context.Context, *DeleteMachineRequest) (*DeleteMachineResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteMachine not implemented") +} +func (UnimplementedHeadscaleServiceServer) ExpireMachine(context.Context, *ExpireMachineRequest) (*ExpireMachineResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ExpireMachine not implemented") +} +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") +} +func (UnimplementedHeadscaleServiceServer) EnableMachineRoutes(context.Context, *EnableMachineRoutesRequest) (*EnableMachineRoutesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method EnableMachineRoutes not implemented") +} +func (UnimplementedHeadscaleServiceServer) mustEmbedUnimplementedHeadscaleServiceServer() {} + +// UnsafeHeadscaleServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to HeadscaleServiceServer will +// result in compilation errors. +type UnsafeHeadscaleServiceServer interface { + mustEmbedUnimplementedHeadscaleServiceServer() +} + +func RegisterHeadscaleServiceServer(s grpc.ServiceRegistrar, srv HeadscaleServiceServer) { + s.RegisterService(&HeadscaleService_ServiceDesc, srv) +} + +func _HeadscaleService_GetNamespace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetNamespaceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).GetNamespace(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/GetNamespace", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).GetNamespace(ctx, req.(*GetNamespaceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_CreateNamespace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateNamespaceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).CreateNamespace(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/CreateNamespace", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).CreateNamespace(ctx, req.(*CreateNamespaceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_RenameNamespace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RenameNamespaceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).RenameNamespace(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/RenameNamespace", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).RenameNamespace(ctx, req.(*RenameNamespaceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_DeleteNamespace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteNamespaceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).DeleteNamespace(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/DeleteNamespace", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).DeleteNamespace(ctx, req.(*DeleteNamespaceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_ListNamespaces_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListNamespacesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).ListNamespaces(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/ListNamespaces", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).ListNamespaces(ctx, req.(*ListNamespacesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_CreatePreAuthKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreatePreAuthKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).CreatePreAuthKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/CreatePreAuthKey", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).CreatePreAuthKey(ctx, req.(*CreatePreAuthKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_ExpirePreAuthKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ExpirePreAuthKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).ExpirePreAuthKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/ExpirePreAuthKey", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).ExpirePreAuthKey(ctx, req.(*ExpirePreAuthKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_ListPreAuthKeys_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListPreAuthKeysRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).ListPreAuthKeys(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/ListPreAuthKeys", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).ListPreAuthKeys(ctx, req.(*ListPreAuthKeysRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_DebugCreateMachine_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DebugCreateMachineRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).DebugCreateMachine(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/DebugCreateMachine", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).DebugCreateMachine(ctx, req.(*DebugCreateMachineRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_GetMachine_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetMachineRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).GetMachine(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/GetMachine", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).GetMachine(ctx, req.(*GetMachineRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_RegisterMachine_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RegisterMachineRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).RegisterMachine(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/RegisterMachine", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).RegisterMachine(ctx, req.(*RegisterMachineRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_DeleteMachine_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteMachineRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).DeleteMachine(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/DeleteMachine", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).DeleteMachine(ctx, req.(*DeleteMachineRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_ExpireMachine_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ExpireMachineRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).ExpireMachine(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/ExpireMachine", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).ExpireMachine(ctx, req.(*ExpireMachineRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_ListMachines_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListMachinesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).ListMachines(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/ListMachines", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).ListMachines(ctx, req.(*ListMachinesRequest)) + } + 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 { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).GetMachineRoute(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/GetMachineRoute", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).GetMachineRoute(ctx, req.(*GetMachineRouteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_EnableMachineRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EnableMachineRoutesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).EnableMachineRoutes(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/EnableMachineRoutes", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).EnableMachineRoutes(ctx, req.(*EnableMachineRoutesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// HeadscaleService_ServiceDesc is the grpc.ServiceDesc for HeadscaleService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var HeadscaleService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "headscale.v1.HeadscaleService", + HandlerType: (*HeadscaleServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetNamespace", + Handler: _HeadscaleService_GetNamespace_Handler, + }, + { + MethodName: "CreateNamespace", + Handler: _HeadscaleService_CreateNamespace_Handler, + }, + { + MethodName: "RenameNamespace", + Handler: _HeadscaleService_RenameNamespace_Handler, + }, + { + MethodName: "DeleteNamespace", + Handler: _HeadscaleService_DeleteNamespace_Handler, + }, + { + MethodName: "ListNamespaces", + Handler: _HeadscaleService_ListNamespaces_Handler, + }, + { + MethodName: "CreatePreAuthKey", + Handler: _HeadscaleService_CreatePreAuthKey_Handler, + }, + { + MethodName: "ExpirePreAuthKey", + Handler: _HeadscaleService_ExpirePreAuthKey_Handler, + }, + { + MethodName: "ListPreAuthKeys", + Handler: _HeadscaleService_ListPreAuthKeys_Handler, + }, + { + MethodName: "DebugCreateMachine", + Handler: _HeadscaleService_DebugCreateMachine_Handler, + }, + { + MethodName: "GetMachine", + Handler: _HeadscaleService_GetMachine_Handler, + }, + { + MethodName: "RegisterMachine", + Handler: _HeadscaleService_RegisterMachine_Handler, + }, + { + MethodName: "DeleteMachine", + Handler: _HeadscaleService_DeleteMachine_Handler, + }, + { + MethodName: "ExpireMachine", + Handler: _HeadscaleService_ExpireMachine_Handler, + }, + { + MethodName: "ListMachines", + Handler: _HeadscaleService_ListMachines_Handler, + }, + { + MethodName: "ShareMachine", + Handler: _HeadscaleService_ShareMachine_Handler, + }, + { + MethodName: "UnshareMachine", + Handler: _HeadscaleService_UnshareMachine_Handler, + }, + { + MethodName: "GetMachineRoute", + Handler: _HeadscaleService_GetMachineRoute_Handler, + }, + { + MethodName: "EnableMachineRoutes", + Handler: _HeadscaleService_EnableMachineRoutes_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "headscale/v1/headscale.proto", +} diff --git a/gen/go/headscale/v1/machine.pb.go b/gen/go/headscale/v1/machine.pb.go new file mode 100644 index 00000000..deafbe5c --- /dev/null +++ b/gen/go/headscale/v1/machine.pb.go @@ -0,0 +1,1445 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: headscale/v1/machine.proto + +package v1 + +import ( + 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 ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type RegisterMethod int32 + +const ( + RegisterMethod_REGISTER_METHOD_UNSPECIFIED RegisterMethod = 0 + RegisterMethod_REGISTER_METHOD_AUTH_KEY RegisterMethod = 1 + RegisterMethod_REGISTER_METHOD_CLI RegisterMethod = 2 + RegisterMethod_REGISTER_METHOD_OIDC RegisterMethod = 3 +) + +// Enum value maps for RegisterMethod. +var ( + RegisterMethod_name = map[int32]string{ + 0: "REGISTER_METHOD_UNSPECIFIED", + 1: "REGISTER_METHOD_AUTH_KEY", + 2: "REGISTER_METHOD_CLI", + 3: "REGISTER_METHOD_OIDC", + } + RegisterMethod_value = map[string]int32{ + "REGISTER_METHOD_UNSPECIFIED": 0, + "REGISTER_METHOD_AUTH_KEY": 1, + "REGISTER_METHOD_CLI": 2, + "REGISTER_METHOD_OIDC": 3, + } +) + +func (x RegisterMethod) Enum() *RegisterMethod { + p := new(RegisterMethod) + *p = x + return p +} + +func (x RegisterMethod) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (RegisterMethod) Descriptor() protoreflect.EnumDescriptor { + return file_headscale_v1_machine_proto_enumTypes[0].Descriptor() +} + +func (RegisterMethod) Type() protoreflect.EnumType { + return &file_headscale_v1_machine_proto_enumTypes[0] +} + +func (x RegisterMethod) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use RegisterMethod.Descriptor instead. +func (RegisterMethod) EnumDescriptor() ([]byte, []int) { + return file_headscale_v1_machine_proto_rawDescGZIP(), []int{0} +} + +type Machine struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + MachineKey string `protobuf:"bytes,2,opt,name=machine_key,json=machineKey,proto3" json:"machine_key,omitempty"` + NodeKey string `protobuf:"bytes,3,opt,name=node_key,json=nodeKey,proto3" json:"node_key,omitempty"` + DiscoKey string `protobuf:"bytes,4,opt,name=disco_key,json=discoKey,proto3" json:"disco_key,omitempty"` + IpAddress string `protobuf:"bytes,5,opt,name=ip_address,json=ipAddress,proto3" json:"ip_address,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"` +} + +func (x *Machine) Reset() { + *x = Machine{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_machine_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Machine) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Machine) ProtoMessage() {} + +func (x *Machine) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_machine_proto_msgTypes[0] + 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 Machine.ProtoReflect.Descriptor instead. +func (*Machine) Descriptor() ([]byte, []int) { + return file_headscale_v1_machine_proto_rawDescGZIP(), []int{0} +} + +func (x *Machine) GetId() uint64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Machine) GetMachineKey() string { + if x != nil { + return x.MachineKey + } + return "" +} + +func (x *Machine) GetNodeKey() string { + if x != nil { + return x.NodeKey + } + return "" +} + +func (x *Machine) GetDiscoKey() string { + if x != nil { + return x.DiscoKey + } + return "" +} + +func (x *Machine) GetIpAddress() string { + if x != nil { + return x.IpAddress + } + return "" +} + +func (x *Machine) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Machine) GetNamespace() *Namespace { + if x != nil { + return x.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 + } + return nil +} + +func (x *Machine) GetLastSuccessfulUpdate() *timestamppb.Timestamp { + if x != nil { + return x.LastSuccessfulUpdate + } + return nil +} + +func (x *Machine) GetExpiry() *timestamppb.Timestamp { + if x != nil { + return x.Expiry + } + return nil +} + +func (x *Machine) GetPreAuthKey() *PreAuthKey { + if x != nil { + return x.PreAuthKey + } + return nil +} + +func (x *Machine) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +type RegisterMachineRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` +} + +func (x *RegisterMachineRequest) Reset() { + *x = RegisterMachineRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_machine_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterMachineRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterMachineRequest) ProtoMessage() {} + +func (x *RegisterMachineRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_machine_proto_msgTypes[1] + 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 RegisterMachineRequest.ProtoReflect.Descriptor instead. +func (*RegisterMachineRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_machine_proto_rawDescGZIP(), []int{1} +} + +func (x *RegisterMachineRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *RegisterMachineRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +type RegisterMachineResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Machine *Machine `protobuf:"bytes,1,opt,name=machine,proto3" json:"machine,omitempty"` +} + +func (x *RegisterMachineResponse) Reset() { + *x = RegisterMachineResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_machine_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterMachineResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterMachineResponse) ProtoMessage() {} + +func (x *RegisterMachineResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_machine_proto_msgTypes[2] + 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 RegisterMachineResponse.ProtoReflect.Descriptor instead. +func (*RegisterMachineResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_machine_proto_rawDescGZIP(), []int{2} +} + +func (x *RegisterMachineResponse) GetMachine() *Machine { + if x != nil { + return x.Machine + } + return nil +} + +type GetMachineRequest 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"` +} + +func (x *GetMachineRequest) Reset() { + *x = GetMachineRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_machine_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetMachineRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMachineRequest) ProtoMessage() {} + +func (x *GetMachineRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_machine_proto_msgTypes[3] + 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 GetMachineRequest.ProtoReflect.Descriptor instead. +func (*GetMachineRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_machine_proto_rawDescGZIP(), []int{3} +} + +func (x *GetMachineRequest) GetMachineId() uint64 { + if x != nil { + return x.MachineId + } + return 0 +} + +type GetMachineResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Machine *Machine `protobuf:"bytes,1,opt,name=machine,proto3" json:"machine,omitempty"` +} + +func (x *GetMachineResponse) Reset() { + *x = GetMachineResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_machine_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetMachineResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMachineResponse) ProtoMessage() {} + +func (x *GetMachineResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_machine_proto_msgTypes[4] + 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 GetMachineResponse.ProtoReflect.Descriptor instead. +func (*GetMachineResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_machine_proto_rawDescGZIP(), []int{4} +} + +func (x *GetMachineResponse) GetMachine() *Machine { + if x != nil { + return x.Machine + } + return nil +} + +type DeleteMachineRequest 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"` +} + +func (x *DeleteMachineRequest) Reset() { + *x = DeleteMachineRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_machine_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteMachineRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteMachineRequest) ProtoMessage() {} + +func (x *DeleteMachineRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_machine_proto_msgTypes[5] + 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 DeleteMachineRequest.ProtoReflect.Descriptor instead. +func (*DeleteMachineRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_machine_proto_rawDescGZIP(), []int{5} +} + +func (x *DeleteMachineRequest) GetMachineId() uint64 { + if x != nil { + return x.MachineId + } + return 0 +} + +type DeleteMachineResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DeleteMachineResponse) Reset() { + *x = DeleteMachineResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_machine_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteMachineResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteMachineResponse) ProtoMessage() {} + +func (x *DeleteMachineResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_machine_proto_msgTypes[6] + 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 DeleteMachineResponse.ProtoReflect.Descriptor instead. +func (*DeleteMachineResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_machine_proto_rawDescGZIP(), []int{6} +} + +type ExpireMachineRequest 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"` +} + +func (x *ExpireMachineRequest) Reset() { + *x = ExpireMachineRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_machine_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExpireMachineRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExpireMachineRequest) ProtoMessage() {} + +func (x *ExpireMachineRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_machine_proto_msgTypes[7] + 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 ExpireMachineRequest.ProtoReflect.Descriptor instead. +func (*ExpireMachineRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_machine_proto_rawDescGZIP(), []int{7} +} + +func (x *ExpireMachineRequest) GetMachineId() uint64 { + if x != nil { + return x.MachineId + } + return 0 +} + +type ExpireMachineResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Machine *Machine `protobuf:"bytes,1,opt,name=machine,proto3" json:"machine,omitempty"` +} + +func (x *ExpireMachineResponse) Reset() { + *x = ExpireMachineResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_machine_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExpireMachineResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExpireMachineResponse) ProtoMessage() {} + +func (x *ExpireMachineResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_machine_proto_msgTypes[8] + 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 ExpireMachineResponse.ProtoReflect.Descriptor instead. +func (*ExpireMachineResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_machine_proto_rawDescGZIP(), []int{8} +} + +func (x *ExpireMachineResponse) GetMachine() *Machine { + if x != nil { + return x.Machine + } + return nil +} + +type ListMachinesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` +} + +func (x *ListMachinesRequest) Reset() { + *x = ListMachinesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_machine_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListMachinesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListMachinesRequest) ProtoMessage() {} + +func (x *ListMachinesRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_machine_proto_msgTypes[9] + 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 ListMachinesRequest.ProtoReflect.Descriptor instead. +func (*ListMachinesRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_machine_proto_rawDescGZIP(), []int{9} +} + +func (x *ListMachinesRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +type ListMachinesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Machines []*Machine `protobuf:"bytes,1,rep,name=machines,proto3" json:"machines,omitempty"` +} + +func (x *ListMachinesResponse) Reset() { + *x = ListMachinesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_machine_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListMachinesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListMachinesResponse) ProtoMessage() {} + +func (x *ListMachinesResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_machine_proto_msgTypes[10] + 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 ListMachinesResponse.ProtoReflect.Descriptor instead. +func (*ListMachinesResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_machine_proto_rawDescGZIP(), []int{10} +} + +func (x *ListMachinesResponse) GetMachines() []*Machine { + if x != nil { + return x.Machines + } + 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 + unknownFields protoimpl.UnknownFields + + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Routes []string `protobuf:"bytes,4,rep,name=routes,proto3" json:"routes,omitempty"` +} + +func (x *DebugCreateMachineRequest) Reset() { + *x = DebugCreateMachineRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_machine_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DebugCreateMachineRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DebugCreateMachineRequest) ProtoMessage() {} + +func (x *DebugCreateMachineRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_machine_proto_msgTypes[15] + 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 DebugCreateMachineRequest.ProtoReflect.Descriptor instead. +func (*DebugCreateMachineRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_machine_proto_rawDescGZIP(), []int{15} +} + +func (x *DebugCreateMachineRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *DebugCreateMachineRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *DebugCreateMachineRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *DebugCreateMachineRequest) GetRoutes() []string { + if x != nil { + return x.Routes + } + return nil +} + +type DebugCreateMachineResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Machine *Machine `protobuf:"bytes,1,opt,name=machine,proto3" json:"machine,omitempty"` +} + +func (x *DebugCreateMachineResponse) Reset() { + *x = DebugCreateMachineResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_machine_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DebugCreateMachineResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DebugCreateMachineResponse) ProtoMessage() {} + +func (x *DebugCreateMachineResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_machine_proto_msgTypes[16] + 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 DebugCreateMachineResponse.ProtoReflect.Descriptor instead. +func (*DebugCreateMachineResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_machine_proto_rawDescGZIP(), []int{16} +} + +func (x *DebugCreateMachineResponse) GetMachine() *Machine { + if x != nil { + return x.Machine + } + return nil +} + +var File_headscale_v1_machine_proto protoreflect.FileDescriptor + +var file_headscale_v1_machine_proto_rawDesc = []byte{ + 0x0a, 0x1a, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6d, + 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x68, 0x65, + 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x68, 0x65, 0x61, + 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, 0xf9, 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, + 0x6e, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6b, 0x65, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x4b, 0x65, 0x79, + 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, + 0x0a, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 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, 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, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x4a, + 0x0a, 0x17, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 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, 0x32, 0x0a, 0x11, 0x47, 0x65, + 0x74, 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, 0x22, 0x45, + 0x0a, 0x12, 0x47, 0x65, 0x74, 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, 0x35, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 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, 0x22, 0x17, 0x0a, 0x15, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x0a, 0x14, 0x45, 0x78, 0x70, 0x69, 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, 0x22, 0x48, 0x0a, 0x15, + 0x45, 0x78, 0x70, 0x69, 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, 0x33, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, + 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 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, 0x22, 0x49, 0x0a, 0x14, 0x4c, + 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 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, +} + +var ( + file_headscale_v1_machine_proto_rawDescOnce sync.Once + file_headscale_v1_machine_proto_rawDescData = file_headscale_v1_machine_proto_rawDesc +) + +func file_headscale_v1_machine_proto_rawDescGZIP() []byte { + file_headscale_v1_machine_proto_rawDescOnce.Do(func() { + file_headscale_v1_machine_proto_rawDescData = protoimpl.X.CompressGZIP(file_headscale_v1_machine_proto_rawDescData) + }) + return file_headscale_v1_machine_proto_rawDescData +} + +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_goTypes = []interface{}{ + (RegisterMethod)(0), // 0: headscale.v1.RegisterMethod + (*Machine)(nil), // 1: headscale.v1.Machine + (*RegisterMachineRequest)(nil), // 2: headscale.v1.RegisterMachineRequest + (*RegisterMachineResponse)(nil), // 3: headscale.v1.RegisterMachineResponse + (*GetMachineRequest)(nil), // 4: headscale.v1.GetMachineRequest + (*GetMachineResponse)(nil), // 5: headscale.v1.GetMachineResponse + (*DeleteMachineRequest)(nil), // 6: headscale.v1.DeleteMachineRequest + (*DeleteMachineResponse)(nil), // 7: headscale.v1.DeleteMachineResponse + (*ExpireMachineRequest)(nil), // 8: headscale.v1.ExpireMachineRequest + (*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 +} +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 + 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 +} + +func init() { file_headscale_v1_machine_proto_init() } +func file_headscale_v1_machine_proto_init() { + if File_headscale_v1_machine_proto != nil { + return + } + file_headscale_v1_namespace_proto_init() + file_headscale_v1_preauthkey_proto_init() + if !protoimpl.UnsafeEnabled { + file_headscale_v1_machine_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Machine); 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[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterMachineRequest); 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[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterMachineResponse); 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[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetMachineRequest); 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[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetMachineResponse); 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[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteMachineRequest); 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[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteMachineResponse); 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[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExpireMachineRequest); 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[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExpireMachineResponse); 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[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListMachinesRequest); 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[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListMachinesResponse); 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[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 + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_machine_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DebugCreateMachineResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_headscale_v1_machine_proto_rawDesc, + NumEnums: 1, + NumMessages: 17, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_headscale_v1_machine_proto_goTypes, + DependencyIndexes: file_headscale_v1_machine_proto_depIdxs, + EnumInfos: file_headscale_v1_machine_proto_enumTypes, + MessageInfos: file_headscale_v1_machine_proto_msgTypes, + }.Build() + File_headscale_v1_machine_proto = out.File + file_headscale_v1_machine_proto_rawDesc = nil + file_headscale_v1_machine_proto_goTypes = nil + file_headscale_v1_machine_proto_depIdxs = nil +} diff --git a/gen/go/headscale/v1/namespace.pb.go b/gen/go/headscale/v1/namespace.pb.go new file mode 100644 index 00000000..f8af539e --- /dev/null +++ b/gen/go/headscale/v1/namespace.pb.go @@ -0,0 +1,801 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: headscale/v1/namespace.proto + +package v1 + +import ( + 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 ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Namespace struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` +} + +func (x *Namespace) Reset() { + *x = Namespace{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_namespace_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Namespace) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Namespace) ProtoMessage() {} + +func (x *Namespace) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_namespace_proto_msgTypes[0] + 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 Namespace.ProtoReflect.Descriptor instead. +func (*Namespace) Descriptor() ([]byte, []int) { + return file_headscale_v1_namespace_proto_rawDescGZIP(), []int{0} +} + +func (x *Namespace) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Namespace) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Namespace) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +type GetNamespaceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *GetNamespaceRequest) Reset() { + *x = GetNamespaceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_namespace_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetNamespaceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetNamespaceRequest) ProtoMessage() {} + +func (x *GetNamespaceRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_namespace_proto_msgTypes[1] + 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 GetNamespaceRequest.ProtoReflect.Descriptor instead. +func (*GetNamespaceRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_namespace_proto_rawDescGZIP(), []int{1} +} + +func (x *GetNamespaceRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type GetNamespaceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Namespace *Namespace `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` +} + +func (x *GetNamespaceResponse) Reset() { + *x = GetNamespaceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_namespace_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetNamespaceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetNamespaceResponse) ProtoMessage() {} + +func (x *GetNamespaceResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_namespace_proto_msgTypes[2] + 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 GetNamespaceResponse.ProtoReflect.Descriptor instead. +func (*GetNamespaceResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_namespace_proto_rawDescGZIP(), []int{2} +} + +func (x *GetNamespaceResponse) GetNamespace() *Namespace { + if x != nil { + return x.Namespace + } + return nil +} + +type CreateNamespaceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *CreateNamespaceRequest) Reset() { + *x = CreateNamespaceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_namespace_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateNamespaceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateNamespaceRequest) ProtoMessage() {} + +func (x *CreateNamespaceRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_namespace_proto_msgTypes[3] + 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 CreateNamespaceRequest.ProtoReflect.Descriptor instead. +func (*CreateNamespaceRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_namespace_proto_rawDescGZIP(), []int{3} +} + +func (x *CreateNamespaceRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type CreateNamespaceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Namespace *Namespace `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` +} + +func (x *CreateNamespaceResponse) Reset() { + *x = CreateNamespaceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_namespace_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateNamespaceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateNamespaceResponse) ProtoMessage() {} + +func (x *CreateNamespaceResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_namespace_proto_msgTypes[4] + 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 CreateNamespaceResponse.ProtoReflect.Descriptor instead. +func (*CreateNamespaceResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_namespace_proto_rawDescGZIP(), []int{4} +} + +func (x *CreateNamespaceResponse) GetNamespace() *Namespace { + if x != nil { + return x.Namespace + } + return nil +} + +type RenameNamespaceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OldName string `protobuf:"bytes,1,opt,name=old_name,json=oldName,proto3" json:"old_name,omitempty"` + NewName string `protobuf:"bytes,2,opt,name=new_name,json=newName,proto3" json:"new_name,omitempty"` +} + +func (x *RenameNamespaceRequest) Reset() { + *x = RenameNamespaceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_namespace_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RenameNamespaceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RenameNamespaceRequest) ProtoMessage() {} + +func (x *RenameNamespaceRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_namespace_proto_msgTypes[5] + 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 RenameNamespaceRequest.ProtoReflect.Descriptor instead. +func (*RenameNamespaceRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_namespace_proto_rawDescGZIP(), []int{5} +} + +func (x *RenameNamespaceRequest) GetOldName() string { + if x != nil { + return x.OldName + } + return "" +} + +func (x *RenameNamespaceRequest) GetNewName() string { + if x != nil { + return x.NewName + } + return "" +} + +type RenameNamespaceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Namespace *Namespace `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` +} + +func (x *RenameNamespaceResponse) Reset() { + *x = RenameNamespaceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_namespace_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RenameNamespaceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RenameNamespaceResponse) ProtoMessage() {} + +func (x *RenameNamespaceResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_namespace_proto_msgTypes[6] + 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 RenameNamespaceResponse.ProtoReflect.Descriptor instead. +func (*RenameNamespaceResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_namespace_proto_rawDescGZIP(), []int{6} +} + +func (x *RenameNamespaceResponse) GetNamespace() *Namespace { + if x != nil { + return x.Namespace + } + return nil +} + +type DeleteNamespaceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *DeleteNamespaceRequest) Reset() { + *x = DeleteNamespaceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_namespace_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteNamespaceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteNamespaceRequest) ProtoMessage() {} + +func (x *DeleteNamespaceRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_namespace_proto_msgTypes[7] + 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 DeleteNamespaceRequest.ProtoReflect.Descriptor instead. +func (*DeleteNamespaceRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_namespace_proto_rawDescGZIP(), []int{7} +} + +func (x *DeleteNamespaceRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type DeleteNamespaceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DeleteNamespaceResponse) Reset() { + *x = DeleteNamespaceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_namespace_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteNamespaceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteNamespaceResponse) ProtoMessage() {} + +func (x *DeleteNamespaceResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_namespace_proto_msgTypes[8] + 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 DeleteNamespaceResponse.ProtoReflect.Descriptor instead. +func (*DeleteNamespaceResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_namespace_proto_rawDescGZIP(), []int{8} +} + +type ListNamespacesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ListNamespacesRequest) Reset() { + *x = ListNamespacesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_namespace_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListNamespacesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListNamespacesRequest) ProtoMessage() {} + +func (x *ListNamespacesRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_namespace_proto_msgTypes[9] + 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 ListNamespacesRequest.ProtoReflect.Descriptor instead. +func (*ListNamespacesRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_namespace_proto_rawDescGZIP(), []int{9} +} + +type ListNamespacesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Namespaces []*Namespace `protobuf:"bytes,1,rep,name=namespaces,proto3" json:"namespaces,omitempty"` +} + +func (x *ListNamespacesResponse) Reset() { + *x = ListNamespacesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_namespace_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListNamespacesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListNamespacesResponse) ProtoMessage() {} + +func (x *ListNamespacesResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_namespace_proto_msgTypes[10] + 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 ListNamespacesResponse.ProtoReflect.Descriptor instead. +func (*ListNamespacesResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_namespace_proto_rawDescGZIP(), []int{10} +} + +func (x *ListNamespacesResponse) GetNamespaces() []*Namespace { + if x != nil { + return x.Namespaces + } + return nil +} + +var File_headscale_v1_namespace_proto protoreflect.FileDescriptor + +var file_headscale_v1_namespace_proto_rawDesc = []byte{ + 0x0a, 0x1c, 0x68, 0x65, 0x61, 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, 0x12, 0x0c, + 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6a, 0x0a, + 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x39, + 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 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, 0x29, 0x0a, 0x13, 0x47, 0x65, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x4d, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x09, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 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, 0x22, 0x2c, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x22, 0x50, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x09, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 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, 0x22, 0x4e, 0x0a, 0x16, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, + 0x08, 0x6f, 0x6c, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6f, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x4e, + 0x61, 0x6d, 0x65, 0x22, 0x50, 0x0a, 0x17, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, + 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 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, 0x22, 0x2c, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x22, 0x19, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, + 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x51, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x03, 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, 0x0a, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 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_namespace_proto_rawDescOnce sync.Once + file_headscale_v1_namespace_proto_rawDescData = file_headscale_v1_namespace_proto_rawDesc +) + +func file_headscale_v1_namespace_proto_rawDescGZIP() []byte { + file_headscale_v1_namespace_proto_rawDescOnce.Do(func() { + file_headscale_v1_namespace_proto_rawDescData = protoimpl.X.CompressGZIP(file_headscale_v1_namespace_proto_rawDescData) + }) + return file_headscale_v1_namespace_proto_rawDescData +} + +var file_headscale_v1_namespace_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_headscale_v1_namespace_proto_goTypes = []interface{}{ + (*Namespace)(nil), // 0: headscale.v1.Namespace + (*GetNamespaceRequest)(nil), // 1: headscale.v1.GetNamespaceRequest + (*GetNamespaceResponse)(nil), // 2: headscale.v1.GetNamespaceResponse + (*CreateNamespaceRequest)(nil), // 3: headscale.v1.CreateNamespaceRequest + (*CreateNamespaceResponse)(nil), // 4: headscale.v1.CreateNamespaceResponse + (*RenameNamespaceRequest)(nil), // 5: headscale.v1.RenameNamespaceRequest + (*RenameNamespaceResponse)(nil), // 6: headscale.v1.RenameNamespaceResponse + (*DeleteNamespaceRequest)(nil), // 7: headscale.v1.DeleteNamespaceRequest + (*DeleteNamespaceResponse)(nil), // 8: headscale.v1.DeleteNamespaceResponse + (*ListNamespacesRequest)(nil), // 9: headscale.v1.ListNamespacesRequest + (*ListNamespacesResponse)(nil), // 10: headscale.v1.ListNamespacesResponse + (*timestamppb.Timestamp)(nil), // 11: google.protobuf.Timestamp +} +var file_headscale_v1_namespace_proto_depIdxs = []int32{ + 11, // 0: headscale.v1.Namespace.created_at:type_name -> google.protobuf.Timestamp + 0, // 1: headscale.v1.GetNamespaceResponse.namespace:type_name -> headscale.v1.Namespace + 0, // 2: headscale.v1.CreateNamespaceResponse.namespace:type_name -> headscale.v1.Namespace + 0, // 3: headscale.v1.RenameNamespaceResponse.namespace:type_name -> headscale.v1.Namespace + 0, // 4: headscale.v1.ListNamespacesResponse.namespaces:type_name -> headscale.v1.Namespace + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_headscale_v1_namespace_proto_init() } +func file_headscale_v1_namespace_proto_init() { + if File_headscale_v1_namespace_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_headscale_v1_namespace_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Namespace); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_namespace_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNamespaceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_namespace_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNamespaceResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_namespace_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateNamespaceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_namespace_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateNamespaceResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_namespace_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RenameNamespaceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_namespace_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RenameNamespaceResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_namespace_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteNamespaceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_namespace_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteNamespaceResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_namespace_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListNamespacesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_namespace_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListNamespacesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_headscale_v1_namespace_proto_rawDesc, + NumEnums: 0, + NumMessages: 11, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_headscale_v1_namespace_proto_goTypes, + DependencyIndexes: file_headscale_v1_namespace_proto_depIdxs, + MessageInfos: file_headscale_v1_namespace_proto_msgTypes, + }.Build() + File_headscale_v1_namespace_proto = out.File + file_headscale_v1_namespace_proto_rawDesc = nil + file_headscale_v1_namespace_proto_goTypes = nil + file_headscale_v1_namespace_proto_depIdxs = nil +} diff --git a/gen/go/headscale/v1/preauthkey.pb.go b/gen/go/headscale/v1/preauthkey.pb.go new file mode 100644 index 00000000..82b16bf8 --- /dev/null +++ b/gen/go/headscale/v1/preauthkey.pb.go @@ -0,0 +1,640 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: headscale/v1/preauthkey.proto + +package v1 + +import ( + 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 ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type PreAuthKey struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Key string `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` + Reusable bool `protobuf:"varint,4,opt,name=reusable,proto3" json:"reusable,omitempty"` + Ephemeral bool `protobuf:"varint,5,opt,name=ephemeral,proto3" json:"ephemeral,omitempty"` + Used bool `protobuf:"varint,6,opt,name=used,proto3" json:"used,omitempty"` + Expiration *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=expiration,proto3" json:"expiration,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` +} + +func (x *PreAuthKey) Reset() { + *x = PreAuthKey{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_preauthkey_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PreAuthKey) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PreAuthKey) ProtoMessage() {} + +func (x *PreAuthKey) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_preauthkey_proto_msgTypes[0] + 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 PreAuthKey.ProtoReflect.Descriptor instead. +func (*PreAuthKey) Descriptor() ([]byte, []int) { + return file_headscale_v1_preauthkey_proto_rawDescGZIP(), []int{0} +} + +func (x *PreAuthKey) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *PreAuthKey) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *PreAuthKey) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *PreAuthKey) GetReusable() bool { + if x != nil { + return x.Reusable + } + return false +} + +func (x *PreAuthKey) GetEphemeral() bool { + if x != nil { + return x.Ephemeral + } + return false +} + +func (x *PreAuthKey) GetUsed() bool { + if x != nil { + return x.Used + } + return false +} + +func (x *PreAuthKey) GetExpiration() *timestamppb.Timestamp { + if x != nil { + return x.Expiration + } + return nil +} + +func (x *PreAuthKey) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +type CreatePreAuthKeyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + Reusable bool `protobuf:"varint,2,opt,name=reusable,proto3" json:"reusable,omitempty"` + Ephemeral bool `protobuf:"varint,3,opt,name=ephemeral,proto3" json:"ephemeral,omitempty"` + Expiration *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=expiration,proto3" json:"expiration,omitempty"` +} + +func (x *CreatePreAuthKeyRequest) Reset() { + *x = CreatePreAuthKeyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_preauthkey_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreatePreAuthKeyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreatePreAuthKeyRequest) ProtoMessage() {} + +func (x *CreatePreAuthKeyRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_preauthkey_proto_msgTypes[1] + 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 CreatePreAuthKeyRequest.ProtoReflect.Descriptor instead. +func (*CreatePreAuthKeyRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_preauthkey_proto_rawDescGZIP(), []int{1} +} + +func (x *CreatePreAuthKeyRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *CreatePreAuthKeyRequest) GetReusable() bool { + if x != nil { + return x.Reusable + } + return false +} + +func (x *CreatePreAuthKeyRequest) GetEphemeral() bool { + if x != nil { + return x.Ephemeral + } + return false +} + +func (x *CreatePreAuthKeyRequest) GetExpiration() *timestamppb.Timestamp { + if x != nil { + return x.Expiration + } + return nil +} + +type CreatePreAuthKeyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PreAuthKey *PreAuthKey `protobuf:"bytes,1,opt,name=pre_auth_key,json=preAuthKey,proto3" json:"pre_auth_key,omitempty"` +} + +func (x *CreatePreAuthKeyResponse) Reset() { + *x = CreatePreAuthKeyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_preauthkey_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreatePreAuthKeyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreatePreAuthKeyResponse) ProtoMessage() {} + +func (x *CreatePreAuthKeyResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_preauthkey_proto_msgTypes[2] + 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 CreatePreAuthKeyResponse.ProtoReflect.Descriptor instead. +func (*CreatePreAuthKeyResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_preauthkey_proto_rawDescGZIP(), []int{2} +} + +func (x *CreatePreAuthKeyResponse) GetPreAuthKey() *PreAuthKey { + if x != nil { + return x.PreAuthKey + } + return nil +} + +type ExpirePreAuthKeyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` +} + +func (x *ExpirePreAuthKeyRequest) Reset() { + *x = ExpirePreAuthKeyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_preauthkey_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExpirePreAuthKeyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExpirePreAuthKeyRequest) ProtoMessage() {} + +func (x *ExpirePreAuthKeyRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_preauthkey_proto_msgTypes[3] + 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 ExpirePreAuthKeyRequest.ProtoReflect.Descriptor instead. +func (*ExpirePreAuthKeyRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_preauthkey_proto_rawDescGZIP(), []int{3} +} + +func (x *ExpirePreAuthKeyRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *ExpirePreAuthKeyRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +type ExpirePreAuthKeyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ExpirePreAuthKeyResponse) Reset() { + *x = ExpirePreAuthKeyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_preauthkey_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExpirePreAuthKeyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExpirePreAuthKeyResponse) ProtoMessage() {} + +func (x *ExpirePreAuthKeyResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_preauthkey_proto_msgTypes[4] + 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 ExpirePreAuthKeyResponse.ProtoReflect.Descriptor instead. +func (*ExpirePreAuthKeyResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_preauthkey_proto_rawDescGZIP(), []int{4} +} + +type ListPreAuthKeysRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` +} + +func (x *ListPreAuthKeysRequest) Reset() { + *x = ListPreAuthKeysRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_preauthkey_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListPreAuthKeysRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListPreAuthKeysRequest) ProtoMessage() {} + +func (x *ListPreAuthKeysRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_preauthkey_proto_msgTypes[5] + 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 ListPreAuthKeysRequest.ProtoReflect.Descriptor instead. +func (*ListPreAuthKeysRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_preauthkey_proto_rawDescGZIP(), []int{5} +} + +func (x *ListPreAuthKeysRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +type ListPreAuthKeysResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PreAuthKeys []*PreAuthKey `protobuf:"bytes,1,rep,name=pre_auth_keys,json=preAuthKeys,proto3" json:"pre_auth_keys,omitempty"` +} + +func (x *ListPreAuthKeysResponse) Reset() { + *x = ListPreAuthKeysResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_preauthkey_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListPreAuthKeysResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListPreAuthKeysResponse) ProtoMessage() {} + +func (x *ListPreAuthKeysResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_preauthkey_proto_msgTypes[6] + 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 ListPreAuthKeysResponse.ProtoReflect.Descriptor instead. +func (*ListPreAuthKeysResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_preauthkey_proto_rawDescGZIP(), []int{6} +} + +func (x *ListPreAuthKeysResponse) GetPreAuthKeys() []*PreAuthKey { + if x != nil { + return x.PreAuthKeys + } + return nil +} + +var File_headscale_v1_preauthkey_proto protoreflect.FileDescriptor + +var file_headscale_v1_preauthkey_proto_rawDesc = []byte{ + 0x0a, 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, 0x12, + 0x0c, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x91, + 0x02, 0x0a, 0x0a, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 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, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1a, 0x0a, + 0x08, 0x72, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x08, 0x72, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x70, 0x68, + 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x70, + 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x75, 0x73, 0x65, 0x64, 0x12, 0x3a, 0x0a, 0x0a, 0x65, + 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 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, 0x0a, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x64, 0x5f, 0x61, 0x74, 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, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x41, 0x74, 0x22, 0xad, 0x01, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, + 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 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, 0x1a, 0x0a, 0x08, + 0x72, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, + 0x72, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x70, 0x68, 0x65, + 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x70, 0x68, + 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x3a, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 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, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x22, 0x56, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x41, + 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, + 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, + 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, 0x22, 0x49, 0x0a, 0x17, 0x45, 0x78, + 0x70, 0x69, 0x72, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 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, 0x22, 0x1a, 0x0a, 0x18, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x50, + 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x36, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, + 0x4b, 0x65, 0x79, 0x73, 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, 0x22, 0x57, 0x0a, 0x17, 0x4c, 0x69, 0x73, + 0x74, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, + 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 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, 0x0b, 0x70, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, + 0x79, 0x73, 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_preauthkey_proto_rawDescOnce sync.Once + file_headscale_v1_preauthkey_proto_rawDescData = file_headscale_v1_preauthkey_proto_rawDesc +) + +func file_headscale_v1_preauthkey_proto_rawDescGZIP() []byte { + file_headscale_v1_preauthkey_proto_rawDescOnce.Do(func() { + file_headscale_v1_preauthkey_proto_rawDescData = protoimpl.X.CompressGZIP(file_headscale_v1_preauthkey_proto_rawDescData) + }) + return file_headscale_v1_preauthkey_proto_rawDescData +} + +var file_headscale_v1_preauthkey_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_headscale_v1_preauthkey_proto_goTypes = []interface{}{ + (*PreAuthKey)(nil), // 0: headscale.v1.PreAuthKey + (*CreatePreAuthKeyRequest)(nil), // 1: headscale.v1.CreatePreAuthKeyRequest + (*CreatePreAuthKeyResponse)(nil), // 2: headscale.v1.CreatePreAuthKeyResponse + (*ExpirePreAuthKeyRequest)(nil), // 3: headscale.v1.ExpirePreAuthKeyRequest + (*ExpirePreAuthKeyResponse)(nil), // 4: headscale.v1.ExpirePreAuthKeyResponse + (*ListPreAuthKeysRequest)(nil), // 5: headscale.v1.ListPreAuthKeysRequest + (*ListPreAuthKeysResponse)(nil), // 6: headscale.v1.ListPreAuthKeysResponse + (*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp +} +var file_headscale_v1_preauthkey_proto_depIdxs = []int32{ + 7, // 0: headscale.v1.PreAuthKey.expiration:type_name -> google.protobuf.Timestamp + 7, // 1: headscale.v1.PreAuthKey.created_at:type_name -> google.protobuf.Timestamp + 7, // 2: headscale.v1.CreatePreAuthKeyRequest.expiration:type_name -> google.protobuf.Timestamp + 0, // 3: headscale.v1.CreatePreAuthKeyResponse.pre_auth_key:type_name -> headscale.v1.PreAuthKey + 0, // 4: headscale.v1.ListPreAuthKeysResponse.pre_auth_keys:type_name -> headscale.v1.PreAuthKey + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_headscale_v1_preauthkey_proto_init() } +func file_headscale_v1_preauthkey_proto_init() { + if File_headscale_v1_preauthkey_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_headscale_v1_preauthkey_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PreAuthKey); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_preauthkey_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreatePreAuthKeyRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_preauthkey_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreatePreAuthKeyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_preauthkey_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExpirePreAuthKeyRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_preauthkey_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExpirePreAuthKeyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_preauthkey_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListPreAuthKeysRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_preauthkey_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListPreAuthKeysResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_headscale_v1_preauthkey_proto_rawDesc, + NumEnums: 0, + NumMessages: 7, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_headscale_v1_preauthkey_proto_goTypes, + DependencyIndexes: file_headscale_v1_preauthkey_proto_depIdxs, + MessageInfos: file_headscale_v1_preauthkey_proto_msgTypes, + }.Build() + File_headscale_v1_preauthkey_proto = out.File + file_headscale_v1_preauthkey_proto_rawDesc = nil + file_headscale_v1_preauthkey_proto_goTypes = nil + file_headscale_v1_preauthkey_proto_depIdxs = nil +} diff --git a/gen/go/headscale/v1/routes.pb.go b/gen/go/headscale/v1/routes.pb.go new file mode 100644 index 00000000..e32e1d15 --- /dev/null +++ b/gen/go/headscale/v1/routes.pb.go @@ -0,0 +1,424 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: headscale/v1/routes.proto + +package v1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Routes struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AdvertisedRoutes []string `protobuf:"bytes,1,rep,name=advertised_routes,json=advertisedRoutes,proto3" json:"advertised_routes,omitempty"` + EnabledRoutes []string `protobuf:"bytes,2,rep,name=enabled_routes,json=enabledRoutes,proto3" json:"enabled_routes,omitempty"` +} + +func (x *Routes) Reset() { + *x = Routes{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_routes_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Routes) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Routes) ProtoMessage() {} + +func (x *Routes) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_routes_proto_msgTypes[0] + 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 Routes.ProtoReflect.Descriptor instead. +func (*Routes) Descriptor() ([]byte, []int) { + return file_headscale_v1_routes_proto_rawDescGZIP(), []int{0} +} + +func (x *Routes) GetAdvertisedRoutes() []string { + if x != nil { + return x.AdvertisedRoutes + } + return nil +} + +func (x *Routes) GetEnabledRoutes() []string { + if x != nil { + return x.EnabledRoutes + } + return nil +} + +type GetMachineRouteRequest 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"` +} + +func (x *GetMachineRouteRequest) Reset() { + *x = GetMachineRouteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_routes_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetMachineRouteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMachineRouteRequest) ProtoMessage() {} + +func (x *GetMachineRouteRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_routes_proto_msgTypes[1] + 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 GetMachineRouteRequest.ProtoReflect.Descriptor instead. +func (*GetMachineRouteRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_routes_proto_rawDescGZIP(), []int{1} +} + +func (x *GetMachineRouteRequest) GetMachineId() uint64 { + if x != nil { + return x.MachineId + } + return 0 +} + +type GetMachineRouteResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Routes *Routes `protobuf:"bytes,1,opt,name=routes,proto3" json:"routes,omitempty"` +} + +func (x *GetMachineRouteResponse) Reset() { + *x = GetMachineRouteResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_routes_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetMachineRouteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMachineRouteResponse) ProtoMessage() {} + +func (x *GetMachineRouteResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_routes_proto_msgTypes[2] + 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 GetMachineRouteResponse.ProtoReflect.Descriptor instead. +func (*GetMachineRouteResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_routes_proto_rawDescGZIP(), []int{2} +} + +func (x *GetMachineRouteResponse) GetRoutes() *Routes { + if x != nil { + return x.Routes + } + return nil +} + +type EnableMachineRoutesRequest 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"` + Routes []string `protobuf:"bytes,2,rep,name=routes,proto3" json:"routes,omitempty"` +} + +func (x *EnableMachineRoutesRequest) Reset() { + *x = EnableMachineRoutesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_routes_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EnableMachineRoutesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EnableMachineRoutesRequest) ProtoMessage() {} + +func (x *EnableMachineRoutesRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_routes_proto_msgTypes[3] + 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 EnableMachineRoutesRequest.ProtoReflect.Descriptor instead. +func (*EnableMachineRoutesRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_routes_proto_rawDescGZIP(), []int{3} +} + +func (x *EnableMachineRoutesRequest) GetMachineId() uint64 { + if x != nil { + return x.MachineId + } + return 0 +} + +func (x *EnableMachineRoutesRequest) GetRoutes() []string { + if x != nil { + return x.Routes + } + return nil +} + +type EnableMachineRoutesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Routes *Routes `protobuf:"bytes,1,opt,name=routes,proto3" json:"routes,omitempty"` +} + +func (x *EnableMachineRoutesResponse) Reset() { + *x = EnableMachineRoutesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_routes_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EnableMachineRoutesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EnableMachineRoutesResponse) ProtoMessage() {} + +func (x *EnableMachineRoutesResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_routes_proto_msgTypes[4] + 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 EnableMachineRoutesResponse.ProtoReflect.Descriptor instead. +func (*EnableMachineRoutesResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_routes_proto_rawDescGZIP(), []int{4} +} + +func (x *EnableMachineRoutesResponse) GetRoutes() *Routes { + if x != nil { + return x.Routes + } + return nil +} + +var File_headscale_v1_routes_proto protoreflect.FileDescriptor + +var file_headscale_v1_routes_proto_rawDesc = []byte{ + 0x0a, 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, 0x12, 0x0c, 0x68, 0x65, 0x61, + 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x22, 0x5c, 0x0a, 0x06, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, 0x65, + 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, + 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, + 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x22, 0x37, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4d, 0x61, + 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 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, + 0x22, 0x47, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x68, 0x65, + 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x73, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x22, 0x53, 0x0a, 0x1a, 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, 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, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x22, 0x4b, + 0x0a, 0x1b, 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, 0x12, 0x2c, 0x0a, + 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x73, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 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_routes_proto_rawDescOnce sync.Once + file_headscale_v1_routes_proto_rawDescData = file_headscale_v1_routes_proto_rawDesc +) + +func file_headscale_v1_routes_proto_rawDescGZIP() []byte { + file_headscale_v1_routes_proto_rawDescOnce.Do(func() { + file_headscale_v1_routes_proto_rawDescData = protoimpl.X.CompressGZIP(file_headscale_v1_routes_proto_rawDescData) + }) + return file_headscale_v1_routes_proto_rawDescData +} + +var file_headscale_v1_routes_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_headscale_v1_routes_proto_goTypes = []interface{}{ + (*Routes)(nil), // 0: headscale.v1.Routes + (*GetMachineRouteRequest)(nil), // 1: headscale.v1.GetMachineRouteRequest + (*GetMachineRouteResponse)(nil), // 2: headscale.v1.GetMachineRouteResponse + (*EnableMachineRoutesRequest)(nil), // 3: headscale.v1.EnableMachineRoutesRequest + (*EnableMachineRoutesResponse)(nil), // 4: headscale.v1.EnableMachineRoutesResponse +} +var file_headscale_v1_routes_proto_depIdxs = []int32{ + 0, // 0: headscale.v1.GetMachineRouteResponse.routes:type_name -> headscale.v1.Routes + 0, // 1: headscale.v1.EnableMachineRoutesResponse.routes:type_name -> headscale.v1.Routes + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_headscale_v1_routes_proto_init() } +func file_headscale_v1_routes_proto_init() { + if File_headscale_v1_routes_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_headscale_v1_routes_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Routes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_routes_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetMachineRouteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_routes_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetMachineRouteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_routes_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EnableMachineRoutesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_routes_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EnableMachineRoutesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_headscale_v1_routes_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_headscale_v1_routes_proto_goTypes, + DependencyIndexes: file_headscale_v1_routes_proto_depIdxs, + MessageInfos: file_headscale_v1_routes_proto_msgTypes, + }.Build() + File_headscale_v1_routes_proto = out.File + file_headscale_v1_routes_proto_rawDesc = nil + file_headscale_v1_routes_proto_goTypes = nil + file_headscale_v1_routes_proto_depIdxs = nil +} diff --git a/gen/openapiv2/headscale/v1/device.swagger.json b/gen/openapiv2/headscale/v1/device.swagger.json new file mode 100644 index 00000000..5360527a --- /dev/null +++ b/gen/openapiv2/headscale/v1/device.swagger.json @@ -0,0 +1,43 @@ +{ + "swagger": "2.0", + "info": { + "title": "headscale/v1/device.proto", + "version": "version not set" + }, + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": {}, + "definitions": { + "protobufAny": { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "additionalProperties": {} + }, + "rpcStatus": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/protobufAny" + } + } + } + } + } +} diff --git a/gen/openapiv2/headscale/v1/headscale.swagger.json b/gen/openapiv2/headscale/v1/headscale.swagger.json new file mode 100644 index 00000000..2274e22c --- /dev/null +++ b/gen/openapiv2/headscale/v1/headscale.swagger.json @@ -0,0 +1,920 @@ +{ + "swagger": "2.0", + "info": { + "title": "headscale/v1/headscale.proto", + "version": "version not set" + }, + "tags": [ + { + "name": "HeadscaleService" + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/api/v1/debug/machine": { + "post": { + "summary": "--- Machine start ---", + "operationId": "HeadscaleService_DebugCreateMachine", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1DebugCreateMachineResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1DebugCreateMachineRequest" + } + } + ], + "tags": [ + "HeadscaleService" + ] + } + }, + "/api/v1/machine": { + "get": { + "operationId": "HeadscaleService_ListMachines", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1ListMachinesResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "HeadscaleService" + ] + } + }, + "/api/v1/machine/register": { + "post": { + "operationId": "HeadscaleService_RegisterMachine", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1RegisterMachineResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "HeadscaleService" + ] + } + }, + "/api/v1/machine/{machineId}": { + "get": { + "operationId": "HeadscaleService_GetMachine", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1GetMachineResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "machineId", + "in": "path", + "required": true, + "type": "string", + "format": "uint64" + } + ], + "tags": [ + "HeadscaleService" + ] + }, + "delete": { + "operationId": "HeadscaleService_DeleteMachine", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1DeleteMachineResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "machineId", + "in": "path", + "required": true, + "type": "string", + "format": "uint64" + } + ], + "tags": [ + "HeadscaleService" + ] + } + }, + "/api/v1/machine/{machineId}/expire": { + "post": { + "operationId": "HeadscaleService_ExpireMachine", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1ExpireMachineResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "machineId", + "in": "path", + "required": true, + "type": "string", + "format": "uint64" + } + ], + "tags": [ + "HeadscaleService" + ] + } + }, + "/api/v1/machine/{machineId}/routes": { + "get": { + "summary": "--- Route start ---", + "operationId": "HeadscaleService_GetMachineRoute", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1GetMachineRouteResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "machineId", + "in": "path", + "required": true, + "type": "string", + "format": "uint64" + } + ], + "tags": [ + "HeadscaleService" + ] + }, + "post": { + "operationId": "HeadscaleService_EnableMachineRoutes", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1EnableMachineRoutesResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "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", + "in": "path", + "required": true, + "type": "string", + "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" + } + ], + "tags": [ + "HeadscaleService" + ] + } + }, + "/api/v1/namespace": { + "get": { + "operationId": "HeadscaleService_ListNamespaces", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1ListNamespacesResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "HeadscaleService" + ] + }, + "post": { + "operationId": "HeadscaleService_CreateNamespace", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1CreateNamespaceResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1CreateNamespaceRequest" + } + } + ], + "tags": [ + "HeadscaleService" + ] + } + }, + "/api/v1/namespace/{name}": { + "get": { + "summary": "--- Namespace start ---", + "operationId": "HeadscaleService_GetNamespace", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1GetNamespaceResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "HeadscaleService" + ] + }, + "delete": { + "operationId": "HeadscaleService_DeleteNamespace", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1DeleteNamespaceResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "HeadscaleService" + ] + } + }, + "/api/v1/namespace/{oldName}/rename/{newName}": { + "post": { + "operationId": "HeadscaleService_RenameNamespace", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1RenameNamespaceResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "oldName", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "newName", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "HeadscaleService" + ] + } + }, + "/api/v1/preauthkey": { + "get": { + "operationId": "HeadscaleService_ListPreAuthKeys", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1ListPreAuthKeysResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "HeadscaleService" + ] + }, + "post": { + "summary": "--- PreAuthKeys start ---", + "operationId": "HeadscaleService_CreatePreAuthKey", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1CreatePreAuthKeyResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1CreatePreAuthKeyRequest" + } + } + ], + "tags": [ + "HeadscaleService" + ] + } + }, + "/api/v1/preauthkey/expire": { + "post": { + "operationId": "HeadscaleService_ExpirePreAuthKey", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1ExpirePreAuthKeyResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1ExpirePreAuthKeyRequest" + } + } + ], + "tags": [ + "HeadscaleService" + ] + } + } + }, + "definitions": { + "protobufAny": { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "additionalProperties": {} + }, + "rpcStatus": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/protobufAny" + } + } + } + }, + "v1CreateNamespaceRequest": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "v1CreateNamespaceResponse": { + "type": "object", + "properties": { + "namespace": { + "$ref": "#/definitions/v1Namespace" + } + } + }, + "v1CreatePreAuthKeyRequest": { + "type": "object", + "properties": { + "namespace": { + "type": "string" + }, + "reusable": { + "type": "boolean" + }, + "ephemeral": { + "type": "boolean" + }, + "expiration": { + "type": "string", + "format": "date-time" + } + } + }, + "v1CreatePreAuthKeyResponse": { + "type": "object", + "properties": { + "preAuthKey": { + "$ref": "#/definitions/v1PreAuthKey" + } + } + }, + "v1DebugCreateMachineRequest": { + "type": "object", + "properties": { + "namespace": { + "type": "string" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "routes": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "v1DebugCreateMachineResponse": { + "type": "object", + "properties": { + "machine": { + "$ref": "#/definitions/v1Machine" + } + } + }, + "v1DeleteMachineResponse": { + "type": "object" + }, + "v1DeleteNamespaceResponse": { + "type": "object" + }, + "v1EnableMachineRoutesResponse": { + "type": "object", + "properties": { + "routes": { + "$ref": "#/definitions/v1Routes" + } + } + }, + "v1ExpireMachineResponse": { + "type": "object", + "properties": { + "machine": { + "$ref": "#/definitions/v1Machine" + } + } + }, + "v1ExpirePreAuthKeyRequest": { + "type": "object", + "properties": { + "namespace": { + "type": "string" + }, + "key": { + "type": "string" + } + } + }, + "v1ExpirePreAuthKeyResponse": { + "type": "object" + }, + "v1GetMachineResponse": { + "type": "object", + "properties": { + "machine": { + "$ref": "#/definitions/v1Machine" + } + } + }, + "v1GetMachineRouteResponse": { + "type": "object", + "properties": { + "routes": { + "$ref": "#/definitions/v1Routes" + } + } + }, + "v1GetNamespaceResponse": { + "type": "object", + "properties": { + "namespace": { + "$ref": "#/definitions/v1Namespace" + } + } + }, + "v1ListMachinesResponse": { + "type": "object", + "properties": { + "machines": { + "type": "array", + "items": { + "$ref": "#/definitions/v1Machine" + } + } + } + }, + "v1ListNamespacesResponse": { + "type": "object", + "properties": { + "namespaces": { + "type": "array", + "items": { + "$ref": "#/definitions/v1Namespace" + } + } + } + }, + "v1ListPreAuthKeysResponse": { + "type": "object", + "properties": { + "preAuthKeys": { + "type": "array", + "items": { + "$ref": "#/definitions/v1PreAuthKey" + } + } + } + }, + "v1Machine": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uint64" + }, + "machineKey": { + "type": "string" + }, + "nodeKey": { + "type": "string" + }, + "discoKey": { + "type": "string" + }, + "ipAddress": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "$ref": "#/definitions/v1Namespace" + }, + "registered": { + "type": "boolean" + }, + "registerMethod": { + "$ref": "#/definitions/v1RegisterMethod" + }, + "lastSeen": { + "type": "string", + "format": "date-time" + }, + "lastSuccessfulUpdate": { + "type": "string", + "format": "date-time" + }, + "expiry": { + "type": "string", + "format": "date-time" + }, + "preAuthKey": { + "$ref": "#/definitions/v1PreAuthKey" + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + } + }, + "v1Namespace": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + } + }, + "v1PreAuthKey": { + "type": "object", + "properties": { + "namespace": { + "type": "string" + }, + "id": { + "type": "string" + }, + "key": { + "type": "string" + }, + "reusable": { + "type": "boolean" + }, + "ephemeral": { + "type": "boolean" + }, + "used": { + "type": "boolean" + }, + "expiration": { + "type": "string", + "format": "date-time" + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + } + }, + "v1RegisterMachineResponse": { + "type": "object", + "properties": { + "machine": { + "$ref": "#/definitions/v1Machine" + } + } + }, + "v1RegisterMethod": { + "type": "string", + "enum": [ + "REGISTER_METHOD_UNSPECIFIED", + "REGISTER_METHOD_AUTH_KEY", + "REGISTER_METHOD_CLI", + "REGISTER_METHOD_OIDC" + ], + "default": "REGISTER_METHOD_UNSPECIFIED" + }, + "v1RenameNamespaceResponse": { + "type": "object", + "properties": { + "namespace": { + "$ref": "#/definitions/v1Namespace" + } + } + }, + "v1Routes": { + "type": "object", + "properties": { + "advertisedRoutes": { + "type": "array", + "items": { + "type": "string" + } + }, + "enabledRoutes": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "v1ShareMachineResponse": { + "type": "object", + "properties": { + "machine": { + "$ref": "#/definitions/v1Machine" + } + } + }, + "v1UnshareMachineResponse": { + "type": "object", + "properties": { + "machine": { + "$ref": "#/definitions/v1Machine" + } + } + } + } +} diff --git a/gen/openapiv2/headscale/v1/machine.swagger.json b/gen/openapiv2/headscale/v1/machine.swagger.json new file mode 100644 index 00000000..714624b8 --- /dev/null +++ b/gen/openapiv2/headscale/v1/machine.swagger.json @@ -0,0 +1,43 @@ +{ + "swagger": "2.0", + "info": { + "title": "headscale/v1/machine.proto", + "version": "version not set" + }, + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": {}, + "definitions": { + "protobufAny": { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "additionalProperties": {} + }, + "rpcStatus": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/protobufAny" + } + } + } + } + } +} diff --git a/gen/openapiv2/headscale/v1/namespace.swagger.json b/gen/openapiv2/headscale/v1/namespace.swagger.json new file mode 100644 index 00000000..13687f0a --- /dev/null +++ b/gen/openapiv2/headscale/v1/namespace.swagger.json @@ -0,0 +1,43 @@ +{ + "swagger": "2.0", + "info": { + "title": "headscale/v1/namespace.proto", + "version": "version not set" + }, + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": {}, + "definitions": { + "protobufAny": { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "additionalProperties": {} + }, + "rpcStatus": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/protobufAny" + } + } + } + } + } +} diff --git a/gen/openapiv2/headscale/v1/preauthkey.swagger.json b/gen/openapiv2/headscale/v1/preauthkey.swagger.json new file mode 100644 index 00000000..ef16319c --- /dev/null +++ b/gen/openapiv2/headscale/v1/preauthkey.swagger.json @@ -0,0 +1,43 @@ +{ + "swagger": "2.0", + "info": { + "title": "headscale/v1/preauthkey.proto", + "version": "version not set" + }, + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": {}, + "definitions": { + "protobufAny": { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "additionalProperties": {} + }, + "rpcStatus": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/protobufAny" + } + } + } + } + } +} diff --git a/gen/openapiv2/headscale/v1/routes.swagger.json b/gen/openapiv2/headscale/v1/routes.swagger.json new file mode 100644 index 00000000..34eda676 --- /dev/null +++ b/gen/openapiv2/headscale/v1/routes.swagger.json @@ -0,0 +1,43 @@ +{ + "swagger": "2.0", + "info": { + "title": "headscale/v1/routes.proto", + "version": "version not set" + }, + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": {}, + "definitions": { + "protobufAny": { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "additionalProperties": {} + }, + "rpcStatus": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/protobufAny" + } + } + } + } + } +} diff --git a/go.mod b/go.mod index 65165e0d..8683c324 100644 --- a/go.mod +++ b/go.mod @@ -1,45 +1,138 @@ module github.com/juanfont/headscale -go 1.16 +go 1.17 require ( github.com/AlecAivazis/survey/v2 v2.3.2 - github.com/Microsoft/go-winio v0.5.0 // indirect - github.com/cenkalti/backoff/v4 v4.1.1 // indirect - github.com/containerd/continuity v0.1.0 // indirect - github.com/docker/cli v20.10.8+incompatible // indirect - github.com/docker/docker v20.10.8+incompatible // indirect + github.com/coreos/go-oidc/v3 v3.1.0 github.com/efekarakus/termcolor v1.0.1 - github.com/fatih/set v0.2.1 // indirect + github.com/fatih/set v0.2.1 github.com/gin-gonic/gin v1.7.4 - github.com/gofrs/uuid v4.0.0+incompatible - github.com/google/go-github v17.0.0+incompatible // indirect - github.com/google/go-querystring v1.1.0 // indirect - github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b - github.com/klauspost/compress v1.13.5 - github.com/lib/pq v1.10.3 // indirect - github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect - github.com/opencontainers/runc v1.0.2 // indirect + github.com/gofrs/uuid v4.1.0+incompatible + github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 + github.com/infobloxopen/protoc-gen-gorm v1.0.1 + github.com/klauspost/compress v1.13.6 github.com/ory/dockertest/v3 v3.7.0 + github.com/patrickmn/go-cache v2.1.0+incompatible + github.com/philip-bui/grpc-zerolog v1.0.1 github.com/prometheus/client_golang v1.11.0 github.com/pterm/pterm v0.12.30 - github.com/rs/zerolog v1.25.0 + github.com/rs/zerolog v1.26.0 + github.com/soheilhy/cmux v0.1.5 github.com/spf13/cobra v1.2.1 - github.com/spf13/viper v1.8.1 + github.com/spf13/viper v1.9.0 github.com/stretchr/testify v1.7.0 - github.com/tailscale/hujson v0.0.0-20210818175511-7360507a6e88 + github.com/tailscale/hujson v0.0.0-20211105212140-3a0adc019d83 github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e - github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/zsais/go-gin-prometheus v0.1.0 - golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 - golang.org/x/net v0.0.0-20210913180222-943fd674d43e // indirect - golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 // indirect + golang.org/x/crypto v0.0.0-20211202192323-5770296d904e + golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247 + google.golang.org/grpc v1.42.0 + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.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.2 gorm.io/driver/postgres v1.1.1 gorm.io/driver/sqlite v1.1.5 gorm.io/gorm v1.21.15 - inet.af/netaddr v0.0.0-20210903134321-85fa6c94624e - tailscale.com v1.14.2 + inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 + tailscale.com v1.20.3 +) + +require ( + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Microsoft/go-winio v0.5.1 // indirect + github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect + github.com/atomicgo/cursor v0.0.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.1.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/containerd/continuity v0.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/docker/cli v20.10.8+incompatible // indirect + github.com/docker/docker v20.10.8+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/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/validator/v10 v10.9.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/glog v1.0.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + 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/gookit/color v1.4.2 // indirect + github.com/hashicorp/go-version v1.2.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgconn v1.10.0 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.1.1 // indirect + github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect + github.com/jackc/pgtype v1.8.1 // indirect + github.com/jackc/pgx/v4 v4.13.0 // indirect + github.com/jinzhu/gorm v1.9.16 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/kr/pretty v0.3.0 // indirect + 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/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.8 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // 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 + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.0.2 // indirect + github.com/opencontainers/runc v1.0.3 // indirect + github.com/pelletier/go-toml v1.9.4 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + 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/rivo/uniseg v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 // indirect + github.com/sirupsen/logrus v1.8.1 // indirect + github.com/spf13/afero v1.6.0 // indirect + github.com/spf13/cast v1.4.1 // indirect + 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/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 + github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect + 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-20211205041911-012df41ee64c // indirect + golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/appengine v1.6.7 // indirect + gopkg.in/ini.v1 v1.66.2 // indirect + gopkg.in/square/go-jose.v2 v2.6.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index b429ca95..86642b87 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,3 @@ -4d63.com/gochecknoglobals v0.0.0-20201008074935-acfc0b28355a/go.mod h1:wfdC5ZjKSPr7CybKEcgJhUOgeAQW1+7WcyK8OvUilfo= bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= 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= @@ -20,6 +19,11 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -29,6 +33,7 @@ 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/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -38,6 +43,7 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +contrib.go.opencensus.io/exporter/ocagent v0.7.0/go.mod h1:IshRmMJBhDfFj5Y67nVhMYTTIze91RUeT73ipWKs/GY= 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= @@ -46,46 +52,33 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Djarvur/go-err113 v0.0.0-20200511133814-5174e21577d5/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= -github.com/Djarvur/go-err113 v0.1.0/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= 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/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 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/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= -github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +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/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/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= +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/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= 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/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= -github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE= 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= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= -github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +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= @@ -93,47 +86,48 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC 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-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +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 v1.38.52/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= 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= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= 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/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= -github.com/bombsimon/wsl/v3 v3.1.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= +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/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A= -github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= 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/census-instrumentation/opencensus-proto v0.2.1/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 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= 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= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/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= @@ -145,7 +139,8 @@ github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUX github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= +github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbrRw= +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= @@ -158,19 +153,18 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/daixiang0/gci v0.2.4/go.mod h1:+AV8KmHTGxxwp/pY84TLQfFKp2vuKXXJVzF3kD/hfR4= -github.com/daixiang0/gci v0.2.7/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc= -github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/denis-tingajkin/go-header v0.3.1/go.mod h1:sq/2IxMhaZX+RRcgHfCRx/m0M5na0fBt4/CRe7Lrji0= +github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +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/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= @@ -191,7 +185,6 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP 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/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= 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= @@ -199,34 +192,28 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= 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/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 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.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 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/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo= -github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/gliderlabs/ssh v0.3.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-critic/go-critic v0.5.2/go.mod h1:cc0+HvdE3lFpqLecgqMaJcvWWH77sLdBp+wLGPM1Yyo= -github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= -github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs= 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= @@ -237,38 +224,28 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb 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= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-multierror/multierror v1.0.2/go.mod h1:U7SZR/D9jHgt2nkSj8XcbCWdmVM2igraCHQ3HC1HiKY= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= -github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= +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-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= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= -github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= -github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= -github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= -github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= -github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= -github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= -github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= -github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= -github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus/v5 v5.0.4/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 h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= 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/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= @@ -278,6 +255,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 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/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= @@ -291,6 +270,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -311,25 +291,8 @@ 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/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= -github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0= -github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= -github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU= -github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= -github.com/golangci/golangci-lint v1.33.0/go.mod h1:zMnMLSCaDlrXExYsuq2LOweE9CHVqYk5jexk23UsjYM= -github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU= -github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= -github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= -github.com/golangci/misspell v0.3.5/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= -github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI= -github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= -github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= 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= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -347,12 +310,11 @@ github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4r github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/goexpect v0.0.0-20210430020637-ab937bf7fd6f/go.mod h1:n1ej5+FqyEytMt/mugVDZLIiqTMO+vsrgY+kM6ohzN0= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -364,52 +326,57 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/rpmpack v0.0.0-20201206194719-59e495f2b7e1/go.mod h1:+y9lKiqDhR4zkLl+V9h4q0rdyrYVsWWm6LLCQP33DIk= 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/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/gookit/color v1.3.1/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= 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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/goreleaser/chglog v0.1.2/go.mod h1:tTZsFuSZK4epDXfjMkxzcGbrIOXprf0JFp47BjIr3B8= -github.com/goreleaser/fileglob v0.3.1/go.mod h1:kNcPrPzjCp+Ox3jmXLU5QEsjhqrtLBm6OnXAif8KRl8= -github.com/goreleaser/nfpm v1.10.3/go.mod h1:EEC7YD5wi+ol0MiAshpgPANBOkjXDl7wqTLVk68OBsk= +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/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= -github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= -github.com/gostaticanalysis/analysisutil v0.1.0/go.mod h1:dMhHRU9KTiDcuLGdy87/2gTR8WruwYZrKdRq9m1O6uw= -github.com/gostaticanalysis/analysisutil v0.6.1/go.mod h1:18U/DLpRgIUd459wGxVHE0fRgmo1UgHDcbw7F5idXu0= -github.com/gostaticanalysis/comment v1.3.0/go.mod h1:xMicKDx7XRXYdVwY9f9wQpDJVnqWxw9wCauCMKp+IBI= -github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= 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/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b h1:wDUNC2eKiL35DbLvsDhiblTUXHxcOPwQSCzi7xpQUN4= -github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0= +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/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/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.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 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= @@ -423,26 +390,25 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= 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/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= -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= -github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 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/insomniacslk/dhcp v0.0.0-20210621130208-1cac67f12b1e/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= -github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= +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/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= @@ -467,7 +433,6 @@ 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= @@ -505,67 +470,50 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f 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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jgautheron/goconst v0.0.0-20201117150253-ccae5bf973f3/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= -github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s= +github.com/jhump/protoreflect v1.8.1/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= +github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= +github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 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/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= -github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= -github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= -github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= -github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= -github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw= -github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs= -github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA= -github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U= -github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo= 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 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 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.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.5 h1:9O69jUPDcsT9fEm74W92rZL9FQY7rCdaXVneq+yyzl4= -github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +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/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= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= @@ -573,39 +521,34 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kunwardeep/paralleltest v1.0.2/go.mod h1:ZPqNm1fVHPllh5LPVujzbVz1JN2GhLxSfY+oqUsvG30= -github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 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/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= -github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= 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.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU= -github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= -github.com/matoous/godox v0.0.0-20200801072554-4fb83dc2941e/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= 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= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +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= @@ -614,56 +557,36 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd 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 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 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.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +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/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= 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/mbilski/exhaustivestruct v1.1.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= -github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= -github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo= -github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= -github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= -github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= -github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= -github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= -github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8= -github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= -github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= -github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys= -github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8= -github.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q= -github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/mdlayher/sdnotify v0.0.0-20210228150836-ea3ec207d697/go.mod h1:HtjVsQfsrBm1GDcDTUFn4ZXhftxTwO/hxrvEiRc61U4= -github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc= 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/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.42/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +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/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.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +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= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= @@ -672,14 +595,12 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8qUplsoSU4k= -github.com/mozilla/tls-observatory v0.0.0-20200317151703-4fa42e1c2dee/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= +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/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/nakabonne/nestif v0.3.0/go.mod h1:dI314BppzXjJ4HsCnbo7XzrJHPszZsjnk5wEBSYHI2c= 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= @@ -687,31 +608,24 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE 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/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nishanths/exhaustive v0.1.0/go.mod h1:S1j9110vxV1ECdCudXRkeMnFQ/DQk9ajLT0Uf2MYZQQ= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +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/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 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 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= 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 h1:opHZMaswlyxz1OuGpBE53Dwe4/xF7EZTY0A2L/FpCOg= -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/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/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= @@ -726,32 +640,30 @@ github.com/ory/dockertest/v3 v3.7.0 h1:Bijzonc69Ont3OU0a3TWKJ1Rzlh3TsDXP1JrTAkSm github.com/ory/dockertest/v3 v3.7.0/go.mod h1:PvCCgnP7AfBZeVrzwiUTjZx/IUXlGLC1zQlUQrLIlUE= 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/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk= +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.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= 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/peterbourgon/ff/v2 v2.0.0/go.mod h1:xjwr+t+SjWm4L46fcj/D+Ap+6ME7+HqFzaP22pP5Ggk= -github.com/peterbourgon/ff/v3 v3.0.0/go.mod h1:UILIFjRH5a/ar8TjXYLTkIvSvekZqPm5Eb/qbGk6CT0= -github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= +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/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28= +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.0/go.mod h1:41g+FIPlQUTDCveupEmEA65IoiQFrtgCeDopC4ajGIM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polyfloyd/go-errorlint v0.0.0-20201006195004-351e25ade6e3/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw= -github.com/polyfloyd/go-errorlint v0.0.0-20201127212506-19bd8db6546f/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw= 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= @@ -773,90 +685,76 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 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.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= 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= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 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/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= -github.com/quasilyte/go-ruleguard v0.2.0/go.mod h1:2RT/tf0Ce0UDj5y243iWKosQogJd8+1G3Rs2fxmlYnw= -github.com/quasilyte/go-ruleguard v0.2.1/go.mod h1:hN2rVc/uS4bQhQKTio2XaSJSafJwqBUWWwtssT3cQmc= -github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= -github.com/quasilyte/regex/syntax v0.0.0-20200805063351-8f842688393c/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= 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= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.6.2/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/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.25.0 h1:Rj7XygbUHKUlDPcVdoLyR91fJBsduXj5fRxyqIQj/II= -github.com/rs/zerolog v1.25.0/go.mod h1:7KHcEGe0QZPOm2IE4Kpb5rTh6n1h2hIgS5OOnu1rUaI= +github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE= +github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryancurrah/gomodguard v1.1.0/go.mod h1:4O8tr7hBODaGE6VIhfJDHcwzh5GUccKSJBU0UMXJFVM= -github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA= 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/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I= 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/securego/gosec/v2 v2.5.0/go.mod h1:L/CDXVntIff5ypVHIkqPXbtRpJiNCh6c6Amn68jXDjo= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= -github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc= -github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= 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/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= 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/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= +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/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= 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.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= 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.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= +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/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -868,17 +766,16 @@ 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.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/ssgreg/nlreturn/v2 v2.1.0/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= +github.com/spf13/viper v1.9.0 h1:yR6EXjTp0y0cLN8OZg1CRZmOBdI88UcGkhgyJhu6nZk= +github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= 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= -github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -890,47 +787,23 @@ 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/certstore v0.0.0-20210528134328-066c94b793d3/go.mod h1:2P+hpOwd53e7JMX/L4f3VXkv1G+33ES6IWZSrkIeWNs= -github.com/tailscale/depaware v0.0.0-20201214215404-77d1e9757027/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8= -github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8= -github.com/tailscale/hujson v0.0.0-20200924210142-dde312d0d6a2/go.mod h1:STqf+YV0ADdzk4ejtXFsGqDpATP9JoL0OB+hiFQbkdE= -github.com/tailscale/hujson v0.0.0-20210818175511-7360507a6e88 h1:q5Sxx79nhG4xWsYEJBlLdqo1hNhUV31/NhA4qQ1SKAY= -github.com/tailscale/hujson v0.0.0-20210818175511-7360507a6e88/go.mod h1:iTDXJsA6A2wNNjurgic2rk+is6uzU4U2NLm4T+edr6M= -github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8= +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/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/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= -github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= -github.com/tetafro/godot v1.3.0/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0= -github.com/tetafro/godot v1.3.2/go.mod h1:ah7jjYmOMnIjS9ku2krapvGQrFNtTLo9Z/qB3dGU1eU= -github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= -github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= 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/tomarrell/wrapcheck v0.0.0-20200807122107-df9e8bcb914d/go.mod h1:yiFB6fFoV7saXirUGfuK+cPtUh4NX/Hf5y2WC2lehu0= -github.com/tomarrell/wrapcheck v0.0.0-20201130113247-1683564d9756/go.mod h1:yiFB6fFoV7saXirUGfuK+cPtUh4NX/Hf5y2WC2lehu0= -github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig= -github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM= -github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= +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 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= -github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= +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/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= -github.com/valyala/quicktemplate v1.6.3/go.mod h1:fwPzK2fHuYEODzJ9pkw0ipCPNHZ2tD5KW4lOuSdPKzY= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= 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= -github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= -github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -938,7 +811,6 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= @@ -948,8 +820,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b/go.mod h1:IZpXDfkJ6tWD3PhBK5YzgQT+xJWh7OsdwiG8hA2MkO4= 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= @@ -966,7 +838,9 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -980,17 +854,16 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go4.org/intern v0.0.0-20210108033219-3eb7198706b2 h1:VFTf+jjIgsldaz/Mr00VaCSswHJrI2hIjQygE/W4IMg= -go4.org/intern v0.0.0-20210108033219-3eb7198706b2/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc= -go4.org/mem v0.0.0-20201119185036-c04c5a6ff174 h1:vSug/WNOi2+4jrKdivxayTN/zd8EA1UrStjpWvvo1jk= -go4.org/mem v0.0.0-20201119185036-c04c5a6ff174/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 h1:1tk03FUNpulq2cuWpXZWj649rwJpk0d20rxWiopKRmc= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= +go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE= +go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA= +go4.org/mem v0.0.0-20210711025021-927187094b94 h1:OAAkygi2Js191AJP1Ds42MhJRgeofeKGjuoUqNp1QC4= +go4.org/mem v0.0.0-20210711025021-927187094b94/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 h1:Tx9kY6yUkLge/pFG7IEMwDZy6CS2ajFc9TvQdPCW0uA= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= @@ -1000,21 +873,18 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U 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-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +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-20200728195943-123391ffb6de/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-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/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-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 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/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/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= @@ -1025,6 +895,7 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1050,6 +921,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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= @@ -1062,7 +934,6 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r 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= -golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= @@ -1072,8 +943,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1081,30 +952,30 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 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-20201216054612-986b41b23924/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= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +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-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210913180222-943fd674d43e h1:+b/22bPvDYt4NPDcy4xAGCmON713ONAWFeY3Z7I3tR8= -golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/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/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= @@ -1117,6 +988,13 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ 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-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 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= @@ -1127,6 +1005,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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= @@ -1137,32 +1016,27 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h 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-20190221075227-b4e8571b14e0/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= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/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-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/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= @@ -1170,18 +1044,18 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1189,24 +1063,12 @@ golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201109165425-215b40eba54c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210301091718-77cc2087c03b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1215,19 +1077,25 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 h1:xrCZDmdtoloIiooiA9q0OQb9r8HejIHYoHGhGCe1pGg= -golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/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-20210927094055-39ccf1dd6fa6/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/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= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1236,28 +1104,20 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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-0.20210524175448-3115f89c4b99/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 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-20210611083556-38a9dc6acbc6/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-20180525024113-a5b4c53f6e8b/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-20190110163146-51295c7ec13a/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-20190221204921-83362c3779f5/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190307163923-6a08e3108db3/go.mod h1:25r3+/G6/xytQM8iWZKq3Hn0kr0rgFKPUNVEL/dr3z4= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -1269,12 +1129,11 @@ golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1285,7 +1144,6 @@ golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1295,45 +1153,30 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200622203043-20e05c1c8ffa/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200624225443-88f3c62a19ff/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200625211823-6506e20df31f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200812195022-5ae4c3c160a0/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200831203904-5a2aa26beb65/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201001104356-43ebab892c4c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201011145850-ed2f50202694/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201013201025-64a9e34f3752/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201118003311-bd56c0adb394/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201121010211-780cb80bd7fb/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-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1341,8 +1184,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= -golang.zx2c4.com/wireguard v0.0.0-20210624150102-15b24b6179e0/go.mod h1:laHzsbfMhGSobUmruXWAyMKKHSqvIcrqZJMyHD+/3O8= -golang.zx2c4.com/wireguard/windows v0.3.16/go.mod h1:f80rkFY2CKQklps1GHE15k/M4Tq78aofbr1iQM5MTVY= 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= @@ -1357,6 +1198,7 @@ google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/ google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.25.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= @@ -1366,6 +1208,12 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR 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= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= 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= @@ -1373,6 +1221,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1396,26 +1245,46 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210224155714-063164c882e6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210426193834-eac7f76ac494/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +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/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1437,10 +1306,23 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0-dev.0.20201218190559-666aea1fb34c/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +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.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A= +google.golang.org/grpc v1.42.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/examples v0.0.0-20210309220351-d5b628860d4e/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE= +google.golang.org/grpc/examples v0.0.0-20210601155443-8bdcb4c9ab8d/go.mod h1:bF8wuZSAZTcbF7ZPKrDI/qY52toTP/yxLpRRY4Eu9Js= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1451,14 +1333,16 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.25.1-0.20201208041424-160c7477e0e8/go.mod h1:hFxJC2f0epmp1elRCiEGJTKAWbwxZ2nvqZdHl3FQXCY= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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= @@ -1467,9 +1351,14 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy 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 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= 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/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= @@ -1483,7 +1372,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 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-20200615113413-eeeca48fe776/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= @@ -1514,24 +1402,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.6/go.mod h1:pyyisuGw24ruLjrr1ddx39WE0y9OooInRzEYLhQB2YY= -honnef.co/go/tools v0.1.4/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -inet.af/netaddr v0.0.0-20210515010201-ad03edc7c841/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls= -inet.af/netaddr v0.0.0-20210721214506-ce7a8ad02cc1/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls= -inet.af/netaddr v0.0.0-20210903134321-85fa6c94624e h1:tvgqez5ZQoBBiBAGNU/fmJy247yB/7++kcLOEoMYup0= -inet.af/netaddr v0.0.0-20210903134321-85fa6c94624e/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls= -inet.af/netstack v0.0.0-20210622165351-29b14ebc044e/go.mod h1:fG3G1dekmK8oDX3iVzt8c0zICLMLSN8SjdxbXVt0WjU= -inet.af/peercred v0.0.0-20210318190834-4259e17bb763/go.mod h1:FjawnflS/udxX+SvpsMgZfdqx2aykOlkISeAsADi5IU= -inet.af/wf v0.0.0-20210516214145-a5343001b756/go.mod h1:ViGMZRA6+RA318D7GCncrjv5gHUrPYrNDejjU12tikA= -mvdan.cc/gofumpt v0.0.0-20200802201014-ab5a8192947d/go.mod h1:bzrjFmaD6+xqohD3KYP0H2FEuxknnBmyyOxdhLdaIws= -mvdan.cc/gofumpt v0.0.0-20201129102820-5c11c50e9475/go.mod h1:E4LOcu9JQEtnYXtB1Y51drqh2Qr2Ngk9J3YrRCwcbd0= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7/go.mod h1:HGC5lll35J70Y5v7vCGb9oLhHoScFwkHDJm/05RdSTc= +inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 h1:acCzuUSQ79tGsM/O50VRFySfMm19IoMKL+sZztZkCxw= +inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6/go.mod h1:y3MGhcFMlh0KZPMuXXow8mpjxxAk3yoDNsp4cQz54i8= 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= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= -tailscale.com v1.14.2 h1:iSbnr+3eVt0ZsRJQG+KLw46K8Hq2fbCMSH/dGoJXnkY= -tailscale.com v1.14.2/go.mod h1:WbJAb2AwrBO8jGQgxZ8E2mj3l628a3ysdPV7bJHBKlY= +tailscale.com v1.20.3 h1:C3g2AgmQaOi0YT5dAal9mslugPXMxwj0EXY7YfL2QrA= +tailscale.com v1.20.3/go.mod h1:kjVy3ji2OH5lZhPLIIRacoY3CN4Bo3Yyb2mtoM8nfJ4= diff --git a/grpcv1.go b/grpcv1.go new file mode 100644 index 00000000..1850ce7a --- /dev/null +++ b/grpcv1.go @@ -0,0 +1,407 @@ +//nolint +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" +) + +type headscaleV1APIServer struct { // v1.HeadscaleServiceServer + v1.UnimplementedHeadscaleServiceServer + h *Headscale +} + +func newHeadscaleV1APIServer(h *Headscale) v1.HeadscaleServiceServer { + return headscaleV1APIServer{ + h: h, + } +} + +func (api headscaleV1APIServer) GetNamespace( + ctx context.Context, + request *v1.GetNamespaceRequest, +) (*v1.GetNamespaceResponse, error) { + namespace, err := api.h.GetNamespace(request.GetName()) + if err != nil { + return nil, err + } + + return &v1.GetNamespaceResponse{Namespace: namespace.toProto()}, nil +} + +func (api headscaleV1APIServer) CreateNamespace( + ctx context.Context, + request *v1.CreateNamespaceRequest, +) (*v1.CreateNamespaceResponse, error) { + namespace, err := api.h.CreateNamespace(request.GetName()) + if err != nil { + return nil, err + } + + return &v1.CreateNamespaceResponse{Namespace: namespace.toProto()}, nil +} + +func (api headscaleV1APIServer) RenameNamespace( + ctx context.Context, + request *v1.RenameNamespaceRequest, +) (*v1.RenameNamespaceResponse, error) { + err := api.h.RenameNamespace(request.GetOldName(), request.GetNewName()) + if err != nil { + return nil, err + } + + namespace, err := api.h.GetNamespace(request.GetNewName()) + if err != nil { + return nil, err + } + + return &v1.RenameNamespaceResponse{Namespace: namespace.toProto()}, nil +} + +func (api headscaleV1APIServer) DeleteNamespace( + ctx context.Context, + request *v1.DeleteNamespaceRequest, +) (*v1.DeleteNamespaceResponse, error) { + err := api.h.DestroyNamespace(request.GetName()) + if err != nil { + return nil, err + } + + return &v1.DeleteNamespaceResponse{}, nil +} + +func (api headscaleV1APIServer) ListNamespaces( + ctx context.Context, + request *v1.ListNamespacesRequest, +) (*v1.ListNamespacesResponse, error) { + namespaces, err := api.h.ListNamespaces() + if err != nil { + return nil, err + } + + response := make([]*v1.Namespace, len(namespaces)) + for index, namespace := range namespaces { + response[index] = namespace.toProto() + } + + log.Trace().Caller().Interface("namespaces", response).Msg("") + + return &v1.ListNamespacesResponse{Namespaces: response}, nil +} + +func (api headscaleV1APIServer) CreatePreAuthKey( + ctx context.Context, + request *v1.CreatePreAuthKeyRequest, +) (*v1.CreatePreAuthKeyResponse, error) { + var expiration time.Time + if request.GetExpiration() != nil { + expiration = request.GetExpiration().AsTime() + } + + preAuthKey, err := api.h.CreatePreAuthKey( + request.GetNamespace(), + request.GetReusable(), + request.GetEphemeral(), + &expiration, + ) + if err != nil { + return nil, err + } + + return &v1.CreatePreAuthKeyResponse{PreAuthKey: preAuthKey.toProto()}, nil +} + +func (api headscaleV1APIServer) ExpirePreAuthKey( + ctx context.Context, + request *v1.ExpirePreAuthKeyRequest, +) (*v1.ExpirePreAuthKeyResponse, error) { + preAuthKey, err := api.h.GetPreAuthKey(request.GetNamespace(), request.Key) + if err != nil { + return nil, err + } + + err = api.h.ExpirePreAuthKey(preAuthKey) + if err != nil { + return nil, err + } + + return &v1.ExpirePreAuthKeyResponse{}, nil +} + +func (api headscaleV1APIServer) ListPreAuthKeys( + ctx context.Context, + request *v1.ListPreAuthKeysRequest, +) (*v1.ListPreAuthKeysResponse, error) { + preAuthKeys, err := api.h.ListPreAuthKeys(request.GetNamespace()) + if err != nil { + return nil, err + } + + response := make([]*v1.PreAuthKey, len(preAuthKeys)) + for index, key := range preAuthKeys { + response[index] = key.toProto() + } + + return &v1.ListPreAuthKeysResponse{PreAuthKeys: response}, nil +} + +func (api headscaleV1APIServer) RegisterMachine( + ctx context.Context, + request *v1.RegisterMachineRequest, +) (*v1.RegisterMachineResponse, error) { + log.Trace(). + Str("namespace", request.GetNamespace()). + Str("machine_key", request.GetKey()). + Msg("Registering machine") + machine, err := api.h.RegisterMachine( + request.GetKey(), + request.GetNamespace(), + ) + if err != nil { + return nil, err + } + + return &v1.RegisterMachineResponse{Machine: machine.toProto()}, nil +} + +func (api headscaleV1APIServer) GetMachine( + ctx context.Context, + request *v1.GetMachineRequest, +) (*v1.GetMachineResponse, error) { + machine, err := api.h.GetMachineByID(request.GetMachineId()) + if err != nil { + return nil, err + } + + return &v1.GetMachineResponse{Machine: machine.toProto()}, nil +} + +func (api headscaleV1APIServer) DeleteMachine( + ctx context.Context, + request *v1.DeleteMachineRequest, +) (*v1.DeleteMachineResponse, error) { + machine, err := api.h.GetMachineByID(request.GetMachineId()) + if err != nil { + return nil, err + } + + err = api.h.DeleteMachine( + machine, + ) + if err != nil { + return nil, err + } + + return &v1.DeleteMachineResponse{}, nil +} + +func (api headscaleV1APIServer) ExpireMachine( + ctx context.Context, + request *v1.ExpireMachineRequest, +) (*v1.ExpireMachineResponse, error) { + machine, err := api.h.GetMachineByID(request.GetMachineId()) + if err != nil { + return nil, err + } + + api.h.ExpireMachine( + machine, + ) + + log.Trace(). + Str("machine", machine.Name). + Time("expiry", *machine.Expiry). + Msg("machine expired") + + return &v1.ExpireMachineResponse{Machine: machine.toProto()}, nil +} + +func (api headscaleV1APIServer) ListMachines( + ctx context.Context, + request *v1.ListMachinesRequest, +) (*v1.ListMachinesResponse, error) { + if request.GetNamespace() != "" { + machines, err := api.h.ListMachinesInNamespace(request.GetNamespace()) + if err != nil { + 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() + } + + return &v1.ListMachinesResponse{Machines: response}, nil + } + + machines, err := api.h.ListMachines() + if err != nil { + return nil, err + } + + response := make([]*v1.Machine, len(machines)) + for index, machine := range machines { + response[index] = machine.toProto() + } + + 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, +) (*v1.GetMachineRouteResponse, error) { + machine, err := api.h.GetMachineByID(request.GetMachineId()) + if err != nil { + return nil, err + } + + routes, err := machine.RoutesToProto() + if err != nil { + return nil, err + } + + return &v1.GetMachineRouteResponse{ + Routes: routes, + }, nil +} + +func (api headscaleV1APIServer) EnableMachineRoutes( + ctx context.Context, + request *v1.EnableMachineRoutesRequest, +) (*v1.EnableMachineRoutesResponse, error) { + machine, err := api.h.GetMachineByID(request.GetMachineId()) + if err != nil { + return nil, err + } + + err = api.h.EnableRoutes(machine, request.GetRoutes()...) + if err != nil { + return nil, err + } + + routes, err := machine.RoutesToProto() + if err != nil { + return nil, err + } + + return &v1.EnableMachineRoutesResponse{ + Routes: routes, + }, nil +} + +// The following service calls are for testing and debugging +func (api headscaleV1APIServer) DebugCreateMachine( + ctx context.Context, + request *v1.DebugCreateMachineRequest, +) (*v1.DebugCreateMachineResponse, error) { + namespace, err := api.h.GetNamespace(request.GetNamespace()) + if err != nil { + return nil, err + } + + routes, err := stringToIPPrefix(request.GetRoutes()) + if err != nil { + return nil, err + } + + log.Trace(). + Caller(). + Interface("route-prefix", routes). + Interface("route-str", request.GetRoutes()). + Msg("") + + hostinfo := tailcfg.Hostinfo{ + RoutableIPs: routes, + OS: "TestOS", + 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(), + Namespace: *namespace, + + Expiry: &time.Time{}, + LastSeen: &time.Time{}, + LastSuccessfulUpdate: &time.Time{}, + + HostInfo: datatypes.JSON(hostinfoJson), + } + + // log.Trace().Caller().Interface("machine", newMachine).Msg("") + + if err := api.h.db.Create(&newMachine).Error; err != nil { + return nil, err + } + + return &v1.DebugCreateMachineResponse{Machine: newMachine.toProto()}, nil +} + +func (api headscaleV1APIServer) mustEmbedUnimplementedHeadscaleServiceServer() {} diff --git a/integration_cli_test.go b/integration_cli_test.go new file mode 100644 index 00000000..0278da0d --- /dev/null +++ b/integration_cli_test.go @@ -0,0 +1,1195 @@ +//go:build integration +// +build integration + +package headscale + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "os" + "testing" + "time" + + v1 "github.com/juanfont/headscale/gen/go/headscale/v1" + "github.com/ory/dockertest/v3" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type IntegrationCLITestSuite struct { + suite.Suite + stats *suite.SuiteInformation + + pool dockertest.Pool + network dockertest.Network + headscale dockertest.Resource + env []string +} + +func TestCLIIntegrationTestSuite(t *testing.T) { + s := new(IntegrationCLITestSuite) + + suite.Run(t, s) +} + +func (s *IntegrationCLITestSuite) SetupTest() { + var err error + + if ppool, err := dockertest.NewPool(""); err == nil { + s.pool = *ppool + } else { + log.Fatalf("Could not connect to docker: %s", err) + } + + if pnetwork, err := s.pool.CreateNetwork("headscale-test"); err == nil { + s.network = *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: "headscale", + Mounts: []string{ + fmt.Sprintf("%s/integration_test/etc:/etc/headscale", currentPath), + }, + Networks: []*dockertest.Network{&s.network}, + Cmd: []string{"headscale", "serve"}, + } + + fmt.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") + + fmt.Println("Waiting for headscale to be ready") + hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8080/tcp")) + + if err := s.pool.Retry(func() error { + url := fmt.Sprintf("http://%s/health", hostEndpoint) + resp, err := http.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) + } + fmt.Println("headscale container is ready") +} + +func (s *IntegrationCLITestSuite) TearDownTest() { + if err := s.pool.Purge(&s.headscale); err != nil { + log.Printf("Could not purge resource: %s\n", err) + } + + if err := s.network.Close(); err != nil { + log.Printf("Could not close network: %s\n", err) + } +} + +func (s *IntegrationCLITestSuite) HandleStats( + suiteName string, + stats *suite.SuiteInformation, +) { + s.stats = stats +} + +func (s *IntegrationCLITestSuite) createNamespace(name string) (*v1.Namespace, error) { + result, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "namespaces", + "create", + name, + "--output", + "json", + }, + []string{}, + ) + if err != nil { + return nil, err + } + + var namespace v1.Namespace + err = json.Unmarshal([]byte(result), &namespace) + if err != nil { + return nil, err + } + + return &namespace, nil +} + +func (s *IntegrationCLITestSuite) TestNamespaceCommand() { + names := []string{"namespace1", "otherspace", "tasty"} + namespaces := make([]*v1.Namespace, len(names)) + + for index, namespaceName := range names { + namespace, err := s.createNamespace(namespaceName) + assert.Nil(s.T(), err) + + namespaces[index] = namespace + } + + assert.Len(s.T(), namespaces, len(names)) + + assert.Equal(s.T(), names[0], namespaces[0].Name) + assert.Equal(s.T(), names[1], namespaces[1].Name) + assert.Equal(s.T(), names[2], namespaces[2].Name) + + // Test list namespaces + listResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "namespaces", + "list", + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var listedNamespaces []v1.Namespace + err = json.Unmarshal([]byte(listResult), &listedNamespaces) + assert.Nil(s.T(), err) + + assert.Equal(s.T(), names[0], listedNamespaces[0].Name) + assert.Equal(s.T(), names[1], listedNamespaces[1].Name) + assert.Equal(s.T(), names[2], listedNamespaces[2].Name) + + // Test rename namespace + renameResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "namespaces", + "rename", + "--output", + "json", + "tasty", + "newname", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var renamedNamespace v1.Namespace + err = json.Unmarshal([]byte(renameResult), &renamedNamespace) + assert.Nil(s.T(), err) + + assert.Equal(s.T(), renamedNamespace.Name, "newname") + + // Test list after rename namespaces + listAfterRenameResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "namespaces", + "list", + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var listedAfterRenameNamespaces []v1.Namespace + err = json.Unmarshal([]byte(listAfterRenameResult), &listedAfterRenameNamespaces) + assert.Nil(s.T(), err) + + assert.Equal(s.T(), names[0], listedAfterRenameNamespaces[0].Name) + assert.Equal(s.T(), names[1], listedAfterRenameNamespaces[1].Name) + assert.Equal(s.T(), "newname", listedAfterRenameNamespaces[2].Name) +} + +func (s *IntegrationCLITestSuite) TestPreAuthKeyCommand() { + count := 5 + + namespace, err := s.createNamespace("pre-auth-key-namespace") + + keys := make([]*v1.PreAuthKey, count) + assert.Nil(s.T(), err) + + for i := 0; i < count; i++ { + preAuthResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "preauthkeys", + "--namespace", + namespace.Name, + "create", + "--reusable", + "--expiration", + "24h", + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var preAuthKey v1.PreAuthKey + err = json.Unmarshal([]byte(preAuthResult), &preAuthKey) + assert.Nil(s.T(), err) + + keys[i] = &preAuthKey + } + + assert.Len(s.T(), keys, 5) + + // Test list of keys + listResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "preauthkeys", + "--namespace", + namespace.Name, + "list", + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var listedPreAuthKeys []v1.PreAuthKey + err = json.Unmarshal([]byte(listResult), &listedPreAuthKeys) + assert.Nil(s.T(), err) + + assert.Equal(s.T(), "1", listedPreAuthKeys[0].Id) + assert.Equal(s.T(), "2", listedPreAuthKeys[1].Id) + assert.Equal(s.T(), "3", listedPreAuthKeys[2].Id) + assert.Equal(s.T(), "4", listedPreAuthKeys[3].Id) + assert.Equal(s.T(), "5", listedPreAuthKeys[4].Id) + + assert.NotEmpty(s.T(), listedPreAuthKeys[0].Key) + assert.NotEmpty(s.T(), listedPreAuthKeys[1].Key) + assert.NotEmpty(s.T(), listedPreAuthKeys[2].Key) + assert.NotEmpty(s.T(), listedPreAuthKeys[3].Key) + assert.NotEmpty(s.T(), listedPreAuthKeys[4].Key) + + assert.True(s.T(), listedPreAuthKeys[0].Expiration.AsTime().After(time.Now())) + assert.True(s.T(), listedPreAuthKeys[1].Expiration.AsTime().After(time.Now())) + assert.True(s.T(), listedPreAuthKeys[2].Expiration.AsTime().After(time.Now())) + assert.True(s.T(), listedPreAuthKeys[3].Expiration.AsTime().After(time.Now())) + assert.True(s.T(), listedPreAuthKeys[4].Expiration.AsTime().After(time.Now())) + + assert.True( + s.T(), + listedPreAuthKeys[0].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), + ) + assert.True( + s.T(), + listedPreAuthKeys[1].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), + ) + assert.True( + s.T(), + listedPreAuthKeys[2].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), + ) + assert.True( + s.T(), + listedPreAuthKeys[3].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), + ) + assert.True( + s.T(), + listedPreAuthKeys[4].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), + ) + + // Expire three keys + for i := 0; i < 3; i++ { + _, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "preauthkeys", + "--namespace", + namespace.Name, + "expire", + listedPreAuthKeys[i].Key, + }, + []string{}, + ) + assert.Nil(s.T(), err) + } + + // Test list pre auth keys after expire + listAfterExpireResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "preauthkeys", + "--namespace", + namespace.Name, + "list", + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var listedAfterExpirePreAuthKeys []v1.PreAuthKey + err = json.Unmarshal([]byte(listAfterExpireResult), &listedAfterExpirePreAuthKeys) + assert.Nil(s.T(), err) + + assert.True( + s.T(), + listedAfterExpirePreAuthKeys[0].Expiration.AsTime().Before(time.Now()), + ) + assert.True( + s.T(), + listedAfterExpirePreAuthKeys[1].Expiration.AsTime().Before(time.Now()), + ) + assert.True( + s.T(), + listedAfterExpirePreAuthKeys[2].Expiration.AsTime().Before(time.Now()), + ) + assert.True( + s.T(), + listedAfterExpirePreAuthKeys[3].Expiration.AsTime().After(time.Now()), + ) + assert.True( + s.T(), + listedAfterExpirePreAuthKeys[4].Expiration.AsTime().After(time.Now()), + ) +} + +func (s *IntegrationCLITestSuite) TestPreAuthKeyCommandWithoutExpiry() { + namespace, err := s.createNamespace("pre-auth-key-without-exp-namespace") + assert.Nil(s.T(), err) + + preAuthResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "preauthkeys", + "--namespace", + namespace.Name, + "create", + "--reusable", + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var preAuthKey v1.PreAuthKey + err = json.Unmarshal([]byte(preAuthResult), &preAuthKey) + assert.Nil(s.T(), err) + + // Test list of keys + listResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "preauthkeys", + "--namespace", + namespace.Name, + "list", + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var listedPreAuthKeys []v1.PreAuthKey + err = json.Unmarshal([]byte(listResult), &listedPreAuthKeys) + assert.Nil(s.T(), err) + + assert.Len(s.T(), listedPreAuthKeys, 1) + + assert.True(s.T(), listedPreAuthKeys[0].Expiration.AsTime().After(time.Now())) + assert.True( + s.T(), + listedPreAuthKeys[0].Expiration.AsTime().Before(time.Now().Add(time.Minute*70)), + ) +} + +func (s *IntegrationCLITestSuite) TestPreAuthKeyCommandReusableEphemeral() { + namespace, err := s.createNamespace("pre-auth-key-reus-ephm-namespace") + assert.Nil(s.T(), err) + + preAuthReusableResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "preauthkeys", + "--namespace", + namespace.Name, + "create", + "--reusable=true", + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var preAuthReusableKey v1.PreAuthKey + err = json.Unmarshal([]byte(preAuthReusableResult), &preAuthReusableKey) + assert.Nil(s.T(), err) + + assert.True(s.T(), preAuthReusableKey.GetReusable()) + assert.False(s.T(), preAuthReusableKey.GetEphemeral()) + + preAuthEphemeralResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "preauthkeys", + "--namespace", + namespace.Name, + "create", + "--ephemeral=true", + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var preAuthEphemeralKey v1.PreAuthKey + err = json.Unmarshal([]byte(preAuthEphemeralResult), &preAuthEphemeralKey) + assert.Nil(s.T(), err) + + assert.True(s.T(), preAuthEphemeralKey.GetEphemeral()) + assert.False(s.T(), preAuthEphemeralKey.GetReusable()) + + // TODO(kradalby): Evaluate if we need a case to test for reusable and ephemeral + // preAuthReusableAndEphemeralResult, err := ExecuteCommand( + // &s.headscale, + // []string{ + // "headscale", + // "preauthkeys", + // "--namespace", + // namespace.Name, + // "create", + // "--ephemeral", + // "--reusable", + // "--output", + // "json", + // }, + // []string{}, + // ) + // assert.NotNil(s.T(), err) + + // Test list of keys + listResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "preauthkeys", + "--namespace", + namespace.Name, + "list", + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var listedPreAuthKeys []v1.PreAuthKey + err = json.Unmarshal([]byte(listResult), &listedPreAuthKeys) + assert.Nil(s.T(), err) + + assert.Len(s.T(), listedPreAuthKeys, 2) +} + +func (s *IntegrationCLITestSuite) TestNodeCommand() { + namespace, err := s.createNamespace("machine-namespace") + assert.Nil(s.T(), err) + + sharedNamespace, err := s.createNamespace("shared-namespace") + assert.Nil(s.T(), err) + + // Randomly generated machine keys + machineKeys := []string{ + "9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", + "6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", + "f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", + "8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1", + "cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + } + machines := make([]*v1.Machine, len(machineKeys)) + assert.Nil(s.T(), err) + + for index, machineKey := range machineKeys { + _, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "debug", + "create-node", + "--name", + fmt.Sprintf("machine-%d", index+1), + "--namespace", + namespace.Name, + "--key", + machineKey, + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + machineResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "nodes", + "--namespace", + namespace.Name, + "register", + "--key", + machineKey, + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var machine v1.Machine + err = json.Unmarshal([]byte(machineResult), &machine) + assert.Nil(s.T(), err) + + machines[index] = &machine + } + + assert.Len(s.T(), machines, len(machineKeys)) + + // Test list all nodes after added shared + listAllResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var listAll []v1.Machine + err = json.Unmarshal([]byte(listAllResult), &listAll) + assert.Nil(s.T(), err) + + assert.Len(s.T(), listAll, 5) + + assert.Equal(s.T(), uint64(1), listAll[0].Id) + assert.Equal(s.T(), uint64(2), listAll[1].Id) + assert.Equal(s.T(), uint64(3), listAll[2].Id) + assert.Equal(s.T(), uint64(4), listAll[3].Id) + assert.Equal(s.T(), uint64(5), listAll[4].Id) + + assert.Equal(s.T(), "machine-1", listAll[0].Name) + assert.Equal(s.T(), "machine-2", listAll[1].Name) + assert.Equal(s.T(), "machine-3", listAll[2].Name) + 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{ + "b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e", + "dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584", + } + sharedMachines := make([]*v1.Machine, len(sharedMachineKeys)) + assert.Nil(s.T(), err) + + for index, machineKey := range sharedMachineKeys { + _, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "debug", + "create-node", + "--name", + fmt.Sprintf("shared-machine-%d", index+1), + "--namespace", + sharedNamespace.Name, + "--key", + machineKey, + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + machineResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "nodes", + "--namespace", + sharedNamespace.Name, + "register", + "--key", + machineKey, + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var machine v1.Machine + err = json.Unmarshal([]byte(machineResult), &machine) + assert.Nil(s.T(), err) + + sharedMachines[index] = &machine + } + + assert.Len(s.T(), sharedMachines, len(sharedMachineKeys)) + + // Test list all nodes after added shared + listAllWithSharedResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var listAllWithShared []v1.Machine + err = json.Unmarshal([]byte(listAllWithSharedResult), &listAllWithShared) + assert.Nil(s.T(), err) + + // All nodes, machines + shared + assert.Len(s.T(), listAllWithShared, 7) + + assert.Equal(s.T(), uint64(6), listAllWithShared[5].Id) + assert.Equal(s.T(), uint64(7), listAllWithShared[6].Id) + + assert.Equal(s.T(), "shared-machine-1", listAllWithShared[5].Name) + assert.Equal(s.T(), "shared-machine-2", listAllWithShared[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( + &s.headscale, + []string{ + "headscale", + "nodes", + "list", + "--namespace", + sharedNamespace.Name, + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var listOnlySharedMachineNamespace []v1.Machine + err = json.Unmarshal( + []byte(listOnlySharedMachineNamespaceResult), + &listOnlySharedMachineNamespace, + ) + assert.Nil(s.T(), err) + + assert.Len(s.T(), listOnlySharedMachineNamespace, 2) + + assert.Equal(s.T(), uint64(6), listOnlySharedMachineNamespace[0].Id) + assert.Equal(s.T(), uint64(7), listOnlySharedMachineNamespace[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) + + // Delete a machines + _, err = ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "nodes", + "delete", + "--identifier", + // Delete the last added machine + "4", + "--output", + "json", + "--force", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + // Test: list main namespace after machine is deleted + listOnlyMachineNamespaceAfterDeleteResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "nodes", + "list", + "--namespace", + namespace.Name, + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var listOnlyMachineNamespaceAfterDelete []v1.Machine + err = json.Unmarshal( + []byte(listOnlyMachineNamespaceAfterDeleteResult), + &listOnlyMachineNamespaceAfterDelete, + ) + 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() { + namespace, err := s.createNamespace("machine-expire-namespace") + assert.Nil(s.T(), err) + + // Randomly generated machine keys + machineKeys := []string{ + "9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", + "6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", + "f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", + "8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1", + "cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + } + machines := make([]*v1.Machine, len(machineKeys)) + assert.Nil(s.T(), err) + + for index, machineKey := range machineKeys { + _, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "debug", + "create-node", + "--name", + fmt.Sprintf("machine-%d", index+1), + "--namespace", + namespace.Name, + "--key", + machineKey, + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + machineResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "nodes", + "--namespace", + namespace.Name, + "register", + "--key", + machineKey, + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var machine v1.Machine + err = json.Unmarshal([]byte(machineResult), &machine) + assert.Nil(s.T(), err) + + machines[index] = &machine + } + + assert.Len(s.T(), machines, len(machineKeys)) + + listAllResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var listAll []v1.Machine + err = json.Unmarshal([]byte(listAllResult), &listAll) + assert.Nil(s.T(), err) + + assert.Len(s.T(), listAll, 5) + + assert.True(s.T(), listAll[0].Expiry.AsTime().IsZero()) + assert.True(s.T(), listAll[1].Expiry.AsTime().IsZero()) + assert.True(s.T(), listAll[2].Expiry.AsTime().IsZero()) + assert.True(s.T(), listAll[3].Expiry.AsTime().IsZero()) + assert.True(s.T(), listAll[4].Expiry.AsTime().IsZero()) + + for i := 0; i < 3; i++ { + _, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "nodes", + "expire", + "--identifier", + fmt.Sprintf("%d", listAll[i].Id), + }, + []string{}, + ) + assert.Nil(s.T(), err) + } + + listAllAfterExpiryResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var listAllAfterExpiry []v1.Machine + err = json.Unmarshal([]byte(listAllAfterExpiryResult), &listAllAfterExpiry) + assert.Nil(s.T(), err) + + assert.Len(s.T(), listAllAfterExpiry, 5) + + assert.True(s.T(), listAllAfterExpiry[0].Expiry.AsTime().Before(time.Now())) + assert.True(s.T(), listAllAfterExpiry[1].Expiry.AsTime().Before(time.Now())) + assert.True(s.T(), listAllAfterExpiry[2].Expiry.AsTime().Before(time.Now())) + assert.True(s.T(), listAllAfterExpiry[3].Expiry.AsTime().IsZero()) + assert.True(s.T(), listAllAfterExpiry[4].Expiry.AsTime().IsZero()) +} + +func (s *IntegrationCLITestSuite) TestRouteCommand() { + namespace, err := s.createNamespace("routes-namespace") + assert.Nil(s.T(), err) + + // Randomly generated machine keys + machineKey := "9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe" + + _, err = ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "debug", + "create-node", + "--name", + "route-machine", + "--namespace", + namespace.Name, + "--key", + machineKey, + "--route", + "10.0.0.0/8", + "--route", + "192.168.1.0/24", + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + machineResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "nodes", + "--namespace", + namespace.Name, + "register", + "--key", + machineKey, + "--output", + "json", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var machine v1.Machine + err = json.Unmarshal([]byte(machineResult), &machine) + assert.Nil(s.T(), err) + + 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, + []string{ + "headscale", + "routes", + "list", + "--output", + "json", + "--identifier", + "0", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var listAll v1.Routes + err = json.Unmarshal([]byte(listAllResult), &listAll) + assert.Nil(s.T(), err) + + assert.Len(s.T(), listAll.AdvertisedRoutes, 2) + assert.Contains(s.T(), listAll.AdvertisedRoutes, "10.0.0.0/8") + assert.Contains(s.T(), listAll.AdvertisedRoutes, "192.168.1.0/24") + + assert.Empty(s.T(), listAll.EnabledRoutes) + + enableTwoRoutesResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "routes", + "enable", + "--output", + "json", + "--identifier", + "0", + "--route", + "10.0.0.0/8", + "--route", + "192.168.1.0/24", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var enableTwoRoutes v1.Routes + err = json.Unmarshal([]byte(enableTwoRoutesResult), &enableTwoRoutes) + assert.Nil(s.T(), err) + + assert.Len(s.T(), enableTwoRoutes.AdvertisedRoutes, 2) + assert.Contains(s.T(), enableTwoRoutes.AdvertisedRoutes, "10.0.0.0/8") + assert.Contains(s.T(), enableTwoRoutes.AdvertisedRoutes, "192.168.1.0/24") + + assert.Len(s.T(), enableTwoRoutes.EnabledRoutes, 2) + assert.Contains(s.T(), enableTwoRoutes.EnabledRoutes, "10.0.0.0/8") + assert.Contains(s.T(), enableTwoRoutes.EnabledRoutes, "192.168.1.0/24") + + // Enable only one route, effectively disabling one of the routes + enableOneRouteResult, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "routes", + "enable", + "--output", + "json", + "--identifier", + "0", + "--route", + "10.0.0.0/8", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + var enableOneRoute v1.Routes + err = json.Unmarshal([]byte(enableOneRouteResult), &enableOneRoute) + assert.Nil(s.T(), err) + + assert.Len(s.T(), enableOneRoute.AdvertisedRoutes, 2) + assert.Contains(s.T(), enableOneRoute.AdvertisedRoutes, "10.0.0.0/8") + assert.Contains(s.T(), enableOneRoute.AdvertisedRoutes, "192.168.1.0/24") + + assert.Len(s.T(), enableOneRoute.EnabledRoutes, 1) + assert.Contains(s.T(), enableOneRoute.EnabledRoutes, "10.0.0.0/8") + + // Enable only one route, effectively disabling one of the routes + failEnableNonAdvertisedRoute, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "routes", + "enable", + "--output", + "json", + "--identifier", + "0", + "--route", + "11.0.0.0/8", + }, + []string{}, + ) + assert.Nil(s.T(), err) + + assert.Contains( + s.T(), + string(failEnableNonAdvertisedRoute), + "route (route-machine) is not available on node", + ) +} diff --git a/integration_common_test.go b/integration_common_test.go new file mode 100644 index 00000000..31bae514 --- /dev/null +++ b/integration_common_test.go @@ -0,0 +1,77 @@ +//go:build integration +// +build integration + +package headscale + +import ( + "bytes" + "fmt" + "time" + + "github.com/ory/dockertest/v3" + "github.com/ory/dockertest/v3/docker" +) + +const DOCKER_EXECUTE_TIMEOUT = 10 * time.Second + +func ExecuteCommand( + resource *dockertest.Resource, + cmd []string, + env []string, +) (string, error) { + var stdout bytes.Buffer + var stderr bytes.Buffer + + // TODO(kradalby): Make configurable + timeout := DOCKER_EXECUTE_TIMEOUT + + type result struct { + exitCode int + err error + } + + resultChan := make(chan result, 1) + + // Run your long running function in it's own goroutine and pass back it's + // response into our channel. + go func() { + exitCode, err := resource.Exec( + cmd, + dockertest.ExecOptions{ + Env: append(env, "HEADSCALE_LOG_LEVEL=disabled"), + StdOut: &stdout, + StdErr: &stderr, + }, + ) + resultChan <- result{exitCode, err} + }() + + // Listen on our channel AND a timeout channel - which ever happens first. + select { + case res := <-resultChan: + if res.err != nil { + return "", res.err + } + + if res.exitCode != 0 { + fmt.Println("Command: ", cmd) + fmt.Println("stdout: ", stdout.String()) + fmt.Println("stderr: ", stderr.String()) + + return "", fmt.Errorf("command failed with: %s", stderr.String()) + } + + return stdout.String(), nil + case <-time.After(timeout): + + return "", fmt.Errorf("command timed out after %s", timeout) + } +} + +func DockerRestartPolicy(config *docker.HostConfig) { + // set AutoRemove to true so that stopped container goes away by itself + config.AutoRemove = true + config.RestartPolicy = docker.RestartPolicy{ + Name: "no", + } +} diff --git a/integration_test.go b/integration_test.go index 53092423..0379429e 100644 --- a/integration_test.go +++ b/integration_test.go @@ -18,28 +18,17 @@ import ( "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" + "inet.af/netaddr" "tailscale.com/client/tailscale/apitype" "tailscale.com/ipn/ipnstate" - - "inet.af/netaddr" ) -var ( - integrationTmpDir string - ih Headscale -) - -var ( - pool dockertest.Pool - network dockertest.Network - headscale dockertest.Resource -) - -var tailscaleVersions = []string{"1.14.3", "1.12.3"} +var tailscaleVersions = []string{"1.20.2", "1.18.2", "1.16.2", "1.14.3", "1.12.3"} type TestNamespace struct { count int @@ -50,6 +39,10 @@ type IntegrationTestSuite struct { suite.Suite stats *suite.SuiteInformation + pool dockertest.Pool + network dockertest.Network + headscale dockertest.Resource + namespaces map[string]TestNamespace } @@ -74,54 +67,31 @@ func TestIntegrationTestSuite(t *testing.T) { // we have potentially saved the logs. for _, scales := range s.namespaces { for _, tailscale := range scales.tailscales { - if err := pool.Purge(&tailscale); err != nil { + if err := s.pool.Purge(&tailscale); err != nil { log.Printf("Could not purge resource: %s\n", err) } } } if !s.stats.Passed() { - err := saveLog(&headscale, "test_output") + err := s.saveLog(&s.headscale, "test_output") if err != nil { log.Printf("Could not save log: %s\n", err) } } - if err := pool.Purge(&headscale); err != nil { + if err := s.pool.Purge(&s.headscale); err != nil { log.Printf("Could not purge resource: %s\n", err) } - if err := network.Close(); err != nil { + if err := s.network.Close(); err != nil { log.Printf("Could not close network: %s\n", err) } } -func executeCommand(resource *dockertest.Resource, cmd []string, env []string) (string, error) { - var stdout bytes.Buffer - var stderr bytes.Buffer - - exitCode, err := resource.Exec( - cmd, - dockertest.ExecOptions{ - Env: env, - StdOut: &stdout, - StdErr: &stderr, - }, - ) - if err != nil { - return "", err - } - - if exitCode != 0 { - fmt.Println("Command: ", cmd) - fmt.Println("stdout: ", stdout.String()) - fmt.Println("stderr: ", stderr.String()) - return "", fmt.Errorf("command failed with: %s", stderr.String()) - } - - return stdout.String(), nil -} - -func saveLog(resource *dockertest.Resource, basePath string) error { +func (s *IntegrationTestSuite) saveLog( + resource *dockertest.Resource, + basePath string, +) error { err := os.MkdirAll(basePath, os.ModePerm) if err != nil { return err @@ -130,7 +100,7 @@ func saveLog(resource *dockertest.Resource, basePath string) error { var stdout bytes.Buffer var stderr bytes.Buffer - err = pool.Client.Logs( + err = s.pool.Client.Logs( docker.LogsOptions{ Context: context.TODO(), Container: resource.Container.ID, @@ -150,12 +120,20 @@ func saveLog(resource *dockertest.Resource, basePath string) error { fmt.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) + 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) + err = ioutil.WriteFile( + path.Join(basePath, resource.Container.Name+".stderr.log"), + []byte(stdout.String()), + 0o644, + ) if err != nil { return err } @@ -163,15 +141,9 @@ func saveLog(resource *dockertest.Resource, basePath string) error { return nil } -func dockerRestartPolicy(config *docker.HostConfig) { - // set AutoRemove to true so that stopped container goes away by itself - config.AutoRemove = true - config.RestartPolicy = docker.RestartPolicy{ - Name: "no", - } -} - -func tailscaleContainer(namespace, identifier, version string) (string, *dockertest.Resource) { +func (s *IntegrationTestSuite) tailscaleContainer( + namespace, identifier, version string, +) (string, *dockertest.Resource) { tailscaleBuildOptions := &dockertest.BuildOptions{ Dockerfile: "Dockerfile.tailscale", ContextDir: ".", @@ -182,36 +154,50 @@ func tailscaleContainer(namespace, identifier, version string) (string, *dockert }, }, } - hostname := fmt.Sprintf("%s-tailscale-%s-%s", namespace, strings.Replace(version, ".", "-", -1), identifier) + hostname := fmt.Sprintf( + "%s-tailscale-%s-%s", + namespace, + strings.Replace(version, ".", "-", -1), + identifier, + ) tailscaleOptions := &dockertest.RunOptions{ Name: hostname, - Networks: []*dockertest.Network{&network}, - Cmd: []string{"tailscaled", "--tun=userspace-networking", "--socks5-server=localhost:1055"}, + Networks: []*dockertest.Network{&s.network}, + Cmd: []string{ + "tailscaled", + "--tun=userspace-networking", + "--socks5-server=localhost:1055", + }, } - pts, err := pool.BuildAndRunWithBuildOptions(tailscaleBuildOptions, tailscaleOptions, dockerRestartPolicy) + pts, err := s.pool.BuildAndRunWithBuildOptions( + tailscaleBuildOptions, + tailscaleOptions, + DockerRestartPolicy, + ) if err != nil { log.Fatalf("Could not start resource: %s", err) } fmt.Printf("Created %s container\n", hostname) + return hostname, pts } func (s *IntegrationTestSuite) SetupSuite() { var err error - h = Headscale{ + app = Headscale{ dbType: "sqlite3", dbString: "integration_test_db.sqlite3", } if ppool, err := dockertest.NewPool(""); err == nil { - pool = *ppool + s.pool = *ppool } else { log.Fatalf("Could not connect to docker: %s", err) } - if pnetwork, err := pool.CreateNetwork("headscale-test"); err == nil { - network = *pnetwork + if pnetwork, err := s.pool.CreateNetwork("headscale-test"); err == nil { + s.network = *pnetwork } else { log.Fatalf("Could not create network: %s", err) } @@ -231,13 +217,13 @@ func (s *IntegrationTestSuite) SetupSuite() { Mounts: []string{ fmt.Sprintf("%s/integration_test/etc:/etc/headscale", currentPath), }, - Networks: []*dockertest.Network{&network}, + Networks: []*dockertest.Network{&s.network}, Cmd: []string{"headscale", "serve"}, } fmt.Println("Creating headscale container") - if pheadscale, err := pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, dockerRestartPolicy); err == nil { - headscale = *pheadscale + if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil { + s.headscale = *pheadscale } else { log.Fatalf("Could not start resource: %s", err) } @@ -248,23 +234,30 @@ func (s *IntegrationTestSuite) SetupSuite() { for i := 0; i < scales.count; i++ { version := tailscaleVersions[i%len(tailscaleVersions)] - hostname, container := tailscaleContainer(namespace, fmt.Sprint(i), version) + hostname, container := s.tailscaleContainer( + namespace, + fmt.Sprint(i), + version, + ) scales.tailscales[hostname] = *container } } fmt.Println("Waiting for headscale to be ready") - hostEndpoint := fmt.Sprintf("localhost:%s", headscale.GetPort("8080/tcp")) + hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8080/tcp")) - if err := pool.Retry(func() error { + if err := s.pool.Retry(func() error { url := fmt.Sprintf("http://%s/health", hostEndpoint) + resp, err := http.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 @@ -277,17 +270,17 @@ func (s *IntegrationTestSuite) SetupSuite() { for namespace, scales := range s.namespaces { fmt.Printf("Creating headscale namespace: %s\n", namespace) - result, err := executeCommand( - &headscale, + result, err := ExecuteCommand( + &s.headscale, []string{"headscale", "namespaces", "create", namespace}, []string{}, ) - assert.Nil(s.T(), err) fmt.Println("headscale create namespace result: ", result) + assert.Nil(s.T(), err) fmt.Printf("Creating pre auth key for %s\n", namespace) - authKey, err := executeCommand( - &headscale, + preAuthResult, err := ExecuteCommand( + &s.headscale, []string{ "headscale", "--namespace", @@ -297,14 +290,24 @@ func (s *IntegrationTestSuite) SetupSuite() { "--reusable", "--expiration", "24h", + "--output", + "json", }, - []string{}, + []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 := "http://headscale:8080" - fmt.Printf("Joining tailscale containers to headscale at %s\n", headscaleEndpoint) + fmt.Printf( + "Joining tailscale containers to headscale at %s\n", + headscaleEndpoint, + ) for hostname, tailscale := range scales.tailscales { command := []string{ "tailscale", @@ -312,14 +315,14 @@ func (s *IntegrationTestSuite) SetupSuite() { "-login-server", headscaleEndpoint, "--authkey", - strings.TrimSuffix(authKey, "\n"), + preAuthKey.Key, "--hostname", hostname, } fmt.Println("Join command:", command) fmt.Printf("Running join command for %s\n", hostname) - result, err := executeCommand( + result, err := ExecuteCommand( &tailscale, command, []string{}, @@ -338,15 +341,18 @@ func (s *IntegrationTestSuite) SetupSuite() { func (s *IntegrationTestSuite) TearDownSuite() { } -func (s *IntegrationTestSuite) HandleStats(suiteName string, stats *suite.SuiteInformation) { +func (s *IntegrationTestSuite) HandleStats( + suiteName string, + stats *suite.SuiteInformation, +) { s.stats = stats } func (s *IntegrationTestSuite) TestListNodes() { for namespace, scales := range s.namespaces { fmt.Println("Listing nodes") - result, err := executeCommand( - &headscale, + result, err := ExecuteCommand( + &s.headscale, []string{"headscale", "--namespace", namespace, "nodes", "list"}, []string{}, ) @@ -387,46 +393,50 @@ func (s *IntegrationTestSuite) TestGetIpAddresses() { } } -func (s *IntegrationTestSuite) TestStatus() { - for _, scales := range s.namespaces { - ips, err := getIPs(scales.tailscales) - assert.Nil(s.T(), err) - - for hostname, tailscale := range scales.tailscales { - s.T().Run(hostname, func(t *testing.T) { - command := []string{"tailscale", "status", "--json"} - - fmt.Printf("Getting status for %s\n", hostname) - result, err := executeCommand( - &tailscale, - command, - []string{}, - ) - assert.Nil(t, err) - - var status ipnstate.Status - err = json.Unmarshal([]byte(result), &status) - assert.Nil(s.T(), err) - - // TODO(kradalby): Replace this check with peer length of SAME namespace - // Check if we have as many nodes in status - // as we have IPs/tailscales - // lines := strings.Split(result, "\n") - // assert.Equal(t, len(ips), len(lines)-1) - // assert.Equal(t, len(scales.tailscales), len(lines)-1) - - peerIps := getIPsfromIPNstate(status) - - // Check that all hosts is present in all hosts status - for ipHostname, ip := range ips { - if hostname != ipHostname { - assert.Contains(t, peerIps, ip) - } - } - }) - } - } -} +// TODO(kradalby): fix this test +// We need some way to impot ipnstate.Status from multiple go packages. +// Currently it will only work with 1.18.x since that is the last +// version we have in go.mod +// func (s *IntegrationTestSuite) TestStatus() { +// for _, scales := range s.namespaces { +// ips, err := getIPs(scales.tailscales) +// assert.Nil(s.T(), err) +// +// for hostname, tailscale := range scales.tailscales { +// s.T().Run(hostname, func(t *testing.T) { +// command := []string{"tailscale", "status", "--json"} +// +// fmt.Printf("Getting status for %s\n", hostname) +// result, err := ExecuteCommand( +// &tailscale, +// command, +// []string{}, +// ) +// assert.Nil(t, err) +// +// var status ipnstate.Status +// err = json.Unmarshal([]byte(result), &status) +// assert.Nil(s.T(), err) +// +// // TODO(kradalby): Replace this check with peer length of SAME namespace +// // Check if we have as many nodes in status +// // as we have IPs/tailscales +// // lines := strings.Split(result, "\n") +// // assert.Equal(t, len(ips), len(lines)-1) +// // assert.Equal(t, len(scales.tailscales), len(lines)-1) +// +// peerIps := getIPsfromIPNstate(status) +// +// // Check that all hosts is present in all hosts status +// for ipHostname, ip := range ips { +// if hostname != ipHostname { +// assert.Contains(t, peerIps, ip) +// } +// } +// }) +// } +// } +// } func getIPsfromIPNstate(status ipnstate.Status) []netaddr.IP { ips := make([]netaddr.IP, 0) @@ -458,8 +468,14 @@ func (s *IntegrationTestSuite) TestPingAllPeers() { ip.String(), } - fmt.Printf("Pinging from %s (%s) to %s (%s)\n", hostname, ips[hostname], peername, ip) - result, err := executeCommand( + fmt.Printf( + "Pinging from %s (%s) to %s (%s)\n", + hostname, + ips[hostname], + peername, + ip, + ) + result, err := ExecuteCommand( &tailscale, command, []string{}, @@ -478,22 +494,35 @@ func (s *IntegrationTestSuite) TestSharedNodes() { main := s.namespaces["main"] shared := s.namespaces["shared"] - result, err := executeCommand( - &headscale, - []string{"headscale", "nodes", "list", "-o", "json", "--namespace", "shared"}, + result, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", + "json", + "--namespace", + "shared", + }, []string{}, ) assert.Nil(s.T(), err) - var machineList []Machine + var machineList []v1.Machine err = json.Unmarshal([]byte(result), &machineList) assert.Nil(s.T(), err) for _, machine := range machineList { - - result, err := executeCommand( - &headscale, - []string{"headscale", "nodes", "share", "--namespace", "shared", fmt.Sprint(machine.ID), "main"}, + result, err := ExecuteCommand( + &s.headscale, + []string{ + "headscale", + "nodes", + "share", + "--identifier", fmt.Sprint(machine.Id), + "--namespace", "main", + }, []string{}, ) assert.Nil(s.T(), err) @@ -501,8 +530,8 @@ func (s *IntegrationTestSuite) TestSharedNodes() { fmt.Println("Shared node with result: ", result) } - result, err = executeCommand( - &headscale, + result, err = ExecuteCommand( + &s.headscale, []string{"headscale", "nodes", "list", "--namespace", "main"}, []string{}, ) @@ -545,8 +574,14 @@ func (s *IntegrationTestSuite) TestSharedNodes() { ip.String(), } - fmt.Printf("Pinging from %s (%s) to %s (%s)\n", hostname, mainIps[hostname], peername, ip) - result, err := executeCommand( + fmt.Printf( + "Pinging from %s (%s) to %s (%s)\n", + hostname, + mainIps[hostname], + peername, + ip, + ) + result, err := ExecuteCommand( &tailscale, command, []string{}, @@ -569,7 +604,7 @@ func (s *IntegrationTestSuite) TestTailDrop() { for hostname, tailscale := range scales.tailscales { command := []string{"touch", fmt.Sprintf("/tmp/file_from_%s", hostname)} - _, err := executeCommand( + _, err := ExecuteCommand( &tailscale, command, []string{}, @@ -578,7 +613,6 @@ func (s *IntegrationTestSuite) TestTailDrop() { for peername, ip := range ips { s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) { if peername != hostname { - // Under normal circumstances, we should be able to send a file // using `tailscale file cp` - but not in userspace networking mode // So curl! @@ -603,10 +637,20 @@ func (s *IntegrationTestSuite) TestTailDrop() { "PUT", "--upload-file", fmt.Sprintf("/tmp/file_from_%s", hostname), - fmt.Sprintf("%s/v0/put/file_from_%s", peerAPI, hostname), + fmt.Sprintf( + "%s/v0/put/file_from_%s", + peerAPI, + hostname, + ), } - fmt.Printf("Sending file from %s (%s) to %s (%s)\n", hostname, ips[hostname], peername, ip) - _, err = executeCommand( + fmt.Printf( + "Sending file from %s (%s) to %s (%s)\n", + hostname, + ips[hostname], + peername, + ip, + ) + _, err = ExecuteCommand( &tailscale, command, []string{"ALL_PROXY=socks5://localhost:1055"}, @@ -633,7 +677,7 @@ func (s *IntegrationTestSuite) TestTailDrop() { "get", "/tmp/", } - _, err := executeCommand( + _, err := ExecuteCommand( &tailscale, command, []string{}, @@ -646,15 +690,25 @@ func (s *IntegrationTestSuite) TestTailDrop() { "ls", fmt.Sprintf("/tmp/file_from_%s", peername), } - fmt.Printf("Checking file in %s (%s) from %s (%s)\n", hostname, ips[hostname], peername, ip) - result, err := executeCommand( + fmt.Printf( + "Checking file in %s (%s) from %s (%s)\n", + hostname, + ips[hostname], + peername, + ip, + ) + result, err := ExecuteCommand( &tailscale, command, []string{}, ) assert.Nil(t, err) fmt.Printf("Result for %s: %s\n", peername, result) - assert.Equal(t, result, fmt.Sprintf("/tmp/file_from_%s\n", peername)) + assert.Equal( + t, + result, + fmt.Sprintf("/tmp/file_from_%s\n", peername), + ) } }) } @@ -685,7 +739,7 @@ func (s *IntegrationTestSuite) TestMagicDNS() { peername, ip, ) - result, err := executeCommand( + result, err := ExecuteCommand( &tailscale, command, []string{}, @@ -705,7 +759,7 @@ func getIPs(tailscales map[string]dockertest.Resource) (map[string]netaddr.IP, e for hostname, tailscale := range tailscales { command := []string{"tailscale", "ip"} - result, err := executeCommand( + result, err := ExecuteCommand( &tailscale, command, []string{}, @@ -721,10 +775,13 @@ func getIPs(tailscales map[string]dockertest.Resource) (map[string]netaddr.IP, e ips[hostname] = ip } + return ips, nil } -func getAPIURLs(tailscales map[string]dockertest.Resource) (map[netaddr.IP]string, error) { +func getAPIURLs( + tailscales map[string]dockertest.Resource, +) (map[netaddr.IP]string, error) { fts := make(map[netaddr.IP]string) for _, tailscale := range tailscales { command := []string{ @@ -733,7 +790,7 @@ func getAPIURLs(tailscales map[string]dockertest.Resource) (map[netaddr.IP]strin "/run/tailscale/tailscaled.sock", "http://localhost/localapi/v0/file-targets", } - result, err := executeCommand( + result, err := ExecuteCommand( &tailscale, command, []string{}, @@ -758,5 +815,6 @@ func getAPIURLs(tailscales map[string]dockertest.Resource) (map[netaddr.IP]strin } } } + return fts, nil } diff --git a/integration_test/etc/private.key b/integration_test/etc/private.key deleted file mode 100644 index b3a3ae6d..00000000 --- a/integration_test/etc/private.key +++ /dev/null @@ -1 +0,0 @@ -SEmQwCu+tGywQWEUsf93TpTRUvlB7WhnCdHgWrSXjEA= diff --git a/k8s/base/ingress.yaml b/k8s/base/ingress.yaml deleted file mode 100644 index a279bc1c..00000000 --- a/k8s/base/ingress.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: headscale - annotations: - kubernetes.io/ingress.class: traefik -spec: - rules: - - host: $(PUBLIC_HOSTNAME) - http: - paths: - - backend: - service: - name: headscale - port: - number: 8080 - path: / - pathType: Prefix diff --git a/k8s/base/kustomization.yaml b/k8s/base/kustomization.yaml deleted file mode 100644 index 54d66e54..00000000 --- a/k8s/base/kustomization.yaml +++ /dev/null @@ -1,42 +0,0 @@ -namespace: headscale -resources: -- configmap.yaml -- ingress.yaml -- service.yaml -generatorOptions: - disableNameSuffixHash: true -configMapGenerator: -- name: headscale-site - files: - - derp.yaml=site/derp.yaml - envs: - - site/public.env -- name: headscale-etc - literals: - - config.json={} -secretGenerator: -- name: headscale - files: - - secrets/private-key -vars: -- name: PUBLIC_PROTO - objRef: - kind: ConfigMap - name: headscale-site - apiVersion: v1 - fieldRef: - fieldPath: data.public-proto -- name: PUBLIC_HOSTNAME - objRef: - kind: ConfigMap - name: headscale-site - apiVersion: v1 - fieldRef: - fieldPath: data.public-hostname -- name: CONTACT_EMAIL - objRef: - kind: ConfigMap - name: headscale-site - apiVersion: v1 - fieldRef: - fieldPath: data.contact-email diff --git a/k8s/postgres/deployment.yaml b/k8s/postgres/deployment.yaml deleted file mode 100644 index dd45d05b..00000000 --- a/k8s/postgres/deployment.yaml +++ /dev/null @@ -1,78 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: headscale -spec: - replicas: 2 - selector: - matchLabels: - app: headscale - template: - metadata: - labels: - app: headscale - spec: - containers: - - name: headscale - image: "headscale:latest" - imagePullPolicy: IfNotPresent - command: ["/go/bin/headscale", "serve"] - env: - - name: SERVER_URL - value: $(PUBLIC_PROTO)://$(PUBLIC_HOSTNAME) - - name: LISTEN_ADDR - valueFrom: - configMapKeyRef: - name: headscale-config - key: listen_addr - - name: PRIVATE_KEY_PATH - value: /vol/secret/private-key - - name: DERP_MAP_PATH - value: /vol/config/derp.yaml - - name: EPHEMERAL_NODE_INACTIVITY_TIMEOUT - valueFrom: - configMapKeyRef: - name: headscale-config - key: ephemeral_node_inactivity_timeout - - name: DB_TYPE - value: postgres - - name: DB_HOST - value: postgres.headscale.svc.cluster.local - - name: DB_PORT - value: "5432" - - name: DB_USER - value: headscale - - name: DB_PASS - valueFrom: - secretKeyRef: - name: postgresql - key: password - - name: DB_NAME - value: headscale - ports: - - name: http - protocol: TCP - containerPort: 8080 - livenessProbe: - tcpSocket: - port: http - initialDelaySeconds: 30 - timeoutSeconds: 5 - periodSeconds: 15 - volumeMounts: - - name: config - mountPath: /vol/config - - name: secret - mountPath: /vol/secret - - name: etc - mountPath: /etc/headscale - volumes: - - name: config - configMap: - name: headscale-site - - name: etc - configMap: - name: headscale-etc - - name: secret - secret: - secretName: headscale diff --git a/k8s/postgres/kustomization.yaml b/k8s/postgres/kustomization.yaml deleted file mode 100644 index 8bd6c40c..00000000 --- a/k8s/postgres/kustomization.yaml +++ /dev/null @@ -1,13 +0,0 @@ -namespace: headscale -bases: -- ../base -resources: -- deployment.yaml -- postgres-service.yaml -- postgres-statefulset.yaml -generatorOptions: - disableNameSuffixHash: true -secretGenerator: -- name: postgresql - files: - - secrets/password diff --git a/k8s/postgres/postgres-statefulset.yaml b/k8s/postgres/postgres-statefulset.yaml deleted file mode 100644 index 25285c5e..00000000 --- a/k8s/postgres/postgres-statefulset.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: postgres -spec: - serviceName: postgres - replicas: 1 - selector: - matchLabels: - app: postgres - template: - metadata: - labels: - app: postgres - spec: - containers: - - name: postgres - image: "postgres:13" - imagePullPolicy: IfNotPresent - env: - - name: POSTGRES_PASSWORD - valueFrom: - secretKeyRef: - name: postgresql - key: password - - name: POSTGRES_USER - value: headscale - ports: - - name: postgres - protocol: TCP - containerPort: 5432 - livenessProbe: - tcpSocket: - port: 5432 - initialDelaySeconds: 30 - timeoutSeconds: 5 - periodSeconds: 15 - volumeMounts: - - name: pgdata - mountPath: /var/lib/postgresql/data - volumeClaimTemplates: - - metadata: - name: pgdata - spec: - storageClassName: local-path - accessModes: ["ReadWriteOnce"] - resources: - requests: - storage: 1Gi diff --git a/k8s/production-tls/kustomization.yaml b/k8s/production-tls/kustomization.yaml deleted file mode 100644 index f57cb540..00000000 --- a/k8s/production-tls/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -namespace: headscale -bases: -- ../base -resources: -- production-issuer.yaml -patches: -- path: ingress-patch.yaml - target: - kind: Ingress diff --git a/k8s/sqlite/statefulset.yaml b/k8s/sqlite/statefulset.yaml deleted file mode 100644 index 9075e007..00000000 --- a/k8s/sqlite/statefulset.yaml +++ /dev/null @@ -1,79 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: headscale -spec: - serviceName: headscale - replicas: 1 - selector: - matchLabels: - app: headscale - template: - metadata: - labels: - app: headscale - spec: - containers: - - name: headscale - image: "headscale:latest" - imagePullPolicy: IfNotPresent - command: ["/go/bin/headscale", "serve"] - env: - - name: SERVER_URL - value: $(PUBLIC_PROTO)://$(PUBLIC_HOSTNAME) - - name: LISTEN_ADDR - valueFrom: - configMapKeyRef: - name: headscale-config - key: listen_addr - - name: PRIVATE_KEY_PATH - value: /vol/secret/private-key - - name: DERP_MAP_PATH - value: /vol/config/derp.yaml - - name: EPHEMERAL_NODE_INACTIVITY_TIMEOUT - valueFrom: - configMapKeyRef: - name: headscale-config - key: ephemeral_node_inactivity_timeout - - name: DB_TYPE - value: sqlite3 - - name: DB_PATH - value: /vol/data/db.sqlite - ports: - - name: http - protocol: TCP - containerPort: 8080 - livenessProbe: - tcpSocket: - port: http - initialDelaySeconds: 30 - timeoutSeconds: 5 - periodSeconds: 15 - volumeMounts: - - name: config - mountPath: /vol/config - - name: data - mountPath: /vol/data - - name: secret - mountPath: /vol/secret - - name: etc - mountPath: /etc/headscale - volumes: - - name: config - configMap: - name: headscale-site - - name: etc - configMap: - name: headscale-etc - - name: secret - secret: - secretName: headscale - volumeClaimTemplates: - - metadata: - name: data - spec: - storageClassName: local-path - accessModes: ["ReadWriteOnce"] - resources: - requests: - storage: 1Gi diff --git a/k8s/staging-tls/kustomization.yaml b/k8s/staging-tls/kustomization.yaml deleted file mode 100644 index 931f27d5..00000000 --- a/k8s/staging-tls/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -namespace: headscale -bases: -- ../base -resources: -- staging-issuer.yaml -patches: -- path: ingress-patch.yaml - target: - kind: Ingress diff --git a/machine.go b/machine.go index 8986ac92..de1764e1 100644 --- a/machine.go +++ b/machine.go @@ -2,6 +2,7 @@ package headscale import ( "encoding/json" + "errors" "fmt" "sort" "strconv" @@ -9,15 +10,22 @@ import ( "time" "github.com/fatih/set" + 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/wgkey" + "tailscale.com/types/key" ) -// Machine is a Headscale client +const ( + errMachineNotFound = Error("machine not found") + errMachineAlreadyRegistered = Error("machine already registered") + errMachineRouteIsNotAvailable = Error("route is not available on machine") +) + +// Machine is a Headscale client. type Machine struct { ID uint64 `gorm:"primary_key"` MachineKey string `gorm:"type:varchar(64);unique_index"` @@ -51,43 +59,57 @@ type ( MachinesP []*Machine ) -// For the time being this method is rather naive -func (m Machine) isAlreadyRegistered() bool { - return m.Registered +// For the time being this method is rather naive. +func (machine Machine) isRegistered() bool { + return machine.Registered } -func (h *Headscale) getDirectPeers(m *Machine) (Machines, error) { +// isExpired returns whether the machine registration has expired. +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() { + return false + } + + return time.Now().UTC().After(*machine.Expiry) +} + +func (h *Headscale) getDirectPeers(machine *Machine) (Machines, error) { log.Trace(). - Str("func", "getDirectPeers"). - Str("machine", m.Name). + 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", - m.NamespaceID, m.MachineKey).Find(&machines).Error; err != nil { + machine.NamespaceID, machine.MachineKey).Find(&machines).Error; err != nil { log.Error().Err(err).Msg("Error accessing db") + return Machines{}, err } sort.Slice(machines, func(i, j int) bool { return machines[i].ID < machines[j].ID }) log.Trace(). - Str("func", "getDirectmachines"). - Str("machine", m.Name). + Caller(). + Str("machine", machine.Name). Msgf("Found direct machines: %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(m *Machine) (Machines, error) { +// 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(). - Str("func", "getShared"). - Str("machine", m.Name). + Caller(). + Str("machine", machine.Name). Msg("Finding shared peers") sharedMachines := []SharedMachine{} if err := h.db.Preload("Namespace").Preload("Machine").Preload("Machine.Namespace").Where("namespace_id = ?", - m.NamespaceID).Find(&sharedMachines).Error; err != nil { + machine.NamespaceID).Find(&sharedMachines).Error; err != nil { return Machines{}, err } @@ -99,68 +121,75 @@ func (h *Headscale) getShared(m *Machine) (Machines, error) { sort.Slice(peers, func(i, j int) bool { return peers[i].ID < peers[j].ID }) log.Trace(). - Str("func", "getShared"). - Str("machine", m.Name). + 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(m *Machine) (Machines, error) { +// getSharedTo fetches the machines of the namespaces this machine is shared in. +func (h *Headscale) getSharedTo(machine *Machine) (Machines, error) { log.Trace(). - Str("func", "getSharedTo"). - Str("machine", m.Name). + 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 = ?", - m.ID).Find(&sharedMachines).Error; err != nil { + 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) + namespaceMachines, err := h.ListMachinesInNamespace( + sharedMachine.Namespace.Name, + ) if err != nil { return Machines{}, err } - peers = append(peers, *namespaceMachines...) + peers = append(peers, namespaceMachines...) } sort.Slice(peers, func(i, j int) bool { return peers[i].ID < peers[j].ID }) log.Trace(). - Str("func", "getSharedTo"). - Str("machine", m.Name). + Caller(). + Str("machine", machine.Name). Msgf("Found peers we are shared with: %s", peers.String()) + return peers, nil } -func (h *Headscale) getPeers(m *Machine) (Machines, error) { - direct, err := h.getDirectPeers(m) +func (h *Headscale) getPeers(machine *Machine) (Machines, error) { + direct, err := h.getDirectPeers(machine) if err != nil { log.Error(). - Str("func", "getPeers"). + Caller(). Err(err). Msg("Cannot fetch peers") + return Machines{}, err } - shared, err := h.getShared(m) + shared, err := h.getShared(machine) if err != nil { log.Error(). - Str("func", "getShared"). + Caller(). Err(err). Msg("Cannot fetch peers") + return Machines{}, err } - sharedTo, err := h.getSharedTo(m) + sharedTo, err := h.getSharedTo(machine) if err != nil { log.Error(). - Str("func", "sharedTo"). + Caller(). Err(err). Msg("Cannot fetch peers") + return Machines{}, err } @@ -170,92 +199,154 @@ func (h *Headscale) getPeers(m *Machine) (Machines, error) { sort.Slice(peers, func(i, j int) bool { return peers[i].ID < peers[j].ID }) log.Trace(). - Str("func", "getShared"). - Str("machine", m.Name). + Caller(). + Str("machine", machine.Name). Msgf("Found total peers: %s", peers.String()) return peers, nil } -// GetMachine finds a Machine by name and namespace and returns the Machine struct +func (h *Headscale) getValidPeers(machine *Machine) (Machines, error) { + validPeers := make(Machines, 0) + + peers, err := h.getPeers(machine) + if err != nil { + return Machines{}, err + } + + for _, peer := range peers { + if peer.isRegistered() && !peer.isExpired() { + validPeers = append(validPeers, peer) + } + } + + return validPeers, nil +} + +func (h *Headscale) ListMachines() ([]Machine, error) { + machines := []Machine{} + if err := h.db.Preload("AuthKey").Preload("AuthKey.Namespace").Preload("Namespace").Find(&machines).Error; err != nil { + return nil, err + } + + return machines, nil +} + +// GetMachine finds a Machine by name and namespace and returns the Machine struct. func (h *Headscale) GetMachine(namespace string, name string) (*Machine, error) { machines, err := h.ListMachinesInNamespace(namespace) if err != nil { return nil, err } - for _, m := range *machines { + for _, m := range machines { if m.Name == name { return &m, nil } } - return nil, fmt.Errorf("machine not found") + + return nil, errMachineNotFound } -// GetMachineByID finds a Machine by ID and returns the Machine struct +// GetMachineByID finds a Machine by ID and returns the Machine struct. func (h *Headscale) GetMachineByID(id uint64) (*Machine, error) { m := Machine{} if result := h.db.Preload("Namespace").Find(&Machine{ID: id}).First(&m); result.Error != nil { return nil, result.Error } + return &m, nil } -// GetMachineByMachineKey finds a Machine by ID and returns the Machine struct -func (h *Headscale) GetMachineByMachineKey(mKey string) (*Machine, error) { +// GetMachineByMachineKey finds a Machine by ID and returns the Machine struct. +func (h *Headscale) GetMachineByMachineKey( + machineKey key.MachinePublic, +) (*Machine, error) { m := Machine{} - if result := h.db.Preload("Namespace").First(&m, "machine_key = ?", mKey); result.Error != nil { + if result := h.db.Preload("Namespace").First(&m, "machine_key = ?", MachinePublicKeyStripPrefix(machineKey)); result.Error != nil { return nil, result.Error } + return &m, nil } // UpdateMachine takes a Machine struct pointer (typically already loaded from database // and updates it with the latest data from the database. -func (h *Headscale) UpdateMachine(m *Machine) error { - if result := h.db.Find(m).First(&m); result.Error != nil { +func (h *Headscale) UpdateMachine(machine *Machine) error { + if result := h.db.Find(machine).First(&machine); result.Error != nil { return result.Error } + return nil } -// DeleteMachine softs deletes a Machine from the database -func (h *Headscale) DeleteMachine(m *Machine) error { - err := h.RemoveSharedMachineFromAllNamespaces(m) - if err != nil && err != errorMachineNotShared { +// ExpireMachine takes a Machine struct and sets the expire field to now. +func (h *Headscale) ExpireMachine(machine *Machine) { + now := time.Now() + machine.Expiry = &now + + h.setLastStateChangeToNow(machine.Namespace.Name) + + h.db.Save(machine) +} + +// RefreshMachine takes a Machine struct and sets the expire field to now. +func (h *Headscale) RefreshMachine(machine *Machine, expiry time.Time) { + now := time.Now() + + machine.LastSuccessfulUpdate = &now + machine.Expiry = &expiry + + h.setLastStateChangeToNow(machine.Namespace.Name) + + h.db.Save(machine) +} + +// 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 } - m.Registered = false - namespaceID := m.NamespaceID - h.db.Save(&m) // we mark it as unregistered, just in case - if err := h.db.Delete(&m).Error; err != nil { + machine.Registered = false + namespaceID := machine.NamespaceID + h.db.Save(&machine) // we mark it as unregistered, just in case + if err := h.db.Delete(&machine).Error; err != nil { return err } return h.RequestMapUpdates(namespaceID) } -// HardDeleteMachine hard deletes a Machine from the database -func (h *Headscale) HardDeleteMachine(m *Machine) error { - err := h.RemoveSharedMachineFromAllNamespaces(m) - if err != nil && err != errorMachineNotShared { +func (h *Headscale) TouchMachine(machine *Machine) error { + return h.db.Updates(Machine{ + ID: machine.ID, + LastSeen: machine.LastSeen, + LastSuccessfulUpdate: machine.LastSuccessfulUpdate, + }).Error +} + +// HardDeleteMachine hard deletes a Machine from the database. +func (h *Headscale) HardDeleteMachine(machine *Machine) error { + err := h.RemoveSharedMachineFromAllNamespaces(machine) + if err != nil && errors.Is(err, errMachineNotShared) { return err } - namespaceID := m.NamespaceID - if err := h.db.Unscoped().Delete(&m).Error; err != nil { + namespaceID := machine.NamespaceID + if err := h.db.Unscoped().Delete(&machine).Error; err != nil { return err } return h.RequestMapUpdates(namespaceID) } -// GetHostInfo returns a Hostinfo struct for the machine -func (m *Machine) GetHostInfo() (*tailcfg.Hostinfo, error) { +// GetHostInfo returns a Hostinfo struct for the machine. +func (machine *Machine) GetHostInfo() (*tailcfg.Hostinfo, error) { hostinfo := tailcfg.Hostinfo{} - if len(m.HostInfo) != 0 { - hi, err := m.HostInfo.MarshalJSON() + if len(machine.HostInfo) != 0 { + hi, err := machine.HostInfo.MarshalJSON() if err != nil { return nil, err } @@ -264,21 +355,21 @@ func (m *Machine) GetHostInfo() (*tailcfg.Hostinfo, error) { return nil, err } } + return &hostinfo, nil } -func (h *Headscale) isOutdated(m *Machine) bool { - err := h.UpdateMachine(m) - if err != nil { +func (h *Headscale) isOutdated(machine *Machine) bool { + if err := h.UpdateMachine(machine); err != nil { // It does not seem meaningful to propagate this error as the end result // will have to be that the machine has to be considered outdated. return true } - sharedMachines, _ := h.getShared(m) + sharedMachines, _ := h.getShared(machine) namespaceSet := set.New(set.ThreadSafe) - namespaceSet.Add(m.Namespace.Name) + namespaceSet.Add(machine.Namespace.Name) // Check if any of our shared namespaces has updates that we have // not propagated. @@ -288,27 +379,30 @@ func (h *Headscale) isOutdated(m *Machine) bool { namespaces := make([]string, namespaceSet.Size()) for index, namespace := range namespaceSet.List() { - namespaces[index] = namespace.(string) + if name, ok := namespace.(string); ok { + namespaces[index] = name + } } lastChange := h.getLastStateChange(namespaces...) log.Trace(). - Str("func", "keepAlive"). - Str("machine", m.Name). - Time("last_successful_update", *m.LastSuccessfulUpdate). + Caller(). + Str("machine", machine.Name). + Time("last_successful_update", *machine.LastSuccessfulUpdate). Time("last_state_change", lastChange). - Msgf("Checking if %s is missing updates", m.Name) - return m.LastSuccessfulUpdate.Before(lastChange) + Msgf("Checking if %s is missing updates", machine.Name) + + return machine.LastSuccessfulUpdate.Before(lastChange) } -func (m Machine) String() string { - return m.Name +func (machine Machine) String() string { + return machine.Name } -func (ms Machines) String() string { - temp := make([]string, len(ms)) +func (machines Machines) String() string { + temp := make([]string, len(machines)) - for index, machine := range ms { + for index, machine := range machines { temp[index] = machine.Name } @@ -316,20 +410,24 @@ func (ms Machines) String() string { } // TODO(kradalby): Remove when we have generics... -func (ms MachinesP) String() string { - temp := make([]string, len(ms)) +func (machines MachinesP) String() string { + temp := make([]string, len(machines)) - for index, machine := range ms { + for index, machine := range machines { temp[index] = machine.Name } return fmt.Sprintf("[ %s ](%d)", strings.Join(temp, ", "), len(temp)) } -func (ms Machines) toNodes(baseDomain string, dnsConfig *tailcfg.DNSConfig, includeRoutes bool) ([]*tailcfg.Node, error) { - nodes := make([]*tailcfg.Node, len(ms)) +func (machines Machines) toNodes( + baseDomain string, + dnsConfig *tailcfg.DNSConfig, + includeRoutes bool, +) ([]*tailcfg.Node, error) { + nodes := make([]*tailcfg.Node, len(machines)) - for index, machine := range ms { + for index, machine := range machines { node, err := machine.toNode(baseDomain, dnsConfig, includeRoutes) if err != nil { return nil, err @@ -342,46 +440,65 @@ func (ms Machines) toNodes(baseDomain string, dnsConfig *tailcfg.DNSConfig, incl } // toNode converts a Machine into a Tailscale Node. includeRoutes is false for shared nodes -// as per the expected behaviour in the official SaaS -func (m Machine) toNode(baseDomain string, dnsConfig *tailcfg.DNSConfig, includeRoutes bool) (*tailcfg.Node, error) { - nKey, err := wgkey.ParseHex(m.NodeKey) +// as per the expected behaviour in the official SaaS. +func (machine Machine) toNode( + baseDomain string, + dnsConfig *tailcfg.DNSConfig, + includeRoutes bool, +) (*tailcfg.Node, error) { + var nodeKey key.NodePublic + err := nodeKey.UnmarshalText([]byte(NodePublicKeyEnsurePrefix(machine.NodeKey))) if err != nil { - return nil, err - } - mKey, err := wgkey.ParseHex(m.MachineKey) - if err != nil { - return nil, err + log.Trace(). + Caller(). + Str("node_key", machine.NodeKey). + Msgf("Failed to parse node public key from hex") + + return nil, fmt.Errorf("failed to parse node public key: %w", err) } - var discoKey tailcfg.DiscoKey - if m.DiscoKey != "" { - dKey, err := wgkey.ParseHex(m.DiscoKey) + var machineKey key.MachinePublic + err = machineKey.UnmarshalText( + []byte(MachinePublicKeyEnsurePrefix(machine.MachineKey)), + ) + if err != nil { + return nil, fmt.Errorf("failed to parse machine public key: %w", err) + } + + var discoKey key.DiscoPublic + if machine.DiscoKey != "" { + err := discoKey.UnmarshalText( + []byte(DiscoPublicKeyEnsurePrefix(machine.DiscoKey)), + ) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to parse disco public key: %w", err) } - discoKey = tailcfg.DiscoKey(dKey) } else { - discoKey = tailcfg.DiscoKey{} + discoKey = key.DiscoPublic{} } addrs := []netaddr.IPPrefix{} - ip, err := netaddr.ParseIPPrefix(fmt.Sprintf("%s/32", m.IPAddress)) + ip, err := netaddr.ParseIPPrefix(fmt.Sprintf("%s/32", machine.IPAddress)) if err != nil { log.Trace(). - Str("func", "toNode"). - Str("ip", m.IPAddress). - Msgf("Failed to parse IP Prefix from IP: %s", m.IPAddress) + Caller(). + Str("ip", machine.IPAddress). + Msgf("Failed to parse IP Prefix from IP: %s", machine.IPAddress) + return nil, err } addrs = append(addrs, ip) // missing the ipv6 ? allowedIPs := []netaddr.IPPrefix{} - allowedIPs = append(allowedIPs, ip) // we append the node own IP, as it is required by the clients + allowedIPs = append( + allowedIPs, + ip, + ) // we append the node own IP, as it is required by the clients if includeRoutes { routesStr := []string{} - if len(m.EnabledRoutes) != 0 { - allwIps, err := m.EnabledRoutes.MarshalJSON() + if len(machine.EnabledRoutes) != 0 { + allwIps, err := machine.EnabledRoutes.MarshalJSON() if err != nil { return nil, err } @@ -401,8 +518,8 @@ func (m Machine) toNode(baseDomain string, dnsConfig *tailcfg.DNSConfig, include } endpoints := []string{} - if len(m.Endpoints) != 0 { - be, err := m.Endpoints.MarshalJSON() + if len(machine.Endpoints) != 0 { + be, err := machine.Endpoints.MarshalJSON() if err != nil { return nil, err } @@ -413,8 +530,8 @@ func (m Machine) toNode(baseDomain string, dnsConfig *tailcfg.DNSConfig, include } hostinfo := tailcfg.Hostinfo{} - if len(m.HostInfo) != 0 { - hi, err := m.HostInfo.MarshalJSON() + if len(machine.HostInfo) != 0 { + hi, err := machine.HostInfo.MarshalJSON() if err != nil { return nil, err } @@ -432,27 +549,34 @@ func (m Machine) toNode(baseDomain string, dnsConfig *tailcfg.DNSConfig, include } var keyExpiry time.Time - if m.Expiry != nil { - keyExpiry = *m.Expiry + if machine.Expiry != nil { + keyExpiry = *machine.Expiry } else { keyExpiry = time.Time{} } var hostname string if dnsConfig != nil && dnsConfig.Proxied { // MagicDNS - hostname = fmt.Sprintf("%s.%s.%s", m.Name, m.Namespace.Name, baseDomain) + hostname = fmt.Sprintf( + "%s.%s.%s", + machine.Name, + machine.Namespace.Name, + baseDomain, + ) } else { - hostname = m.Name + hostname = machine.Name } - n := tailcfg.Node{ - ID: tailcfg.NodeID(m.ID), // this is the actual ID - StableID: tailcfg.StableNodeID(strconv.FormatUint(m.ID, 10)), // in headscale, unlike tailcontrol server, IDs are permanent + node := tailcfg.Node{ + ID: tailcfg.NodeID(machine.ID), // this is the actual ID + StableID: tailcfg.StableNodeID( + strconv.FormatUint(machine.ID, Base10), + ), // in headscale, unlike tailcontrol server, IDs are permanent Name: hostname, - User: tailcfg.UserID(m.NamespaceID), - Key: tailcfg.NodeKey(nKey), + User: tailcfg.UserID(machine.NamespaceID), + Key: nodeKey, KeyExpiry: keyExpiry, - Machine: tailcfg.MachineKey(mKey), + Machine: machineKey, DiscoKey: discoKey, Addresses: addrs, AllowedIPs: allowedIPs, @@ -460,12 +584,268 @@ func (m Machine) toNode(baseDomain string, dnsConfig *tailcfg.DNSConfig, include DERP: derp, Hostinfo: hostinfo, - Created: m.CreatedAt, - LastSeen: m.LastSeen, + Created: machine.CreatedAt, + LastSeen: machine.LastSeen, KeepAlive: true, - MachineAuthorized: m.Registered, + MachineAuthorized: machine.Registered, Capabilities: []string{tailcfg.CapabilityFileSharing}, } - return &n, nil + + return &node, nil +} + +func (machine *Machine) toProto() *v1.Machine { + machineProto := &v1.Machine{ + Id: machine.ID, + MachineKey: machine.MachineKey, + + NodeKey: machine.NodeKey, + DiscoKey: machine.DiscoKey, + IpAddress: machine.IPAddress, + Name: machine.Name, + Namespace: machine.Namespace.toProto(), + + Registered: machine.Registered, + + // TODO(kradalby): Implement register method enum converter + // RegisterMethod: , + + CreatedAt: timestamppb.New(machine.CreatedAt), + } + + if machine.AuthKey != nil { + machineProto.PreAuthKey = machine.AuthKey.toProto() + } + + if machine.LastSeen != nil { + machineProto.LastSeen = timestamppb.New(*machine.LastSeen) + } + + if machine.LastSuccessfulUpdate != nil { + machineProto.LastSuccessfulUpdate = timestamppb.New( + *machine.LastSuccessfulUpdate, + ) + } + + if machine.Expiry != nil { + machineProto.Expiry = timestamppb.New(*machine.Expiry) + } + + return machineProto +} + +// RegisterMachine is executed from the CLI to register a new Machine using its MachineKey. +func (h *Headscale) RegisterMachine( + machineKeyStr string, + namespaceName string, +) (*Machine, error) { + namespace, err := h.GetNamespace(namespaceName) + if err != nil { + return nil, err + } + + var machineKey key.MachinePublic + err = machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machineKeyStr))) + if err != nil { + return nil, err + } + + log.Trace(). + Caller(). + Str("machine_key_str", machineKeyStr). + Str("machine_key", machineKey.String()). + Msg("Registering machine") + + 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 + } + } + + if machine.isRegistered() { + log.Trace(). + Caller(). + Str("machine", machine.Name). + Msg("machine already registered, reauthenticating") + + h.RefreshMachine(machine, requestedTime) + + return machine, nil + } + + 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 + } + + ip, err := h.getAvailableIP() + if err != nil { + log.Error(). + Caller(). + Err(err). + Str("machine", machine.Name). + Msg("Could not find IP for the new machine") + + return nil, err + } + + log.Trace(). + Caller(). + Str("machine", machine.Name). + Str("ip", ip.String()). + Msg("Found IP for host") + + machine.IPAddress = ip.String() + machine.NamespaceID = namespace.ID + machine.Registered = true + machine.RegisterMethod = RegisterMethodCLI + machine.Expiry = &requestedTime + h.db.Save(&machine) + + log.Trace(). + Caller(). + Str("machine", machine.Name). + Str("ip", ip.String()). + Msg("Machine registered with the database") + + 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) 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) IsRoutesEnabled(routeStr string) bool { + route, err := netaddr.ParseIPPrefix(routeStr) + if err != nil { + return false + } + + enabledRoutes, err := machine.GetEnabledRoutes() + if err != nil { + return false + } + + for _, enabledRoute := range enabledRoutes { + if route == enabledRoute { + return true + } + } + + return false +} + +// EnableNodeRoute enables new routes based on a list of new routes. It will _replace_ the +// previous list of routes. +func (h *Headscale) EnableRoutes(machine *Machine, routeStrs ...string) error { + newRoutes := make([]netaddr.IPPrefix, len(routeStrs)) + for index, routeStr := range routeStrs { + route, err := netaddr.ParseIPPrefix(routeStr) + if err != nil { + return err + } + + newRoutes[index] = route + } + + availableRoutes, err := machine.GetAdvertisedRoutes() + if err != nil { + return err + } + + for _, newRoute := range newRoutes { + if !containsIPPrefix(availableRoutes, newRoute) { + return fmt.Errorf( + "route (%s) is not available on node %s: %w", + machine.Name, + newRoute, errMachineRouteIsNotAvailable, + ) + } + } + + routes, err := json.Marshal(newRoutes) + if err != nil { + return err + } + + machine.EnabledRoutes = datatypes.JSON(routes) + h.db.Save(&machine) + + err = h.RequestMapUpdates(machine.NamespaceID) + if err != nil { + return err + } + + return nil +} + +func (machine *Machine) RoutesToProto() (*v1.Routes, error) { + availableRoutes, err := machine.GetAdvertisedRoutes() + if err != nil { + return nil, err + } + + enabledRoutes, err := machine.GetEnabledRoutes() + if err != nil { + return nil, err + } + + return &v1.Routes{ + AdvertisedRoutes: ipPrefixToString(availableRoutes), + EnabledRoutes: ipPrefixToString(enabledRoutes), + }, nil } diff --git a/machine_test.go b/machine_test.go index dfe84d33..eb090072 100644 --- a/machine_test.go +++ b/machine_test.go @@ -3,157 +3,199 @@ package headscale import ( "encoding/json" "strconv" + "time" "gopkg.in/check.v1" ) func (s *Suite) TestGetMachine(c *check.C) { - n, err := h.CreateNamespace("test") + namespace, err := app.CreateNamespace("test") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, false, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) c.Assert(err, check.IsNil) - _, err = h.GetMachine("test", "testmachine") + _, err = app.GetMachine("test", "testmachine") c.Assert(err, check.NotNil) - m := &Machine{ + machine := &Machine{ ID: 0, MachineKey: "foo", NodeKey: "bar", DiscoKey: "faa", Name: "testmachine", - NamespaceID: n.ID, + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } - h.db.Save(m) + app.db.Save(machine) - m1, err := h.GetMachine("test", "testmachine") + machineFromDB, err := app.GetMachine("test", "testmachine") c.Assert(err, check.IsNil) - _, err = m1.GetHostInfo() + _, err = machineFromDB.GetHostInfo() c.Assert(err, check.IsNil) } func (s *Suite) TestGetMachineByID(c *check.C) { - n, err := h.CreateNamespace("test") + namespace, err := app.CreateNamespace("test") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, false, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) c.Assert(err, check.IsNil) - _, err = h.GetMachineByID(0) + _, err = app.GetMachineByID(0) c.Assert(err, check.NotNil) - m := Machine{ + machine := Machine{ ID: 0, MachineKey: "foo", NodeKey: "bar", DiscoKey: "faa", Name: "testmachine", - NamespaceID: n.ID, + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } - h.db.Save(&m) + app.db.Save(&machine) - m1, err := h.GetMachineByID(0) + machineByID, err := app.GetMachineByID(0) c.Assert(err, check.IsNil) - _, err = m1.GetHostInfo() + _, err = machineByID.GetHostInfo() c.Assert(err, check.IsNil) } func (s *Suite) TestDeleteMachine(c *check.C) { - n, err := h.CreateNamespace("test") + namespace, err := app.CreateNamespace("test") c.Assert(err, check.IsNil) - m := Machine{ + machine := Machine{ ID: 0, MachineKey: "foo", NodeKey: "bar", DiscoKey: "faa", Name: "testmachine", - NamespaceID: n.ID, + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(1), } - h.db.Save(&m) - err = h.DeleteMachine(&m) + app.db.Save(&machine) + + err = app.DeleteMachine(&machine) c.Assert(err, check.IsNil) - v, err := h.getValue("namespaces_pending_updates") + + namespacesPendingUpdates, err := app.getValue("namespaces_pending_updates") c.Assert(err, check.IsNil) + names := []string{} - err = json.Unmarshal([]byte(v), &names) + err = json.Unmarshal([]byte(namespacesPendingUpdates), &names) c.Assert(err, check.IsNil) - c.Assert(names, check.DeepEquals, []string{n.Name}) - h.checkForNamespacesPendingUpdates() - v, _ = h.getValue("namespaces_pending_updates") - c.Assert(v, check.Equals, "") - _, err = h.GetMachine(n.Name, "testmachine") + c.Assert(names, check.DeepEquals, []string{namespace.Name}) + + app.checkForNamespacesPendingUpdates() + + namespacesPendingUpdates, _ = app.getValue("namespaces_pending_updates") + c.Assert(namespacesPendingUpdates, check.Equals, "") + _, err = app.GetMachine(namespace.Name, "testmachine") c.Assert(err, check.NotNil) } func (s *Suite) TestHardDeleteMachine(c *check.C) { - n, err := h.CreateNamespace("test") + namespace, err := app.CreateNamespace("test") c.Assert(err, check.IsNil) - m := Machine{ + machine := Machine{ ID: 0, MachineKey: "foo", NodeKey: "bar", DiscoKey: "faa", Name: "testmachine3", - NamespaceID: n.ID, + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(1), } - h.db.Save(&m) - err = h.HardDeleteMachine(&m) + app.db.Save(&machine) + + err = app.HardDeleteMachine(&machine) c.Assert(err, check.IsNil) - _, err = h.GetMachine(n.Name, "testmachine3") + + _, err = app.GetMachine(namespace.Name, "testmachine3") c.Assert(err, check.NotNil) } func (s *Suite) TestGetDirectPeers(c *check.C) { - n, err := h.CreateNamespace("test") + namespace, err := app.CreateNamespace("test") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, false, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) c.Assert(err, check.IsNil) - _, err = h.GetMachineByID(0) + _, err = app.GetMachineByID(0) c.Assert(err, check.NotNil) - for i := 0; i <= 10; i++ { - m := Machine{ - ID: uint64(i), - MachineKey: "foo" + strconv.Itoa(i), - NodeKey: "bar" + strconv.Itoa(i), - DiscoKey: "faa" + strconv.Itoa(i), - Name: "testmachine" + strconv.Itoa(i), - NamespaceID: n.ID, + 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), + Name: "testmachine" + strconv.Itoa(index), + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } - h.db.Save(&m) + app.db.Save(&machine) } - m1, err := h.GetMachineByID(0) + machine0ByID, err := app.GetMachineByID(0) c.Assert(err, check.IsNil) - _, err = m1.GetHostInfo() + _, err = machine0ByID.GetHostInfo() c.Assert(err, check.IsNil) - peers, err := h.getDirectPeers(m1) + peersOfMachine0, err := app.getDirectPeers(machine0ByID) c.Assert(err, check.IsNil) - c.Assert(len(peers), check.Equals, 9) - c.Assert(peers[0].Name, check.Equals, "testmachine2") - c.Assert(peers[5].Name, check.Equals, "testmachine7") - c.Assert(peers[8].Name, check.Equals, "testmachine10") + c.Assert(len(peersOfMachine0), check.Equals, 9) + c.Assert(peersOfMachine0[0].Name, check.Equals, "testmachine2") + c.Assert(peersOfMachine0[5].Name, check.Equals, "testmachine7") + c.Assert(peersOfMachine0[8].Name, check.Equals, "testmachine10") +} + +func (s *Suite) TestExpireMachine(c *check.C) { + namespace, err := app.CreateNamespace("test") + c.Assert(err, check.IsNil) + + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) + c.Assert(err, check.IsNil) + + _, err = app.GetMachine("test", "testmachine") + c.Assert(err, check.NotNil) + + machine := &Machine{ + ID: 0, + MachineKey: "foo", + NodeKey: "bar", + DiscoKey: "faa", + Name: "testmachine", + NamespaceID: namespace.ID, + Registered: true, + RegisterMethod: RegisterMethodAuthKey, + AuthKeyID: uint(pak.ID), + Expiry: &time.Time{}, + } + app.db.Save(machine) + + machineFromDB, err := app.GetMachine("test", "testmachine") + c.Assert(err, check.IsNil) + + c.Assert(machineFromDB.isExpired(), check.Equals, false) + + app.ExpireMachine(machineFromDB) + + c.Assert(machineFromDB.isExpired(), check.Equals, true) } diff --git a/metrics.go b/metrics.go index 0d3dca34..f0ce16ec 100644 --- a/metrics.go +++ b/metrics.go @@ -32,7 +32,7 @@ var ( Name: "update_request_sent_to_node_total", Help: "The number of calls/messages issued on a specific nodes update channel", }, []string{"namespace", "machine", "status"}) - //TODO(kradalby): This is very debugging, we might want to remove it. + // TODO(kradalby): This is very debugging, we might want to remove it. updateRequestsReceivedOnChannel = promauto.NewCounterVec(prometheus.CounterOpts{ Namespace: prometheusNamespace, Name: "update_request_received_on_channel_total", diff --git a/namespaces.go b/namespaces.go index c350e8c8..e512068d 100644 --- a/namespaces.go +++ b/namespaces.go @@ -4,16 +4,21 @@ import ( "encoding/json" "errors" "fmt" + "strconv" "time" + v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/rs/zerolog/log" + "google.golang.org/protobuf/types/known/timestamppb" "gorm.io/gorm" "tailscale.com/tailcfg" ) -const errorNamespaceExists = Error("Namespace already exists") -const errorNamespaceNotFound = Error("Namespace not found") -const errorNamespaceNotEmpty = Error("Namespace not empty") +const ( + errNamespaceExists = Error("Namespace already exists") + errNamespaceNotFound = Error("Namespace not found") + errNamespaceNotEmptyOfNodes = Error("Namespace not empty: node(s) found") +) // Namespace is the way Headscale implements the concept of users in Tailscale // @@ -25,40 +30,53 @@ type Namespace struct { } // CreateNamespace creates a new Namespace. Returns error if could not be created -// or another namespace already exists +// or another namespace already exists. func (h *Headscale) CreateNamespace(name string) (*Namespace, error) { - n := Namespace{} - if err := h.db.Where("name = ?", name).First(&n).Error; err == nil { - return nil, errorNamespaceExists + namespace := Namespace{} + if err := h.db.Where("name = ?", name).First(&namespace).Error; err == nil { + return nil, errNamespaceExists } - n.Name = name - if err := h.db.Create(&n).Error; err != nil { + namespace.Name = name + if err := h.db.Create(&namespace).Error; err != nil { log.Error(). Str("func", "CreateNamespace"). Err(err). Msg("Could not create row") + return nil, err } - return &n, nil + + return &namespace, nil } // DestroyNamespace destroys a Namespace. Returns error if the Namespace does // not exist or if there are machines associated with it. func (h *Headscale) DestroyNamespace(name string) error { - n, err := h.GetNamespace(name) + namespace, err := h.GetNamespace(name) if err != nil { - return errorNamespaceNotFound + return errNamespaceNotFound } - m, err := h.ListMachinesInNamespace(name) + machines, err := h.ListMachinesInNamespace(name) if err != nil { return err } - if len(*m) > 0 { - return errorNamespaceNotEmpty + if len(machines) > 0 { + return errNamespaceNotEmptyOfNodes } - if result := h.db.Unscoped().Delete(&n); result.Error != nil { + keys, err := h.ListPreAuthKeys(name) + if err != nil { + return err + } + for _, key := range keys { + err = h.DestroyPreAuthKey(key) + if err != nil { + return err + } + } + + if result := h.db.Unscoped().Delete(&namespace); result.Error != nil { return result.Error } @@ -68,25 +86,25 @@ 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 { - n, err := h.GetNamespace(oldName) + oldNamespace, err := h.GetNamespace(oldName) if err != nil { return err } _, err = h.GetNamespace(newName) if err == nil { - return errorNamespaceExists + return errNamespaceExists } - if !errors.Is(err, errorNamespaceNotFound) { + if !errors.Is(err, errNamespaceNotFound) { return err } - n.Name = newName + oldNamespace.Name = newName - if result := h.db.Save(&n); result.Error != nil { + if result := h.db.Save(&oldNamespace); result.Error != nil { return result.Error } - err = h.RequestMapUpdates(n.ID) + err = h.RequestMapUpdates(oldNamespace.ID) if err != nil { return err } @@ -94,40 +112,46 @@ func (h *Headscale) RenameNamespace(oldName, newName string) error { return nil } -// GetNamespace fetches a namespace by name +// GetNamespace fetches a namespace by name. func (h *Headscale) GetNamespace(name string) (*Namespace, error) { - n := Namespace{} - if result := h.db.First(&n, "name = ?", name); errors.Is(result.Error, gorm.ErrRecordNotFound) { - return nil, errorNamespaceNotFound + namespace := Namespace{} + if result := h.db.First(&namespace, "name = ?", name); errors.Is( + result.Error, + gorm.ErrRecordNotFound, + ) { + return nil, errNamespaceNotFound } - return &n, nil + + return &namespace, nil } -// ListNamespaces gets all the existing namespaces -func (h *Headscale) ListNamespaces() (*[]Namespace, error) { +// ListNamespaces gets all the existing namespaces. +func (h *Headscale) ListNamespaces() ([]Namespace, error) { namespaces := []Namespace{} if err := h.db.Find(&namespaces).Error; err != nil { return nil, err } - return &namespaces, nil + + return namespaces, nil } -// ListMachinesInNamespace gets all the nodes in a given namespace -func (h *Headscale) ListMachinesInNamespace(name string) (*[]Machine, error) { - n, err := h.GetNamespace(name) +// ListMachinesInNamespace gets all the nodes in a given namespace. +func (h *Headscale) ListMachinesInNamespace(name string) ([]Machine, error) { + namespace, err := h.GetNamespace(name) if err != nil { return nil, err } machines := []Machine{} - if err := h.db.Preload("AuthKey").Preload("AuthKey.Namespace").Preload("Namespace").Where(&Machine{NamespaceID: n.ID}).Find(&machines).Error; err != nil { + if err := h.db.Preload("AuthKey").Preload("AuthKey.Namespace").Preload("Namespace").Where(&Machine{NamespaceID: namespace.ID}).Find(&machines).Error; err != nil { return nil, err } - return &machines, nil + + return machines, nil } -// ListSharedMachinesInNamespace returns all the machines that are shared to the specified namespace -func (h *Headscale) ListSharedMachinesInNamespace(name string) (*[]Machine, error) { +// 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 @@ -139,48 +163,61 @@ func (h *Headscale) ListSharedMachinesInNamespace(name string) (*[]Machine, erro machines := []Machine{} for _, sharedMachine := range sharedMachines { - machine, err := h.GetMachineByID(sharedMachine.MachineID) // otherwise not everything comes filled + machine, err := h.GetMachineByID( + sharedMachine.MachineID, + ) // otherwise not everything comes filled if err != nil { return nil, err } machines = append(machines, *machine) } - return &machines, nil + + return machines, nil } -// SetMachineNamespace assigns a Machine to a namespace -func (h *Headscale) SetMachineNamespace(m *Machine, namespaceName string) error { - n, err := h.GetNamespace(namespaceName) +// SetMachineNamespace assigns a Machine to a namespace. +func (h *Headscale) SetMachineNamespace(machine *Machine, namespaceName string) error { + namespace, err := h.GetNamespace(namespaceName) if err != nil { return err } - m.NamespaceID = n.ID - h.db.Save(&m) + machine.NamespaceID = namespace.ID + h.db.Save(&machine) + return nil } -// RequestMapUpdates signals the KV worker to update the maps for this namespace +// TODO(kradalby): Remove the need for this. +// RequestMapUpdates signals the KV worker to update the maps for this namespace. func (h *Headscale) RequestMapUpdates(namespaceID uint) error { namespace := Namespace{} if err := h.db.First(&namespace, namespaceID).Error; err != nil { return err } - v, err := h.getValue("namespaces_pending_updates") - if err != nil || v == "" { - err = h.setValue("namespaces_pending_updates", fmt.Sprintf(`["%s"]`, namespace.Name)) + namespacesPendingUpdates, err := h.getValue("namespaces_pending_updates") + if err != nil || namespacesPendingUpdates == "" { + err = h.setValue( + "namespaces_pending_updates", + fmt.Sprintf(`["%s"]`, namespace.Name), + ) if err != nil { return err } + return nil } names := []string{} - err = json.Unmarshal([]byte(v), &names) + err = json.Unmarshal([]byte(namespacesPendingUpdates), &names) if err != nil { - err = h.setValue("namespaces_pending_updates", fmt.Sprintf(`["%s"]`, namespace.Name)) + err = h.setValue( + "namespaces_pending_updates", + fmt.Sprintf(`["%s"]`, namespace.Name), + ) if err != nil { return err } + return nil } @@ -191,22 +228,24 @@ func (h *Headscale) RequestMapUpdates(namespaceID uint) error { Str("func", "RequestMapUpdates"). Err(err). Msg("Could not marshal namespaces_pending_updates") + return err } + return h.setValue("namespaces_pending_updates", string(data)) } func (h *Headscale) checkForNamespacesPendingUpdates() { - v, err := h.getValue("namespaces_pending_updates") + namespacesPendingUpdates, err := h.getValue("namespaces_pending_updates") if err != nil { return } - if v == "" { + if namespacesPendingUpdates == "" { return } namespaces := []string{} - err = json.Unmarshal([]byte(v), &namespaces) + err = json.Unmarshal([]byte(namespacesPendingUpdates), &namespaces) if err != nil { return } @@ -217,24 +256,25 @@ func (h *Headscale) checkForNamespacesPendingUpdates() { Msg("Sending updates to nodes in namespacespace") h.setLastStateChangeToNow(namespace) } - newV, err := h.getValue("namespaces_pending_updates") + newPendingUpdateValue, err := h.getValue("namespaces_pending_updates") if err != nil { return } - if v == newV { // only clear when no changes, so we notified everybody + if namespacesPendingUpdates == newPendingUpdateValue { // only clear when no changes, so we notified everybody err = h.setValue("namespaces_pending_updates", "") if err != nil { log.Error(). Str("func", "checkForNamespacesPendingUpdates"). Err(err). Msg("Could not save to KV") + return } } } func (n *Namespace) toUser() *tailcfg.User { - u := tailcfg.User{ + user := tailcfg.User{ ID: tailcfg.UserID(n.ID), LoginName: n.Name, DisplayName: n.Name, @@ -243,14 +283,27 @@ func (n *Namespace) toUser() *tailcfg.User { Logins: []tailcfg.LoginID{}, Created: time.Time{}, } - return &u + + return &user } -func getMapResponseUserProfiles(m Machine, peers Machines) []tailcfg.UserProfile { +func (n *Namespace) toLogin() *tailcfg.Login { + login := tailcfg.Login{ + ID: tailcfg.LoginID(n.ID), + LoginName: n.Name, + DisplayName: n.Name, + ProfilePicURL: "", + Domain: "headscale.net", + } + + return &login +} + +func getMapResponseUserProfiles(machine Machine, peers Machines) []tailcfg.UserProfile { namespaceMap := make(map[string]Namespace) - namespaceMap[m.Namespace.Name] = m.Namespace - for _, p := range peers { - namespaceMap[p.Namespace.Name] = p.Namespace // not worth checking if already is there + namespaceMap[machine.Namespace.Name] = machine.Namespace + for _, peer := range peers { + namespaceMap[peer.Namespace.Name] = peer.Namespace // not worth checking if already is there } profiles := []tailcfg.UserProfile{} @@ -262,5 +315,14 @@ func getMapResponseUserProfiles(m Machine, peers Machines) []tailcfg.UserProfile DisplayName: namespace.Name, }) } + return profiles } + +func (n *Namespace) toProto() *v1.Namespace { + return &v1.Namespace{ + Id: strconv.FormatUint(uint64(n.ID), Base10), + Name: n.Name, + CreatedAt: timestamppb.New(n.CreatedAt), + } +} diff --git a/namespaces_test.go b/namespaces_test.go index 2a211da9..9793e608 100644 --- a/namespaces_test.go +++ b/namespaces_test.go @@ -3,197 +3,236 @@ package headscale import ( "github.com/rs/zerolog/log" "gopkg.in/check.v1" + "gorm.io/gorm" ) func (s *Suite) TestCreateAndDestroyNamespace(c *check.C) { - n, err := h.CreateNamespace("test") + namespace, err := app.CreateNamespace("test") c.Assert(err, check.IsNil) - c.Assert(n.Name, check.Equals, "test") + c.Assert(namespace.Name, check.Equals, "test") - ns, err := h.ListNamespaces() + namespaces, err := app.ListNamespaces() c.Assert(err, check.IsNil) - c.Assert(len(*ns), check.Equals, 1) + c.Assert(len(namespaces), check.Equals, 1) - err = h.DestroyNamespace("test") + err = app.DestroyNamespace("test") c.Assert(err, check.IsNil) - _, err = h.GetNamespace("test") + _, err = app.GetNamespace("test") c.Assert(err, check.NotNil) } func (s *Suite) TestDestroyNamespaceErrors(c *check.C) { - err := h.DestroyNamespace("test") - c.Assert(err, check.Equals, errorNamespaceNotFound) + err := app.DestroyNamespace("test") + c.Assert(err, check.Equals, errNamespaceNotFound) - n, err := h.CreateNamespace("test") + namespace, err := app.CreateNamespace("test") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, false, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) c.Assert(err, check.IsNil) - m := Machine{ + err = app.DestroyNamespace("test") + c.Assert(err, check.IsNil) + + result := app.db.Preload("Namespace").First(&pak, "key = ?", pak.Key) + // destroying a namespace also deletes all associated preauthkeys + c.Assert(result.Error, check.Equals, gorm.ErrRecordNotFound) + + namespace, err = app.CreateNamespace("test") + c.Assert(err, check.IsNil) + + pak, err = app.CreatePreAuthKey(namespace.Name, false, false, nil) + c.Assert(err, check.IsNil) + + machine := Machine{ ID: 0, MachineKey: "foo", NodeKey: "bar", DiscoKey: "faa", Name: "testmachine", - NamespaceID: n.ID, + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } - h.db.Save(&m) + app.db.Save(&machine) - err = h.DestroyNamespace("test") - c.Assert(err, check.Equals, errorNamespaceNotEmpty) + err = app.DestroyNamespace("test") + c.Assert(err, check.Equals, errNamespaceNotEmptyOfNodes) } func (s *Suite) TestRenameNamespace(c *check.C) { - n, err := h.CreateNamespace("test") + namespaceTest, err := app.CreateNamespace("test") c.Assert(err, check.IsNil) - c.Assert(n.Name, check.Equals, "test") + c.Assert(namespaceTest.Name, check.Equals, "test") - ns, err := h.ListNamespaces() + namespaces, err := app.ListNamespaces() c.Assert(err, check.IsNil) - c.Assert(len(*ns), check.Equals, 1) + c.Assert(len(namespaces), check.Equals, 1) - err = h.RenameNamespace("test", "test_renamed") + err = app.RenameNamespace("test", "test_renamed") c.Assert(err, check.IsNil) - _, err = h.GetNamespace("test") - c.Assert(err, check.Equals, errorNamespaceNotFound) + _, err = app.GetNamespace("test") + c.Assert(err, check.Equals, errNamespaceNotFound) - _, err = h.GetNamespace("test_renamed") + _, err = app.GetNamespace("test_renamed") c.Assert(err, check.IsNil) - err = h.RenameNamespace("test_does_not_exit", "test") - c.Assert(err, check.Equals, errorNamespaceNotFound) + err = app.RenameNamespace("test_does_not_exit", "test") + c.Assert(err, check.Equals, errNamespaceNotFound) - n2, err := h.CreateNamespace("test2") + namespaceTest2, err := app.CreateNamespace("test2") c.Assert(err, check.IsNil) - c.Assert(n2.Name, check.Equals, "test2") + c.Assert(namespaceTest2.Name, check.Equals, "test2") - err = h.RenameNamespace("test2", "test_renamed") - c.Assert(err, check.Equals, errorNamespaceExists) + err = app.RenameNamespace("test2", "test_renamed") + c.Assert(err, check.Equals, errNamespaceExists) } func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { - n1, err := h.CreateNamespace("shared1") + namespaceShared1, err := app.CreateNamespace("shared1") c.Assert(err, check.IsNil) - n2, err := h.CreateNamespace("shared2") + namespaceShared2, err := app.CreateNamespace("shared2") c.Assert(err, check.IsNil) - n3, err := h.CreateNamespace("shared3") + namespaceShared3, err := app.CreateNamespace("shared3") c.Assert(err, check.IsNil) - pak1n1, err := h.CreatePreAuthKey(n1.Name, false, false, nil) + preAuthKeyShared1, err := app.CreatePreAuthKey( + namespaceShared1.Name, + false, + false, + nil, + ) c.Assert(err, check.IsNil) - pak2n2, err := h.CreatePreAuthKey(n2.Name, false, false, nil) + preAuthKeyShared2, err := app.CreatePreAuthKey( + namespaceShared2.Name, + false, + false, + nil, + ) c.Assert(err, check.IsNil) - pak3n3, err := h.CreatePreAuthKey(n3.Name, false, false, nil) + preAuthKeyShared3, err := app.CreatePreAuthKey( + namespaceShared3.Name, + false, + false, + nil, + ) c.Assert(err, check.IsNil) - pak4n1, err := h.CreatePreAuthKey(n1.Name, false, false, nil) + preAuthKey2Shared1, err := app.CreatePreAuthKey( + namespaceShared1.Name, + false, + false, + nil, + ) c.Assert(err, check.IsNil) - _, err = h.GetMachine(n1.Name, "test_get_shared_nodes_1") + _, err = app.GetMachine(namespaceShared1.Name, "test_get_shared_nodes_1") c.Assert(err, check.NotNil) - m1 := &Machine{ + machineInShared1 := &Machine{ ID: 1, MachineKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", NodeKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", DiscoKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", Name: "test_get_shared_nodes_1", - NamespaceID: n1.ID, - Namespace: *n1, + NamespaceID: namespaceShared1.ID, + Namespace: *namespaceShared1, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, IPAddress: "100.64.0.1", - AuthKeyID: uint(pak1n1.ID), + AuthKeyID: uint(preAuthKeyShared1.ID), } - h.db.Save(m1) + app.db.Save(machineInShared1) - _, err = h.GetMachine(n1.Name, m1.Name) + _, err = app.GetMachine(namespaceShared1.Name, machineInShared1.Name) c.Assert(err, check.IsNil) - m2 := &Machine{ + machineInShared2 := &Machine{ ID: 2, MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", Name: "test_get_shared_nodes_2", - NamespaceID: n2.ID, - Namespace: *n2, + NamespaceID: namespaceShared2.ID, + Namespace: *namespaceShared2, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, IPAddress: "100.64.0.2", - AuthKeyID: uint(pak2n2.ID), + AuthKeyID: uint(preAuthKeyShared2.ID), } - h.db.Save(m2) + app.db.Save(machineInShared2) - _, err = h.GetMachine(n2.Name, m2.Name) + _, err = app.GetMachine(namespaceShared2.Name, machineInShared2.Name) c.Assert(err, check.IsNil) - m3 := &Machine{ + machineInShared3 := &Machine{ ID: 3, MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", Name: "test_get_shared_nodes_3", - NamespaceID: n3.ID, - Namespace: *n3, + NamespaceID: namespaceShared3.ID, + Namespace: *namespaceShared3, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, IPAddress: "100.64.0.3", - AuthKeyID: uint(pak3n3.ID), + AuthKeyID: uint(preAuthKeyShared3.ID), } - h.db.Save(m3) + app.db.Save(machineInShared3) - _, err = h.GetMachine(n3.Name, m3.Name) + _, err = app.GetMachine(namespaceShared3.Name, machineInShared3.Name) c.Assert(err, check.IsNil) - m4 := &Machine{ + machine2InShared1 := &Machine{ ID: 4, MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", Name: "test_get_shared_nodes_4", - NamespaceID: n1.ID, - Namespace: *n1, + NamespaceID: namespaceShared1.ID, + Namespace: *namespaceShared1, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, IPAddress: "100.64.0.4", - AuthKeyID: uint(pak4n1.ID), + AuthKeyID: uint(preAuthKey2Shared1.ID), } - h.db.Save(m4) + app.db.Save(machine2InShared1) - err = h.AddSharedMachineToNamespace(m2, n1) + err = app.AddSharedMachineToNamespace(machineInShared2, namespaceShared1) c.Assert(err, check.IsNil) - m1peers, err := h.getPeers(m1) + peersOfMachine1InShared1, err := app.getPeers(machineInShared1) c.Assert(err, check.IsNil) - userProfiles := getMapResponseUserProfiles(*m1, m1peers) + userProfiles := getMapResponseUserProfiles( + *machineInShared1, + peersOfMachine1InShared1, + ) log.Trace().Msgf("userProfiles %#v", userProfiles) c.Assert(len(userProfiles), check.Equals, 2) found := false - for _, up := range userProfiles { - if up.DisplayName == n1.Name { + for _, userProfiles := range userProfiles { + if userProfiles.DisplayName == namespaceShared1.Name { found = true + break } } c.Assert(found, check.Equals, true) found = false - for _, up := range userProfiles { - if up.DisplayName == n2.Name { + for _, userProfile := range userProfiles { + if userProfile.DisplayName == namespaceShared2.Name { found = true + break } } diff --git a/oidc.go b/oidc.go new file mode 100644 index 00000000..120a4cff --- /dev/null +++ b/oidc.go @@ -0,0 +1,389 @@ +package headscale + +import ( + "bytes" + "context" + "crypto/rand" + "encoding/hex" + "errors" + "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 +) + +type IDTokenClaims struct { + Name string `json:"name,omitempty"` + Groups []string `json:"groups,omitempty"` + Email string `json:"email"` + Username string `json:"preferred_username,omitempty"` +} + +func (h *Headscale) initOIDC() error { + var err error + // grab oidc config if it hasn't been already + if h.oauth2Config == nil { + h.oidcProvider, err = oidc.NewProvider(context.Background(), h.cfg.OIDC.Issuer) + + if err != nil { + log.Error(). + Err(err). + Caller(). + Msgf("Could not retrieve OIDC Config: %s", err.Error()) + + return err + } + + h.oauth2Config = &oauth2.Config{ + ClientID: h.cfg.OIDC.ClientID, + ClientSecret: h.cfg.OIDC.ClientSecret, + Endpoint: h.oidcProvider.Endpoint(), + RedirectURL: fmt.Sprintf( + "%s/oidc/callback", + strings.TrimSuffix(h.cfg.ServerURL, "/"), + ), + Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, + } + } + + // init the state cache if it hasn't been already + if h.oidcStateCache == nil { + h.oidcStateCache = cache.New( + oidcStateCacheExpiration, + oidcStateCacheCleanupInterval, + ) + } + + return nil +} + +// RegisterOIDC redirects to the OIDC provider for authentication +// Puts machine key in cache so the callback can retrieve it using the oidc state param +// Listens in /oidc/register/:mKey. +func (h *Headscale) RegisterOIDC(ctx *gin.Context) { + machineKeyStr := ctx.Param("mkey") + if machineKeyStr == "" { + ctx.String(http.StatusBadRequest, "Wrong params") + + return + } + + log.Trace(). + Caller(). + Str("machine_key", machineKeyStr). + Msg("Received oidc register call") + + randomBlob := make([]byte, randomByteSize) + if _, err := rand.Read(randomBlob); err != nil { + log.Error(). + Caller(). + Msg("could not read 16 bytes from rand") + ctx.String(http.StatusInternalServerError, "could not read 16 bytes from rand") + + return + } + + 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) + + authURL := h.oauth2Config.AuthCodeURL(stateStr) + log.Debug().Msgf("Redirecting to %s for authentication", authURL) + + ctx.Redirect(http.StatusFound, authURL) +} + +type oidcCallbackTemplateConfig struct { + User string + Verb string +} + +var oidcCallbackTemplate = template.Must( + template.New("oidccallback").Parse(` + +

headscale

+

+ {{.Verb}} as {{.User}}, you can now close this window. +

+ + `), +) + +// 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 +// TODO: Add groups information from OIDC tokens into machine HostInfo +// Listens in /oidc/callback. +func (h *Headscale) OIDCCallback(ctx *gin.Context) { + code := ctx.Query("code") + state := ctx.Query("state") + + if code == "" || state == "" { + ctx.String(http.StatusBadRequest, "Wrong params") + + return + } + + oauth2Token, err := h.oauth2Config.Exchange(context.Background(), code) + if err != nil { + ctx.String(http.StatusBadRequest, "Could not exchange code for token") + + return + } + + log.Trace(). + Caller(). + Str("code", code). + Str("state", state). + Msg("Got oidc callback") + + rawIDToken, rawIDTokenOK := oauth2Token.Extra("id_token").(string) + if !rawIDTokenOK { + ctx.String(http.StatusBadRequest, "Could not extract ID Token") + + return + } + + verifier := h.oidcProvider.Verifier(&oidc.Config{ClientID: h.cfg.OIDC.ClientID}) + + idToken, err := verifier.Verify(context.Background(), rawIDToken) + if err != nil { + log.Error(). + Err(err). + Caller(). + Msg("failed to verify id token") + ctx.String(http.StatusBadRequest, "Failed to verify id token") + + return + } + + // TODO: we can use userinfo at some point to grab additional information about the user (groups membership, etc) + // userInfo, err := oidcProvider.UserInfo(context.Background(), oauth2.StaticTokenSource(oauth2Token)) + // if err != nil { + // c.String(http.StatusBadRequest, fmt.Sprintf("Failed to retrieve userinfo")) + // return + // } + + // Extract custom claims + var claims IDTokenClaims + if err = idToken.Claims(&claims); err != nil { + log.Error(). + Err(err). + Caller(). + Msg("Failed to decode id token claims") + ctx.String( + http.StatusBadRequest, + "Failed to decode id token claims", + ) + + return + } + + // retrieve machinekey from state cache + machineKeyIf, machineKeyFound := h.oidcStateCache.Get(state) + + if !machineKeyFound { + log.Error(). + Msg("requested machine state key expired before authorisation completed") + ctx.String(http.StatusBadRequest, "state has expired") + + return + } + + machineKeyStr, machineKeyOK := machineKeyIf.(string) + + var machineKey key.MachinePublic + err = machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machineKeyStr))) + if err != nil { + log.Error(). + Msg("could not parse machine public key") + ctx.String(http.StatusBadRequest, "could not parse public key") + + return + } + + if !machineKeyOK { + log.Error().Msg("could not get machine key from cache") + ctx.String( + http.StatusInternalServerError, + "could not get machine key from cache", + ) + + 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 + 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() { + log.Trace(). + Caller(). + Str("machine", machine.Name). + Msg("machine already registered, reauthenticating") + + h.RefreshMachine(machine, requestedTime) + + var content bytes.Buffer + if err := oidcCallbackTemplate.Execute(&content, oidcCallbackTemplateConfig{ + User: claims.Email, + Verb: "Reauthenticated", + }); err != nil { + log.Error(). + Str("func", "OIDCCallback"). + Str("type", "reauthenticate"). + 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()) + + 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 + } + + ip, err := h.getAvailableIP() + 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.IPAddress = ip.String() + 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()) + + 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", + ) +} + +// 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 + } + } + + return "", false +} diff --git a/oidc_test.go b/oidc_test.go new file mode 100644 index 00000000..d50027a9 --- /dev/null +++ b/oidc_test.go @@ -0,0 +1,180 @@ +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/poll.go b/poll.go index 6a652805..d7aa12e9 100644 --- a/poll.go +++ b/poll.go @@ -12,7 +12,12 @@ import ( "gorm.io/datatypes" "gorm.io/gorm" "tailscale.com/tailcfg" - "tailscale.com/types/wgkey" + "tailscale.com/types/key" +) + +const ( + keepAliveInterval = 60 * time.Second + updateCheckInterval = 10 * time.Second ) // PollNetMapHandler takes care of /machine/:id/map @@ -24,57 +29,64 @@ import ( // only after their first request (marked with the ReadOnly field). // // At this moment the updates are sent in a quite horrendous way, but they kinda work. -func (h *Headscale) PollNetMapHandler(c *gin.Context) { +func (h *Headscale) PollNetMapHandler(ctx *gin.Context) { log.Trace(). Str("handler", "PollNetMap"). - Str("id", c.Param("id")). + Str("id", ctx.Param("id")). Msg("PollNetMapHandler called") - body, _ := io.ReadAll(c.Request.Body) - mKeyStr := c.Param("id") - mKey, err := wgkey.ParseHex(mKeyStr) + body, _ := io.ReadAll(ctx.Request.Body) + machineKeyStr := ctx.Param("id") + + var machineKey key.MachinePublic + err := machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machineKeyStr))) if err != nil { log.Error(). Str("handler", "PollNetMap"). Err(err). Msg("Cannot parse client key") - c.String(http.StatusBadRequest, "") + ctx.String(http.StatusBadRequest, "") + return } req := tailcfg.MapRequest{} - err = decode(body, &req, &mKey, h.privateKey) + err = decode(body, &req, &machineKey, h.privateKey) if err != nil { log.Error(). Str("handler", "PollNetMap"). Err(err). Msg("Cannot decode message") - c.String(http.StatusBadRequest, "") + ctx.String(http.StatusBadRequest, "") + return } - m, err := h.GetMachineByMachineKey(mKey.HexString()) + machine, err := h.GetMachineByMachineKey(machineKey) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { log.Warn(). Str("handler", "PollNetMap"). - Msgf("Ignoring request, cannot find machine with key %s", mKey.HexString()) - c.String(http.StatusUnauthorized, "") + Msgf("Ignoring request, cannot find machine with key %s", machineKey.String()) + ctx.String(http.StatusUnauthorized, "") + return } log.Error(). Str("handler", "PollNetMap"). - Msgf("Failed to fetch machine from the database with Machine key: %s", mKey.HexString()) - c.String(http.StatusInternalServerError, "") + Msgf("Failed to fetch machine from the database with Machine key: %s", machineKey.String()) + ctx.String(http.StatusInternalServerError, "") + + return } log.Trace(). Str("handler", "PollNetMap"). - Str("id", c.Param("id")). - Str("machine", m.Name). + Str("id", ctx.Param("id")). + Str("machine", machine.Name). Msg("Found machine in database") hostinfo, _ := json.Marshal(req.Hostinfo) - m.Name = req.Hostinfo.Hostname - m.HostInfo = datatypes.JSON(hostinfo) - m.DiscoKey = wgkey.Key(req.DiscoKey).HexString() + machine.Name = req.Hostinfo.Hostname + machine.HostInfo = datatypes.JSON(hostinfo) + machine.DiscoKey = DiscoPublicKeyStripPrefix(req.DiscoKey) now := time.Now().UTC() // From Tailscale client: @@ -87,20 +99,21 @@ func (h *Headscale) PollNetMapHandler(c *gin.Context) { // before their first real endpoint update. if !req.ReadOnly { endpoints, _ := json.Marshal(req.Endpoints) - m.Endpoints = datatypes.JSON(endpoints) - m.LastSeen = &now + machine.Endpoints = datatypes.JSON(endpoints) + machine.LastSeen = &now } - h.db.Save(&m) + h.db.Updates(machine) - data, err := h.getMapResponse(mKey, req, m) + data, err := h.getMapResponse(machineKey, req, machine) if err != nil { log.Error(). Str("handler", "PollNetMap"). - Str("id", c.Param("id")). - Str("machine", m.Name). + Str("id", ctx.Param("id")). + Str("machine", machine.Name). Err(err). Msg("Failed to get Map response") - c.String(http.StatusInternalServerError, ":(") + ctx.String(http.StatusInternalServerError, ":(") + return } @@ -111,8 +124,8 @@ func (h *Headscale) PollNetMapHandler(c *gin.Context) { // Details on the protocol can be found in https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go#L696 log.Debug(). Str("handler", "PollNetMap"). - Str("id", c.Param("id")). - Str("machine", m.Name). + Str("id", ctx.Param("id")). + Str("machine", machine.Name). Bool("readOnly", req.ReadOnly). Bool("omitPeers", req.OmitPeers). Bool("stream", req.Stream). @@ -121,15 +134,16 @@ func (h *Headscale) PollNetMapHandler(c *gin.Context) { if req.ReadOnly { log.Info(). Str("handler", "PollNetMap"). - Str("machine", m.Name). + Str("machine", machine.Name). Msg("Client is starting up. Probably interested in a DERP map") - c.Data(200, "application/json; charset=utf-8", data) + ctx.Data(http.StatusOK, "application/json; charset=utf-8", data) + return } // There has been an update to _any_ of the nodes that the other nodes would // need to know about - h.setLastStateChangeToNow(m.Namespace.Name) + h.setLastStateChangeToNow(machine.Namespace.Name) // The request is not ReadOnly, so we need to set up channels for updating // peers via longpoll @@ -137,8 +151,8 @@ func (h *Headscale) PollNetMapHandler(c *gin.Context) { // Only create update channel if it has not been created log.Trace(). Str("handler", "PollNetMap"). - Str("id", c.Param("id")). - Str("machine", m.Name). + Str("id", ctx.Param("id")). + Str("machine", machine.Name). Msg("Loading or creating update channel") updateChan := make(chan struct{}) @@ -152,46 +166,59 @@ func (h *Headscale) PollNetMapHandler(c *gin.Context) { if req.OmitPeers && !req.Stream { log.Info(). Str("handler", "PollNetMap"). - Str("machine", m.Name). + Str("machine", machine.Name). Msg("Client sent endpoint update and is ok with a response without peer list") - c.Data(200, "application/json; charset=utf-8", data) + ctx.Data(http.StatusOK, "application/json; charset=utf-8", data) // It sounds like we should update the nodes when we have received a endpoint update // even tho the comments in the tailscale code dont explicitly say so. - updateRequestsFromNode.WithLabelValues(m.Name, m.Namespace.Name, "endpoint-update").Inc() + updateRequestsFromNode.WithLabelValues(machine.Name, machine.Namespace.Name, "endpoint-update"). + Inc() go func() { updateChan <- struct{}{} }() + return } else if req.OmitPeers && req.Stream { log.Warn(). Str("handler", "PollNetMap"). - Str("machine", m.Name). + Str("machine", machine.Name). Msg("Ignoring request, don't know how to handle it") - c.String(http.StatusBadRequest, "") + ctx.String(http.StatusBadRequest, "") + return } log.Info(). Str("handler", "PollNetMap"). - Str("machine", m.Name). + Str("machine", machine.Name). Msg("Client is ready to access the tailnet") log.Info(). Str("handler", "PollNetMap"). - Str("machine", m.Name). + Str("machine", machine.Name). Msg("Sending initial map") go func() { pollDataChan <- data }() log.Info(). Str("handler", "PollNetMap"). - Str("machine", m.Name). + Str("machine", machine.Name). Msg("Notifying peers") - updateRequestsFromNode.WithLabelValues(m.Name, m.Namespace.Name, "full-update").Inc() + updateRequestsFromNode.WithLabelValues(machine.Name, machine.Namespace.Name, "full-update"). + Inc() go func() { updateChan <- struct{}{} }() - h.PollNetMapStream(c, m, req, mKey, pollDataChan, keepAliveChan, updateChan, cancelKeepAlive) + h.PollNetMapStream( + ctx, + machine, + req, + machineKey, + pollDataChan, + keepAliveChan, + updateChan, + cancelKeepAlive, + ) log.Trace(). Str("handler", "PollNetMap"). - Str("id", c.Param("id")). - Str("machine", m.Name). + Str("id", ctx.Param("id")). + Str("machine", machine.Name). Msg("Finished stream, closing PollNetMap session") } @@ -199,165 +226,207 @@ func (h *Headscale) PollNetMapHandler(c *gin.Context) { // stream logic, ensuring we communicate updates and data // to the connected clients. func (h *Headscale) PollNetMapStream( - c *gin.Context, - m *Machine, - req tailcfg.MapRequest, - mKey wgkey.Key, + ctx *gin.Context, + machine *Machine, + mapRequest tailcfg.MapRequest, + machineKey key.MachinePublic, pollDataChan chan []byte, keepAliveChan chan []byte, updateChan chan struct{}, cancelKeepAlive chan struct{}, ) { - go h.scheduledPollWorker(cancelKeepAlive, updateChan, keepAliveChan, mKey, req, m) + go h.scheduledPollWorker( + cancelKeepAlive, + updateChan, + keepAliveChan, + machineKey, + mapRequest, + machine, + ) - c.Stream(func(w io.Writer) bool { + ctx.Stream(func(writer io.Writer) bool { log.Trace(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Msg("Waiting for data to stream...") log.Trace(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Msgf("pollData is %#v, keepAliveChan is %#v, updateChan is %#v", pollDataChan, keepAliveChan, updateChan) select { case data := <-pollDataChan: log.Trace(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "pollData"). Int("bytes", len(data)). Msg("Sending data received via pollData channel") - _, err := w.Write(data) + _, err := writer.Write(data) if err != nil { log.Error(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "pollData"). Err(err). Msg("Cannot write data") + return false } log.Trace(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "pollData"). Int("bytes", len(data)). Msg("Data from pollData channel written successfully") // TODO(kradalby): Abstract away all the database calls, this can cause race conditions // when an outdated machine object is kept alive, e.g. db is update from // command line, but then overwritten. - err = h.UpdateMachine(m) + err = h.UpdateMachine(machine) if err != nil { log.Error(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "pollData"). Err(err). Msg("Cannot update machine from database") + + // client has been removed from database + // since the stream opened, terminate connection. + return false } now := time.Now().UTC() - m.LastSeen = &now + machine.LastSeen = &now - lastStateUpdate.WithLabelValues(m.Namespace.Name, m.Name).Set(float64(now.Unix())) - m.LastSuccessfulUpdate = &now + lastStateUpdate.WithLabelValues(machine.Namespace.Name, machine.Name). + Set(float64(now.Unix())) + machine.LastSuccessfulUpdate = &now + + err = h.TouchMachine(machine) + if err != nil { + log.Error(). + Str("handler", "PollNetMapStream"). + Str("machine", machine.Name). + Str("channel", "pollData"). + Err(err). + Msg("Cannot update machine LastSuccessfulUpdate") + } else { + log.Trace(). + Str("handler", "PollNetMapStream"). + Str("machine", machine.Name). + Str("channel", "pollData"). + Int("bytes", len(data)). + Msg("Machine entry in database updated successfully after sending pollData") + } - h.db.Save(&m) - log.Trace(). - Str("handler", "PollNetMapStream"). - Str("machine", m.Name). - Str("channel", "pollData"). - Int("bytes", len(data)). - Msg("Machine entry in database updated successfully after sending pollData") return true case data := <-keepAliveChan: log.Trace(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "keepAlive"). Int("bytes", len(data)). Msg("Sending keep alive message") - _, err := w.Write(data) + _, err := writer.Write(data) if err != nil { log.Error(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "keepAlive"). Err(err). Msg("Cannot write keep alive message") + return false } log.Trace(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "keepAlive"). Int("bytes", len(data)). Msg("Keep alive sent successfully") // TODO(kradalby): Abstract away all the database calls, this can cause race conditions // when an outdated machine object is kept alive, e.g. db is update from // command line, but then overwritten. - err = h.UpdateMachine(m) + err = h.UpdateMachine(machine) if err != nil { log.Error(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "keepAlive"). Err(err). Msg("Cannot update machine from database") + + // client has been removed from database + // since the stream opened, terminate connection. + return false } now := time.Now().UTC() - m.LastSeen = &now - h.db.Save(&m) - log.Trace(). - Str("handler", "PollNetMapStream"). - Str("machine", m.Name). - Str("channel", "keepAlive"). - Int("bytes", len(data)). - Msg("Machine updated successfully after sending keep alive") + machine.LastSeen = &now + err = h.TouchMachine(machine) + if err != nil { + log.Error(). + Str("handler", "PollNetMapStream"). + Str("machine", machine.Name). + Str("channel", "keepAlive"). + Err(err). + Msg("Cannot update machine LastSeen") + } else { + log.Trace(). + Str("handler", "PollNetMapStream"). + Str("machine", machine.Name). + Str("channel", "keepAlive"). + Int("bytes", len(data)). + Msg("Machine updated successfully after sending keep alive") + } + return true case <-updateChan: log.Trace(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "update"). Msg("Received a request for update") - updateRequestsReceivedOnChannel.WithLabelValues(m.Name, m.Namespace.Name).Inc() - if h.isOutdated(m) { + updateRequestsReceivedOnChannel.WithLabelValues(machine.Name, machine.Namespace.Name). + Inc() + if h.isOutdated(machine) { log.Debug(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). - Time("last_successful_update", *m.LastSuccessfulUpdate). - Time("last_state_change", h.getLastStateChange(m.Namespace.Name)). - Msgf("There has been updates since the last successful update to %s", m.Name) - data, err := h.getMapResponse(mKey, req, m) + Str("machine", machine.Name). + Time("last_successful_update", *machine.LastSuccessfulUpdate). + Time("last_state_change", h.getLastStateChange(machine.Namespace.Name)). + Msgf("There has been updates since the last successful update to %s", machine.Name) + data, err := h.getMapResponse(machineKey, mapRequest, machine) if err != nil { log.Error(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "update"). Err(err). Msg("Could not get the map update") } - _, err = w.Write(data) + _, err = writer.Write(data) if err != nil { log.Error(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "update"). Err(err). Msg("Could not write the map response") - updateRequestsSentToNode.WithLabelValues(m.Name, m.Namespace.Name, "failed").Inc() + updateRequestsSentToNode.WithLabelValues(machine.Name, machine.Namespace.Name, "failed"). + Inc() + return false } log.Trace(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "update"). Msg("Updated Map has been sent") - updateRequestsSentToNode.WithLabelValues(m.Name, m.Namespace.Name, "success").Inc() + updateRequestsSentToNode.WithLabelValues(machine.Name, machine.Namespace.Name, "success"). + Inc() // Keep track of the last successful update, // we sometimes end in a state were the update @@ -366,77 +435,103 @@ func (h *Headscale) PollNetMapStream( // TODO(kradalby): Abstract away all the database calls, this can cause race conditions // when an outdated machine object is kept alive, e.g. db is update from // command line, but then overwritten. - err = h.UpdateMachine(m) + err = h.UpdateMachine(machine) if err != nil { log.Error(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "update"). Err(err). Msg("Cannot update machine from database") + + // client has been removed from database + // since the stream opened, terminate connection. + return false } now := time.Now().UTC() - lastStateUpdate.WithLabelValues(m.Namespace.Name, m.Name).Set(float64(now.Unix())) - m.LastSuccessfulUpdate = &now + lastStateUpdate.WithLabelValues(machine.Namespace.Name, machine.Name). + Set(float64(now.Unix())) + machine.LastSuccessfulUpdate = &now - h.db.Save(&m) + err = h.TouchMachine(machine) + if err != nil { + log.Error(). + Str("handler", "PollNetMapStream"). + Str("machine", machine.Name). + Str("channel", "update"). + Err(err). + Msg("Cannot update machine LastSuccessfulUpdate") + } } else { log.Trace(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). - Time("last_successful_update", *m.LastSuccessfulUpdate). - Time("last_state_change", h.getLastStateChange(m.Namespace.Name)). - Msgf("%s is up to date", m.Name) + Str("machine", machine.Name). + Time("last_successful_update", *machine.LastSuccessfulUpdate). + Time("last_state_change", h.getLastStateChange(machine.Namespace.Name)). + Msgf("%s is up to date", machine.Name) } + return true - case <-c.Request.Context().Done(): + case <-ctx.Request.Context().Done(): log.Info(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Msg("The client has closed the connection") // TODO: Abstract away all the database calls, this can cause race conditions // when an outdated machine object is kept alive, e.g. db is update from // command line, but then overwritten. - err := h.UpdateMachine(m) + err := h.UpdateMachine(machine) if err != nil { log.Error(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "Done"). Err(err). Msg("Cannot update machine from database") + + // client has been removed from database + // since the stream opened, terminate connection. + return false } now := time.Now().UTC() - m.LastSeen = &now - h.db.Save(&m) + machine.LastSeen = &now + err = h.TouchMachine(machine) + if err != nil { + log.Error(). + Str("handler", "PollNetMapStream"). + Str("machine", machine.Name). + Str("channel", "Done"). + Err(err). + Msg("Cannot update machine LastSeen") + } log.Trace(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "Done"). Msg("Cancelling keepAlive channel") cancelKeepAlive <- struct{}{} log.Trace(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "Done"). Msg("Closing update channel") - //h.closeUpdateChannel(m) + // h.closeUpdateChannel(m) close(updateChan) log.Trace(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "Done"). Msg("Closing pollData channel") close(pollDataChan) log.Trace(). Str("handler", "PollNetMapStream"). - Str("machine", m.Name). + Str("machine", machine.Name). Str("channel", "Done"). Msg("Closing keepAliveChan channel") close(keepAliveChan) @@ -450,12 +545,12 @@ func (h *Headscale) scheduledPollWorker( cancelChan <-chan struct{}, updateChan chan<- struct{}, keepAliveChan chan<- []byte, - mKey wgkey.Key, - req tailcfg.MapRequest, - m *Machine, + machineKey key.MachinePublic, + mapRequest tailcfg.MapRequest, + machine *Machine, ) { - keepAliveTicker := time.NewTicker(60 * time.Second) - updateCheckerTicker := time.NewTicker(10 * time.Second) + keepAliveTicker := time.NewTicker(keepAliveInterval) + updateCheckerTicker := time.NewTicker(updateCheckInterval) for { select { @@ -463,27 +558,29 @@ func (h *Headscale) scheduledPollWorker( return case <-keepAliveTicker.C: - data, err := h.getMapKeepAliveResponse(mKey, req, m) + data, err := h.getMapKeepAliveResponse(machineKey, mapRequest) if err != nil { log.Error(). Str("func", "keepAlive"). Err(err). Msg("Error generating the keep alive msg") + return } log.Debug(). Str("func", "keepAlive"). - Str("machine", m.Name). + Str("machine", machine.Name). Msg("Sending keepalive") keepAliveChan <- data case <-updateCheckerTicker.C: log.Debug(). Str("func", "scheduledPollWorker"). - Str("machine", m.Name). + Str("machine", machine.Name). Msg("Sending update request") - updateRequestsFromNode.WithLabelValues(m.Name, m.Namespace.Name, "scheduled-update").Inc() + updateRequestsFromNode.WithLabelValues(machine.Name, machine.Namespace.Name, "scheduled-update"). + Inc() updateChan <- struct{}{} } } diff --git a/preauth_keys.go b/preauth_keys.go index de10cdb7..50bc4746 100644 --- a/preauth_keys.go +++ b/preauth_keys.go @@ -4,16 +4,22 @@ import ( "crypto/rand" "encoding/hex" "errors" + "strconv" "time" + v1 "github.com/juanfont/headscale/gen/go/headscale/v1" + "google.golang.org/protobuf/types/known/timestamppb" "gorm.io/gorm" ) -const errorAuthKeyNotFound = Error("AuthKey not found") -const errorAuthKeyExpired = Error("AuthKey expired") -const errSingleUseAuthKeyHasBeenUsed = Error("AuthKey has already been used") +const ( + errPreAuthKeyNotFound = Error("AuthKey not found") + errPreAuthKeyExpired = Error("AuthKey expired") + errSingleUseAuthKeyHasBeenUsed = Error("AuthKey has already been used") + errNamespaceMismatch = Error("namespace mismatch") +) -// PreAuthKey describes a pre-authorization key usable in a particular namespace +// PreAuthKey describes a pre-authorization key usable in a particular namespace. type PreAuthKey struct { ID uint64 `gorm:"primary_key"` Key string @@ -27,9 +33,14 @@ type PreAuthKey struct { Expiration *time.Time } -// CreatePreAuthKey creates a new PreAuthKey in a namespace, and returns it -func (h *Headscale) CreatePreAuthKey(namespaceName string, reusable bool, ephemeral bool, expiration *time.Time) (*PreAuthKey, error) { - n, err := h.GetNamespace(namespaceName) +// CreatePreAuthKey creates a new PreAuthKey in a namespace, and returns it. +func (h *Headscale) CreatePreAuthKey( + namespaceName string, + reusable bool, + ephemeral bool, + expiration *time.Time, +) (*PreAuthKey, error) { + namespace, err := h.GetNamespace(namespaceName) if err != nil { return nil, err } @@ -40,35 +51,36 @@ func (h *Headscale) CreatePreAuthKey(namespaceName string, reusable bool, epheme return nil, err } - k := PreAuthKey{ + key := PreAuthKey{ Key: kstr, - NamespaceID: n.ID, - Namespace: *n, + NamespaceID: namespace.ID, + Namespace: *namespace, Reusable: reusable, Ephemeral: ephemeral, CreatedAt: &now, Expiration: expiration, } - h.db.Save(&k) + h.db.Save(&key) - return &k, nil + return &key, nil } -// GetPreAuthKeys returns the list of PreAuthKeys for a namespace -func (h *Headscale) GetPreAuthKeys(namespaceName string) (*[]PreAuthKey, error) { - n, err := h.GetNamespace(namespaceName) +// ListPreAuthKeys returns the list of PreAuthKeys for a namespace. +func (h *Headscale) ListPreAuthKeys(namespaceName string) ([]PreAuthKey, error) { + namespace, err := h.GetNamespace(namespaceName) if err != nil { return nil, err } keys := []PreAuthKey{} - if err := h.db.Preload("Namespace").Where(&PreAuthKey{NamespaceID: n.ID}).Find(&keys).Error; err != nil { + if err := h.db.Preload("Namespace").Where(&PreAuthKey{NamespaceID: namespace.ID}).Find(&keys).Error; err != nil { return nil, err } - return &keys, nil + + return keys, nil } -// GetPreAuthKey returns a PreAuthKey for a given key +// GetPreAuthKey returns a PreAuthKey for a given key. func (h *Headscale) GetPreAuthKey(namespace string, key string) (*PreAuthKey, error) { pak, err := h.checkKeyValidity(key) if err != nil { @@ -76,30 +88,44 @@ func (h *Headscale) GetPreAuthKey(namespace string, key string) (*PreAuthKey, er } if pak.Namespace.Name != namespace { - return nil, errors.New("Namespace mismatch") + return nil, errNamespaceMismatch } return pak, nil } -// MarkExpirePreAuthKey marks a PreAuthKey as expired -func (h *Headscale) MarkExpirePreAuthKey(k *PreAuthKey) error { +// DestroyPreAuthKey destroys a preauthkey. Returns error if the PreAuthKey +// does not exist. +func (h *Headscale) DestroyPreAuthKey(pak PreAuthKey) error { + if result := h.db.Unscoped().Delete(pak); result.Error != nil { + return result.Error + } + + return nil +} + +// MarkExpirePreAuthKey marks a PreAuthKey as expired. +func (h *Headscale) ExpirePreAuthKey(k *PreAuthKey) error { if err := h.db.Model(&k).Update("Expiration", time.Now()).Error; err != nil { return err } + return nil } // 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 +// If returns no error and a PreAuthKey, it can be used. func (h *Headscale) checkKeyValidity(k string) (*PreAuthKey, error) { pak := PreAuthKey{} - if result := h.db.Preload("Namespace").First(&pak, "key = ?", k); errors.Is(result.Error, gorm.ErrRecordNotFound) { - return nil, errorAuthKeyNotFound + if result := h.db.Preload("Namespace").First(&pak, "key = ?", k); errors.Is( + result.Error, + gorm.ErrRecordNotFound, + ) { + return nil, errPreAuthKeyNotFound } if pak.Expiration != nil && pak.Expiration.Before(time.Now()) { - return nil, errorAuthKeyExpired + return nil, errPreAuthKeyExpired } if pak.Reusable || pak.Ephemeral { // we don't need to check if has been used before @@ -124,5 +150,27 @@ func (h *Headscale) generateKey() (string, error) { if _, err := rand.Read(bytes); err != nil { return "", err } + return hex.EncodeToString(bytes), nil } + +func (key *PreAuthKey) toProto() *v1.PreAuthKey { + protoKey := v1.PreAuthKey{ + Namespace: key.Namespace.Name, + Id: strconv.FormatUint(key.ID, Base10), + Key: key.Key, + Ephemeral: key.Ephemeral, + Reusable: key.Reusable, + Used: key.Used, + } + + if key.Expiration != nil { + protoKey.Expiration = timestamppb.New(*key.Expiration) + } + + if key.CreatedAt != nil { + protoKey.CreatedAt = timestamppb.New(*key.CreatedAt) + } + + return &protoKey +} diff --git a/preauth_keys_test.go b/preauth_keys_test.go index f8973eaf..f8cf276d 100644 --- a/preauth_keys_test.go +++ b/preauth_keys_test.go @@ -7,189 +7,189 @@ import ( ) func (*Suite) TestCreatePreAuthKey(c *check.C) { - _, err := h.CreatePreAuthKey("bogus", true, false, nil) + _, err := app.CreatePreAuthKey("bogus", true, false, nil) c.Assert(err, check.NotNil) - n, err := h.CreateNamespace("test") + namespace, err := app.CreateNamespace("test") c.Assert(err, check.IsNil) - k, err := h.CreatePreAuthKey(n.Name, true, false, nil) + key, err := app.CreatePreAuthKey(namespace.Name, true, false, nil) c.Assert(err, check.IsNil) // Did we get a valid key? - c.Assert(k.Key, check.NotNil) - c.Assert(len(k.Key), check.Equals, 48) + c.Assert(key.Key, check.NotNil) + c.Assert(len(key.Key), check.Equals, 48) // Make sure the Namespace association is populated - c.Assert(k.Namespace.Name, check.Equals, n.Name) + c.Assert(key.Namespace.Name, check.Equals, namespace.Name) - _, err = h.GetPreAuthKeys("bogus") + _, err = app.ListPreAuthKeys("bogus") c.Assert(err, check.NotNil) - keys, err := h.GetPreAuthKeys(n.Name) + keys, err := app.ListPreAuthKeys(namespace.Name) c.Assert(err, check.IsNil) - c.Assert(len(*keys), check.Equals, 1) + c.Assert(len(keys), check.Equals, 1) // Make sure the Namespace association is populated - c.Assert((*keys)[0].Namespace.Name, check.Equals, n.Name) + c.Assert((keys)[0].Namespace.Name, check.Equals, namespace.Name) } func (*Suite) TestExpiredPreAuthKey(c *check.C) { - n, err := h.CreateNamespace("test2") + namespace, err := app.CreateNamespace("test2") c.Assert(err, check.IsNil) now := time.Now() - pak, err := h.CreatePreAuthKey(n.Name, true, false, &now) + pak, err := app.CreatePreAuthKey(namespace.Name, true, false, &now) c.Assert(err, check.IsNil) - p, err := h.checkKeyValidity(pak.Key) - c.Assert(err, check.Equals, errorAuthKeyExpired) - c.Assert(p, check.IsNil) + key, err := app.checkKeyValidity(pak.Key) + c.Assert(err, check.Equals, errPreAuthKeyExpired) + c.Assert(key, check.IsNil) } func (*Suite) TestPreAuthKeyDoesNotExist(c *check.C) { - p, err := h.checkKeyValidity("potatoKey") - c.Assert(err, check.Equals, errorAuthKeyNotFound) - c.Assert(p, check.IsNil) + key, err := app.checkKeyValidity("potatoKey") + c.Assert(err, check.Equals, errPreAuthKeyNotFound) + c.Assert(key, check.IsNil) } func (*Suite) TestValidateKeyOk(c *check.C) { - n, err := h.CreateNamespace("test3") + namespace, err := app.CreateNamespace("test3") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, true, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, true, false, nil) c.Assert(err, check.IsNil) - p, err := h.checkKeyValidity(pak.Key) + key, err := app.checkKeyValidity(pak.Key) c.Assert(err, check.IsNil) - c.Assert(p.ID, check.Equals, pak.ID) + c.Assert(key.ID, check.Equals, pak.ID) } func (*Suite) TestAlreadyUsedKey(c *check.C) { - n, err := h.CreateNamespace("test4") + namespace, err := app.CreateNamespace("test4") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, false, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) c.Assert(err, check.IsNil) - m := Machine{ + machine := Machine{ ID: 0, MachineKey: "foo", NodeKey: "bar", DiscoKey: "faa", Name: "testest", - NamespaceID: n.ID, + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } - h.db.Save(&m) + app.db.Save(&machine) - p, err := h.checkKeyValidity(pak.Key) + key, err := app.checkKeyValidity(pak.Key) c.Assert(err, check.Equals, errSingleUseAuthKeyHasBeenUsed) - c.Assert(p, check.IsNil) + c.Assert(key, check.IsNil) } func (*Suite) TestReusableBeingUsedKey(c *check.C) { - n, err := h.CreateNamespace("test5") + namespace, err := app.CreateNamespace("test5") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, true, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, true, false, nil) c.Assert(err, check.IsNil) - m := Machine{ + machine := Machine{ ID: 1, MachineKey: "foo", NodeKey: "bar", DiscoKey: "faa", Name: "testest", - NamespaceID: n.ID, + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } - h.db.Save(&m) + app.db.Save(&machine) - p, err := h.checkKeyValidity(pak.Key) + key, err := app.checkKeyValidity(pak.Key) c.Assert(err, check.IsNil) - c.Assert(p.ID, check.Equals, pak.ID) + c.Assert(key.ID, check.Equals, pak.ID) } func (*Suite) TestNotReusableNotBeingUsedKey(c *check.C) { - n, err := h.CreateNamespace("test6") + namespace, err := app.CreateNamespace("test6") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, false, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) c.Assert(err, check.IsNil) - p, err := h.checkKeyValidity(pak.Key) + key, err := app.checkKeyValidity(pak.Key) c.Assert(err, check.IsNil) - c.Assert(p.ID, check.Equals, pak.ID) + c.Assert(key.ID, check.Equals, pak.ID) } func (*Suite) TestEphemeralKey(c *check.C) { - n, err := h.CreateNamespace("test7") + namespace, err := app.CreateNamespace("test7") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, false, true, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, false, true, nil) c.Assert(err, check.IsNil) now := time.Now() - m := Machine{ + machine := Machine{ ID: 0, MachineKey: "foo", NodeKey: "bar", DiscoKey: "faa", Name: "testest", - NamespaceID: n.ID, + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, LastSeen: &now, AuthKeyID: uint(pak.ID), } - h.db.Save(&m) + app.db.Save(&machine) - _, err = h.checkKeyValidity(pak.Key) + _, err = app.checkKeyValidity(pak.Key) // Ephemeral keys are by definition reusable c.Assert(err, check.IsNil) - _, err = h.GetMachine("test7", "testest") + _, err = app.GetMachine("test7", "testest") c.Assert(err, check.IsNil) - h.expireEphemeralNodesWorker() + app.expireEphemeralNodesWorker() // The machine record should have been deleted - _, err = h.GetMachine("test7", "testest") + _, err = app.GetMachine("test7", "testest") c.Assert(err, check.NotNil) } func (*Suite) TestExpirePreauthKey(c *check.C) { - n, err := h.CreateNamespace("test3") + namespace, err := app.CreateNamespace("test3") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, true, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, true, false, nil) c.Assert(err, check.IsNil) c.Assert(pak.Expiration, check.IsNil) - err = h.MarkExpirePreAuthKey(pak) + err = app.ExpirePreAuthKey(pak) c.Assert(err, check.IsNil) c.Assert(pak.Expiration, check.NotNil) - p, err := h.checkKeyValidity(pak.Key) - c.Assert(err, check.Equals, errorAuthKeyExpired) - c.Assert(p, check.IsNil) + key, err := app.checkKeyValidity(pak.Key) + c.Assert(err, check.Equals, errPreAuthKeyExpired) + c.Assert(key, check.IsNil) } func (*Suite) TestNotReusableMarkedAsUsed(c *check.C) { - n, err := h.CreateNamespace("test6") + namespace, err := app.CreateNamespace("test6") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, false, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) c.Assert(err, check.IsNil) pak.Used = true - h.db.Save(&pak) + app.db.Save(&pak) - _, err = h.checkKeyValidity(pak.Key) + _, err = app.checkKeyValidity(pak.Key) c.Assert(err, check.Equals, errSingleUseAuthKeyHasBeenUsed) } diff --git a/proto/buf.lock b/proto/buf.lock new file mode 100644 index 00000000..03cd7b89 --- /dev/null +++ b/proto/buf.lock @@ -0,0 +1,24 @@ +# Generated by buf. DO NOT EDIT. +version: v1 +deps: + - remote: buf.build + owner: googleapis + repository: googleapis + branch: main + commit: cd101b0abb7b4404a0b1ecc1afd4ce10 + digest: b1-H4GHwHVHcJBbVPg-Cdmnx812reFCDQws_QoQ0W2hYQA= + create_time: 2021-10-23T15:04:06.087748Z + - remote: buf.build + owner: grpc-ecosystem + repository: grpc-gateway + branch: main + commit: ff83506eb9cc4cf8972f49ce87e6ed3e + digest: b1-iLPHgLaoeWWinMiXXqPnxqE4BThtY3eSbswVGh9GOGI= + create_time: 2021-10-23T16:26:52.283938Z + - remote: buf.build + owner: ufoundit-dev + repository: protoc-gen-gorm + branch: main + commit: e2ecbaa0d37843298104bd29fd866df8 + digest: b1-SV9yKH_8P-IKTOlHZxP-bb0ALANYeEqH_mtPA0EWfLc= + create_time: 2021-10-08T06:03:05.64876Z diff --git a/proto/buf.yaml b/proto/buf.yaml new file mode 100644 index 00000000..7e524ba0 --- /dev/null +++ b/proto/buf.yaml @@ -0,0 +1,12 @@ +version: v1 +lint: + use: + - DEFAULT +breaking: + use: + - FILE + +deps: + - buf.build/googleapis/googleapis + - buf.build/grpc-ecosystem/grpc-gateway + - buf.build/ufoundit-dev/protoc-gen-gorm diff --git a/proto/headscale/v1/device.proto b/proto/headscale/v1/device.proto new file mode 100644 index 00000000..207ff374 --- /dev/null +++ b/proto/headscale/v1/device.proto @@ -0,0 +1,83 @@ +syntax = "proto3"; +package headscale.v1; +option go_package = "github.com/juanfont/headscale/gen/go/v1"; + +import "google/protobuf/timestamp.proto"; + +// This is a potential reimplementation of Tailscale's API +// https://github.com/tailscale/tailscale/blob/main/api.md + +message Latency { + float latency_ms = 1; + bool preferred = 2; +} + +message ClientSupports { + bool hair_pinning = 1; + bool ipv6 = 2; + bool pcp = 3; + bool pmp = 4; + bool udp = 5; + bool upnp = 6; +} + +message ClientConnectivity { + repeated string endpoints = 1; + string derp = 2; + bool mapping_varies_by_dest_ip = 3; + map latency = 4; + ClientSupports client_supports = 5; +} + +message GetDeviceRequest { + string id = 1; +} + +message GetDeviceResponse { + repeated string addresses = 1; + string id = 2; + string user = 3; + string name = 4; + string hostname = 5; + string client_version = 6; + bool update_available = 7; + string os = 8; + google.protobuf.Timestamp created = 9; + google.protobuf.Timestamp last_seen = 10; + bool key_expiry_disabled = 11; + google.protobuf.Timestamp expires = 12; + bool authorized = 13; + bool is_external = 14; + string machine_key = 15; + string node_key = 16; + bool blocks_incoming_connections = 17; + repeated string enabled_routes = 18; + repeated string advertised_routes = 19; + ClientConnectivity client_connectivity = 20; +} + +message DeleteDeviceRequest { + string id = 1; +} + +message DeleteDeviceResponse { +} + +message GetDeviceRoutesRequest { + string id = 1; +} + +message GetDeviceRoutesResponse { + repeated string enabled_routes = 1; + repeated string advertised_routes = 2; +} + +message EnableDeviceRoutesRequest { + string id = 1; + repeated string routes = 2; +} + +message EnableDeviceRoutesResponse { + repeated string enabled_routes = 1; + repeated string advertised_routes = 2; +} diff --git a/proto/headscale/v1/headscale.proto b/proto/headscale/v1/headscale.proto new file mode 100644 index 00000000..f7332a88 --- /dev/null +++ b/proto/headscale/v1/headscale.proto @@ -0,0 +1,158 @@ +syntax = "proto3"; +package headscale.v1; +option go_package = "github.com/juanfont/headscale/gen/go/v1"; + +import "google/api/annotations.proto"; + +import "headscale/v1/namespace.proto"; +import "headscale/v1/preauthkey.proto"; +import "headscale/v1/machine.proto"; +import "headscale/v1/routes.proto"; +// import "headscale/v1/device.proto"; + +service HeadscaleService { + // --- Namespace start --- + rpc GetNamespace(GetNamespaceRequest) returns (GetNamespaceResponse) { + option (google.api.http) = { + get: "/api/v1/namespace/{name}" + }; + } + + rpc CreateNamespace(CreateNamespaceRequest) returns (CreateNamespaceResponse) { + option (google.api.http) = { + post: "/api/v1/namespace" + body: "*" + }; + } + + rpc RenameNamespace(RenameNamespaceRequest) returns (RenameNamespaceResponse) { + option (google.api.http) = { + post: "/api/v1/namespace/{old_name}/rename/{new_name}" + }; + } + + rpc DeleteNamespace(DeleteNamespaceRequest) returns (DeleteNamespaceResponse) { + option (google.api.http) = { + delete: "/api/v1/namespace/{name}" + }; + } + + rpc ListNamespaces(ListNamespacesRequest) returns (ListNamespacesResponse) { + option (google.api.http) = { + get: "/api/v1/namespace" + }; + } + // --- Namespace end --- + + // --- PreAuthKeys start --- + rpc CreatePreAuthKey(CreatePreAuthKeyRequest) returns (CreatePreAuthKeyResponse) { + option (google.api.http) = { + post: "/api/v1/preauthkey" + body: "*" + }; + } + + rpc ExpirePreAuthKey(ExpirePreAuthKeyRequest) returns (ExpirePreAuthKeyResponse) { + option (google.api.http) = { + post: "/api/v1/preauthkey/expire" + body: "*" + }; + } + + rpc ListPreAuthKeys(ListPreAuthKeysRequest) returns (ListPreAuthKeysResponse) { + option (google.api.http) = { + get: "/api/v1/preauthkey" + }; + } + // --- PreAuthKeys end --- + + // --- Machine start --- + rpc DebugCreateMachine(DebugCreateMachineRequest) returns (DebugCreateMachineResponse) { + option (google.api.http) = { + post: "/api/v1/debug/machine" + body: "*" + }; + } + + rpc GetMachine(GetMachineRequest) returns (GetMachineResponse) { + option (google.api.http) = { + get: "/api/v1/machine/{machine_id}" + }; + } + + rpc RegisterMachine(RegisterMachineRequest) returns (RegisterMachineResponse) { + option (google.api.http) = { + post: "/api/v1/machine/register" + }; + } + + rpc DeleteMachine(DeleteMachineRequest) returns (DeleteMachineResponse) { + option (google.api.http) = { + delete: "/api/v1/machine/{machine_id}" + }; + } + + rpc ExpireMachine(ExpireMachineRequest) returns (ExpireMachineResponse) { + option (google.api.http) = { + post: "/api/v1/machine/{machine_id}/expire" + }; + } + + rpc ListMachines(ListMachinesRequest) returns (ListMachinesResponse) { + option (google.api.http) = { + 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 --- + rpc GetMachineRoute(GetMachineRouteRequest) returns (GetMachineRouteResponse) { + option (google.api.http) = { + get: "/api/v1/machine/{machine_id}/routes" + }; + } + + rpc EnableMachineRoutes(EnableMachineRoutesRequest) returns (EnableMachineRoutesResponse) { + option (google.api.http) = { + post: "/api/v1/machine/{machine_id}/routes" + }; + } + // --- Route end --- + + // Implement Tailscale API + // rpc GetDevice(GetDeviceRequest) returns(GetDeviceResponse) { + // option(google.api.http) = { + // get : "/api/v1/device/{id}" + // }; + // } + + // rpc DeleteDevice(DeleteDeviceRequest) returns(DeleteDeviceResponse) { + // option(google.api.http) = { + // delete : "/api/v1/device/{id}" + // }; + // } + + // rpc GetDeviceRoutes(GetDeviceRoutesRequest) returns(GetDeviceRoutesResponse) { + // option(google.api.http) = { + // get : "/api/v1/device/{id}/routes" + // }; + // } + + // rpc EnableDeviceRoutes(EnableDeviceRoutesRequest) returns(EnableDeviceRoutesResponse) { + // option(google.api.http) = { + // post : "/api/v1/device/{id}/routes" + // }; + // } +} diff --git a/proto/headscale/v1/machine.proto b/proto/headscale/v1/machine.proto new file mode 100644 index 00000000..ed99f002 --- /dev/null +++ b/proto/headscale/v1/machine.proto @@ -0,0 +1,110 @@ +syntax = "proto3"; +package headscale.v1; +option go_package = "github.com/juanfont/headscale/gen/go/v1"; + +import "google/protobuf/timestamp.proto"; +import "headscale/v1/namespace.proto"; +import "headscale/v1/preauthkey.proto"; + +enum RegisterMethod { + REGISTER_METHOD_UNSPECIFIED = 0; + REGISTER_METHOD_AUTH_KEY = 1; + REGISTER_METHOD_CLI = 2; + REGISTER_METHOD_OIDC = 3; +} + +message Machine { + uint64 id = 1; + string machine_key = 2; + string node_key = 3; + string disco_key = 4; + string ip_address = 5; + 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; + + PreAuthKey pre_auth_key = 13; + + google.protobuf.Timestamp created_at = 14; + // google.protobuf.Timestamp updated_at = 14; + // google.protobuf.Timestamp deleted_at = 15; + + // bytes host_info = 15; + // bytes endpoints = 16; + // bytes enabled_routes = 17; +} + +message RegisterMachineRequest { + string namespace = 1; + string key = 2; +} + +message RegisterMachineResponse { + Machine machine = 1; +} + +message GetMachineRequest { + uint64 machine_id = 1; +} + +message GetMachineResponse { + Machine machine = 1; +} + +message DeleteMachineRequest { + uint64 machine_id = 1; +} + +message DeleteMachineResponse { +} + +message ExpireMachineRequest { + uint64 machine_id = 1; +} + +message ExpireMachineResponse { + Machine machine = 1; +} + +message ListMachinesRequest { + string namespace = 1; +} + +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; + string name = 3; + repeated string routes = 4; +} + +message DebugCreateMachineResponse { + Machine machine = 1; +} diff --git a/proto/headscale/v1/namespace.proto b/proto/headscale/v1/namespace.proto new file mode 100644 index 00000000..997b74c3 --- /dev/null +++ b/proto/headscale/v1/namespace.proto @@ -0,0 +1,50 @@ +syntax = "proto3"; +package headscale.v1; +option go_package = "github.com/juanfont/headscale/gen/go/v1"; + +import "google/protobuf/timestamp.proto"; + +message Namespace { + string id = 1; + string name = 2; + google.protobuf.Timestamp created_at = 3; +} + +message GetNamespaceRequest { + string name = 1; +} + +message GetNamespaceResponse { + Namespace namespace = 1; +} + +message CreateNamespaceRequest { + string name = 1; +} + +message CreateNamespaceResponse { + Namespace namespace = 1; +} + +message RenameNamespaceRequest { + string old_name = 1; + string new_name = 2; +} + +message RenameNamespaceResponse { + Namespace namespace = 1; +} + +message DeleteNamespaceRequest { + string name = 1; +} + +message DeleteNamespaceResponse { +} + +message ListNamespacesRequest { +} + +message ListNamespacesResponse { + repeated Namespace namespaces = 1; +} diff --git a/proto/headscale/v1/preauthkey.proto b/proto/headscale/v1/preauthkey.proto new file mode 100644 index 00000000..a945ba01 --- /dev/null +++ b/proto/headscale/v1/preauthkey.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; +package headscale.v1; +option go_package = "github.com/juanfont/headscale/gen/go/v1"; + +import "google/protobuf/timestamp.proto"; + +message PreAuthKey { + string namespace = 1; + string id = 2; + string key = 3; + bool reusable = 4; + bool ephemeral = 5; + bool used = 6; + google.protobuf.Timestamp expiration = 7; + google.protobuf.Timestamp created_at = 8; +} + +message CreatePreAuthKeyRequest { + string namespace = 1; + bool reusable = 2; + bool ephemeral = 3; + google.protobuf.Timestamp expiration = 4; +} + +message CreatePreAuthKeyResponse { + PreAuthKey pre_auth_key = 1; +} + +message ExpirePreAuthKeyRequest { + string namespace = 1; + string key = 2; +} + +message ExpirePreAuthKeyResponse { +} + +message ListPreAuthKeysRequest { + string namespace = 1; +} + +message ListPreAuthKeysResponse { + repeated PreAuthKey pre_auth_keys = 1; +} diff --git a/proto/headscale/v1/routes.proto b/proto/headscale/v1/routes.proto new file mode 100644 index 00000000..353c4294 --- /dev/null +++ b/proto/headscale/v1/routes.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; +package headscale.v1; +option go_package = "github.com/juanfont/headscale/gen/go/v1"; + +message Routes { + repeated string advertised_routes = 1; + repeated string enabled_routes = 2; +} + +message GetMachineRouteRequest { + uint64 machine_id = 1; +} + +message GetMachineRouteResponse { + Routes routes = 1; +} + +message EnableMachineRoutesRequest { + uint64 machine_id = 1; + repeated string routes = 2; +} + +message EnableMachineRoutesResponse { + Routes routes = 1; +} diff --git a/routes.go b/routes.go index 0ef01780..448095a5 100644 --- a/routes.go +++ b/routes.go @@ -2,38 +2,48 @@ package headscale import ( "encoding/json" - "fmt" - "strconv" - "github.com/pterm/pterm" "gorm.io/datatypes" "inet.af/netaddr" ) +const ( + errRouteIsNotAvailable = Error("route is not available") +) + +// Deprecated: use machine function instead // GetAdvertisedNodeRoutes returns the subnet routes advertised by a node (identified by -// namespace and node name) -func (h *Headscale) GetAdvertisedNodeRoutes(namespace string, nodeName string) (*[]netaddr.IPPrefix, error) { - m, err := h.GetMachine(namespace, nodeName) +// namespace and node name). +func (h *Headscale) GetAdvertisedNodeRoutes( + namespace string, + nodeName string, +) (*[]netaddr.IPPrefix, error) { + machine, err := h.GetMachine(namespace, nodeName) if err != nil { return nil, err } - hostInfo, err := m.GetHostInfo() + hostInfo, err := machine.GetHostInfo() if err != nil { return nil, err } + return &hostInfo.RoutableIPs, nil } +// Deprecated: use machine function instead // GetEnabledNodeRoutes returns the subnet routes enabled by a node (identified by -// namespace and node name) -func (h *Headscale) GetEnabledNodeRoutes(namespace string, nodeName string) ([]netaddr.IPPrefix, error) { - m, err := h.GetMachine(namespace, nodeName) +// namespace and node name). +func (h *Headscale) GetEnabledNodeRoutes( + namespace string, + nodeName string, +) ([]netaddr.IPPrefix, error) { + machine, err := h.GetMachine(namespace, nodeName) if err != nil { return nil, err } - data, err := m.EnabledRoutes.MarshalJSON() + data, err := machine.EnabledRoutes.MarshalJSON() if err != nil { return nil, err } @@ -56,8 +66,13 @@ func (h *Headscale) GetEnabledNodeRoutes(namespace string, nodeName string) ([]n return routes, nil } -// IsNodeRouteEnabled checks if a certain route has been enabled -func (h *Headscale) IsNodeRouteEnabled(namespace string, nodeName string, routeStr string) bool { +// Deprecated: use machine function instead +// IsNodeRouteEnabled checks if a certain route has been enabled. +func (h *Headscale) IsNodeRouteEnabled( + namespace string, + nodeName string, + routeStr string, +) bool { route, err := netaddr.ParseIPPrefix(routeStr) if err != nil { return false @@ -73,13 +88,19 @@ func (h *Headscale) IsNodeRouteEnabled(namespace string, nodeName string, routeS return true } } + return false } +// Deprecated: use EnableRoute in machine.go // EnableNodeRoute enables a subnet route advertised by a node (identified by -// namespace and node name) -func (h *Headscale) EnableNodeRoute(namespace string, nodeName string, routeStr string) error { - m, err := h.GetMachine(namespace, nodeName) +// namespace and node name). +func (h *Headscale) EnableNodeRoute( + namespace string, + nodeName string, + routeStr string, +) error { + machine, err := h.GetMachine(namespace, nodeName) if err != nil { return err } @@ -111,7 +132,7 @@ func (h *Headscale) EnableNodeRoute(namespace string, nodeName string, routeStr } if !available { - return fmt.Errorf("route (%s) is not available on node %s", nodeName, routeStr) + return errRouteIsNotAvailable } routes, err := json.Marshal(enabledRoutes) @@ -119,25 +140,13 @@ func (h *Headscale) EnableNodeRoute(namespace string, nodeName string, routeStr return err } - m.EnabledRoutes = datatypes.JSON(routes) - h.db.Save(&m) + machine.EnabledRoutes = datatypes.JSON(routes) + h.db.Save(&machine) - err = h.RequestMapUpdates(m.NamespaceID) + err = h.RequestMapUpdates(machine.NamespaceID) if err != nil { return err } return nil } - -// RoutesToPtables converts the list of routes to a nice table -func (h *Headscale) RoutesToPtables(namespace string, nodeName string, availableRoutes []netaddr.IPPrefix) pterm.TableData { - d := pterm.TableData{{"Route", "Enabled"}} - - for _, route := range availableRoutes { - enabled := h.IsNodeRouteEnabled(namespace, nodeName, route.String()) - - d = append(d, []string{route.String(), strconv.FormatBool(enabled)}) - } - return d -} diff --git a/routes_test.go b/routes_test.go index ad16d21c..94bda45b 100644 --- a/routes_test.go +++ b/routes_test.go @@ -10,57 +10,60 @@ import ( ) func (s *Suite) TestGetRoutes(c *check.C) { - n, err := h.CreateNamespace("test") + namespace, err := app.CreateNamespace("test") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, false, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) c.Assert(err, check.IsNil) - _, err = h.GetMachine("test", "test_get_route_machine") + _, err = app.GetMachine("test", "test_get_route_machine") c.Assert(err, check.NotNil) route, err := netaddr.ParseIPPrefix("10.0.0.0/24") c.Assert(err, check.IsNil) - hi := tailcfg.Hostinfo{ + hostInfo := tailcfg.Hostinfo{ RoutableIPs: []netaddr.IPPrefix{route}, } - hostinfo, err := json.Marshal(hi) + hostinfo, err := json.Marshal(hostInfo) c.Assert(err, check.IsNil) - m := Machine{ + machine := Machine{ ID: 0, MachineKey: "foo", NodeKey: "bar", DiscoKey: "faa", Name: "test_get_route_machine", - NamespaceID: n.ID, + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), HostInfo: datatypes.JSON(hostinfo), } - h.db.Save(&m) + app.db.Save(&machine) - r, err := h.GetAdvertisedNodeRoutes("test", "test_get_route_machine") + advertisedRoutes, err := app.GetAdvertisedNodeRoutes( + "test", + "test_get_route_machine", + ) c.Assert(err, check.IsNil) - c.Assert(len(*r), check.Equals, 1) + c.Assert(len(*advertisedRoutes), check.Equals, 1) - err = h.EnableNodeRoute("test", "test_get_route_machine", "192.168.0.0/24") + err = app.EnableNodeRoute("test", "test_get_route_machine", "192.168.0.0/24") c.Assert(err, check.NotNil) - err = h.EnableNodeRoute("test", "test_get_route_machine", "10.0.0.0/24") + err = app.EnableNodeRoute("test", "test_get_route_machine", "10.0.0.0/24") c.Assert(err, check.IsNil) } func (s *Suite) TestGetEnableRoutes(c *check.C) { - n, err := h.CreateNamespace("test") + namespace, err := app.CreateNamespace("test") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, false, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) c.Assert(err, check.IsNil) - _, err = h.GetMachine("test", "test_enable_route_machine") + _, err = app.GetMachine("test", "test_enable_route_machine") c.Assert(err, check.NotNil) route, err := netaddr.ParseIPPrefix( @@ -73,56 +76,68 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) { ) c.Assert(err, check.IsNil) - hi := tailcfg.Hostinfo{ + hostInfo := tailcfg.Hostinfo{ RoutableIPs: []netaddr.IPPrefix{route, route2}, } - hostinfo, err := json.Marshal(hi) + hostinfo, err := json.Marshal(hostInfo) c.Assert(err, check.IsNil) - m := Machine{ + machine := Machine{ ID: 0, MachineKey: "foo", NodeKey: "bar", DiscoKey: "faa", Name: "test_enable_route_machine", - NamespaceID: n.ID, + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), HostInfo: datatypes.JSON(hostinfo), } - h.db.Save(&m) + app.db.Save(&machine) - availableRoutes, err := h.GetAdvertisedNodeRoutes("test", "test_enable_route_machine") + availableRoutes, err := app.GetAdvertisedNodeRoutes( + "test", + "test_enable_route_machine", + ) c.Assert(err, check.IsNil) c.Assert(len(*availableRoutes), check.Equals, 2) - enabledRoutes, err := h.GetEnabledNodeRoutes("test", "test_enable_route_machine") + noEnabledRoutes, err := app.GetEnabledNodeRoutes( + "test", + "test_enable_route_machine", + ) c.Assert(err, check.IsNil) - c.Assert(len(enabledRoutes), check.Equals, 0) + c.Assert(len(noEnabledRoutes), check.Equals, 0) - err = h.EnableNodeRoute("test", "test_enable_route_machine", "192.168.0.0/24") + err = app.EnableNodeRoute("test", "test_enable_route_machine", "192.168.0.0/24") c.Assert(err, check.NotNil) - err = h.EnableNodeRoute("test", "test_enable_route_machine", "10.0.0.0/24") + err = app.EnableNodeRoute("test", "test_enable_route_machine", "10.0.0.0/24") c.Assert(err, check.IsNil) - enabledRoutes1, err := h.GetEnabledNodeRoutes("test", "test_enable_route_machine") + enabledRoutes, err := app.GetEnabledNodeRoutes("test", "test_enable_route_machine") c.Assert(err, check.IsNil) - c.Assert(len(enabledRoutes1), check.Equals, 1) + c.Assert(len(enabledRoutes), check.Equals, 1) // Adding it twice will just let it pass through - err = h.EnableNodeRoute("test", "test_enable_route_machine", "10.0.0.0/24") + err = app.EnableNodeRoute("test", "test_enable_route_machine", "10.0.0.0/24") c.Assert(err, check.IsNil) - enabledRoutes2, err := h.GetEnabledNodeRoutes("test", "test_enable_route_machine") + enableRoutesAfterDoubleApply, err := app.GetEnabledNodeRoutes( + "test", + "test_enable_route_machine", + ) c.Assert(err, check.IsNil) - c.Assert(len(enabledRoutes2), check.Equals, 1) + c.Assert(len(enableRoutesAfterDoubleApply), check.Equals, 1) - err = h.EnableNodeRoute("test", "test_enable_route_machine", "150.0.10.0/25") + err = app.EnableNodeRoute("test", "test_enable_route_machine", "150.0.10.0/25") c.Assert(err, check.IsNil) - enabledRoutes3, err := h.GetEnabledNodeRoutes("test", "test_enable_route_machine") + enabledRoutesWithAdditionalRoute, err := app.GetEnabledNodeRoutes( + "test", + "test_enable_route_machine", + ) c.Assert(err, check.IsNil) - c.Assert(len(enabledRoutes3), check.Equals, 2) + c.Assert(len(enabledRoutesWithAdditionalRoute), check.Equals, 2) } diff --git a/sharing.go b/sharing.go index 879ed06f..be1689d5 100644 --- a/sharing.go +++ b/sharing.go @@ -2,11 +2,13 @@ package headscale import "gorm.io/gorm" -const errorSameNamespace = Error("Destination namespace same as origin") -const errorMachineAlreadyShared = Error("Node already shared to this namespace") -const errorMachineNotShared = Error("Machine not shared to this namespace") +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 +// SharedMachine is a join table to support sharing nodes between namespaces. type SharedMachine struct { gorm.Model MachineID uint64 @@ -15,48 +17,57 @@ type SharedMachine struct { Namespace Namespace } -// AddSharedMachineToNamespace adds a machine as a shared node to a namespace -func (h *Headscale) AddSharedMachineToNamespace(m *Machine, ns *Namespace) error { - if m.NamespaceID == ns.ID { - return errorSameNamespace +// 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 = ?", m.ID, ns.ID).Find(&sharedMachines).Error; err != nil { + 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 errorMachineAlreadyShared + return errMachineAlreadyShared } sharedMachine := SharedMachine{ - MachineID: m.ID, - Machine: *m, - NamespaceID: ns.ID, - Namespace: *ns, + 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(m *Machine, ns *Namespace) error { - if m.NamespaceID == ns.ID { - return errorSameNamespace +// 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 = ?", m.ID, ns.ID).Unscoped().Delete(&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 errorMachineNotShared + return errMachineNotShared } - err := h.RequestMapUpdates(ns.ID) + err := h.RequestMapUpdates(namespace.ID) if err != nil { return err } @@ -64,10 +75,10 @@ func (h *Headscale) RemoveSharedMachineFromNamespace(m *Machine, ns *Namespace) return nil } -// RemoveSharedMachineFromAllNamespaces removes a machine as a shared node from all namespaces -func (h *Headscale) RemoveSharedMachineFromAllNamespaces(m *Machine) error { +// 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 = ?", m.ID).Unscoped().Delete(&sharedMachine); result.Error != nil { + if result := h.db.Where("machine_id = ?", machine.ID).Unscoped().Delete(&sharedMachine); result.Error != nil { return result.Error } diff --git a/sharing_test.go b/sharing_test.go index 140b05f2..fd7634da 100644 --- a/sharing_test.go +++ b/sharing_test.go @@ -4,231 +4,337 @@ import ( "gopkg.in/check.v1" ) -func CreateNodeNamespace(c *check.C, namespace, node, key, IP string) (*Namespace, *Machine) { - n1, err := h.CreateNamespace(namespace) +func CreateNodeNamespace( + c *check.C, + namespaceName, node, key, ip string, +) (*Namespace, *Machine) { + namespace, err := app.CreateNamespace(namespaceName) c.Assert(err, check.IsNil) - pak1, err := h.CreatePreAuthKey(n1.Name, false, false, nil) + pak1, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) c.Assert(err, check.IsNil) - _, err = h.GetMachine(n1.Name, node) + _, err = app.GetMachine(namespace.Name, node) c.Assert(err, check.NotNil) - m1 := &Machine{ + machine := &Machine{ ID: 0, MachineKey: key, NodeKey: key, DiscoKey: key, Name: node, - NamespaceID: n1.ID, + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", - IPAddress: IP, + RegisterMethod: RegisterMethodAuthKey, + IPAddress: ip, AuthKeyID: uint(pak1.ID), } - h.db.Save(m1) + app.db.Save(machine) - _, err = h.GetMachine(n1.Name, m1.Name) + _, err = app.GetMachine(namespace.Name, machine.Name) c.Assert(err, check.IsNil) - return n1, m1 + return namespace, machine } func (s *Suite) TestBasicSharedNodesInNamespace(c *check.C) { - n1, m1 := CreateNodeNamespace(c, "shared1", "test_get_shared_nodes_1", "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", "100.64.0.1") - _, m2 := CreateNodeNamespace(c, "shared2", "test_get_shared_nodes_2", "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", "100.64.0.2") + 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", + ) - p1s, err := h.getPeers(m1) + peersOfMachine1BeforeShared, err := app.getPeers(machine1) c.Assert(err, check.IsNil) - c.Assert(len(p1s), check.Equals, 0) + c.Assert(len(peersOfMachine1BeforeShared), check.Equals, 0) - err = h.AddSharedMachineToNamespace(m2, n1) + err = app.AddSharedMachineToNamespace(machine2, namespace1) c.Assert(err, check.IsNil) - p1sAfter, err := h.getPeers(m1) + peersOfMachine1AfterShared, err := app.getPeers(machine1) c.Assert(err, check.IsNil) - c.Assert(len(p1sAfter), check.Equals, 1) - c.Assert(p1sAfter[0].ID, check.Equals, m2.ID) + c.Assert(len(peersOfMachine1AfterShared), check.Equals, 1) + c.Assert(peersOfMachine1AfterShared[0].ID, check.Equals, machine2.ID) } func (s *Suite) TestSameNamespace(c *check.C) { - n1, m1 := CreateNodeNamespace(c, "shared1", "test_get_shared_nodes_1", "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", "100.64.0.1") + namespace1, machine1 := CreateNodeNamespace( + c, + "shared1", + "test_get_shared_nodes_1", + "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", + "100.64.0.1", + ) - p1s, err := h.getPeers(m1) + peersOfMachine1BeforeShare, err := app.getPeers(machine1) c.Assert(err, check.IsNil) - c.Assert(len(p1s), check.Equals, 0) + c.Assert(len(peersOfMachine1BeforeShare), check.Equals, 0) - err = h.AddSharedMachineToNamespace(m1, n1) - c.Assert(err, check.Equals, errorSameNamespace) + err = app.AddSharedMachineToNamespace(machine1, namespace1) + c.Assert(err, check.Equals, errSameNamespace) } func (s *Suite) TestUnshare(c *check.C) { - n1, m1 := CreateNodeNamespace(c, "shared1", "test_unshare_1", "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", "100.64.0.1") - _, m2 := CreateNodeNamespace(c, "shared2", "test_unshare_2", "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", "100.64.0.2") + 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", + ) - p1s, err := h.getPeers(m1) + peersOfMachine1BeforeShare, err := app.getPeers(machine1) c.Assert(err, check.IsNil) - c.Assert(len(p1s), check.Equals, 0) + c.Assert(len(peersOfMachine1BeforeShare), check.Equals, 0) - err = h.AddSharedMachineToNamespace(m2, n1) + err = app.AddSharedMachineToNamespace(machine2, namespace1) c.Assert(err, check.IsNil) - p1s, err = h.getShared(m1) + peersOfMachine1BeforeShare, err = app.getShared(machine1) c.Assert(err, check.IsNil) - c.Assert(len(p1s), check.Equals, 1) + c.Assert(len(peersOfMachine1BeforeShare), check.Equals, 1) - err = h.RemoveSharedMachineFromNamespace(m2, n1) + err = app.RemoveSharedMachineFromNamespace(machine2, namespace1) c.Assert(err, check.IsNil) - p1s, err = h.getShared(m1) + peersOfMachine1BeforeShare, err = app.getShared(machine1) c.Assert(err, check.IsNil) - c.Assert(len(p1s), check.Equals, 0) + c.Assert(len(peersOfMachine1BeforeShare), check.Equals, 0) - err = h.RemoveSharedMachineFromNamespace(m2, n1) - c.Assert(err, check.Equals, errorMachineNotShared) + 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) { - n1, m1 := CreateNodeNamespace(c, "shared1", "test_get_shared_nodes_1", "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", "100.64.0.1") - _, m2 := CreateNodeNamespace(c, "shared2", "test_get_shared_nodes_2", "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", "100.64.0.2") + 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", + ) - p1s, err := h.getPeers(m1) + peersOfMachine1BeforeShare, err := app.getPeers(machine1) c.Assert(err, check.IsNil) - c.Assert(len(p1s), check.Equals, 0) + c.Assert(len(peersOfMachine1BeforeShare), check.Equals, 0) - err = h.AddSharedMachineToNamespace(m2, n1) + err = app.AddSharedMachineToNamespace(machine2, namespace1) c.Assert(err, check.IsNil) - err = h.AddSharedMachineToNamespace(m2, n1) - c.Assert(err, check.Equals, errorMachineAlreadyShared) + err = app.AddSharedMachineToNamespace(machine2, namespace1) + c.Assert(err, check.Equals, errMachineAlreadyShared) } func (s *Suite) TestDoNotIncludeRoutesOnShared(c *check.C) { - n1, m1 := CreateNodeNamespace(c, "shared1", "test_get_shared_nodes_1", "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", "100.64.0.1") - _, m2 := CreateNodeNamespace(c, "shared2", "test_get_shared_nodes_2", "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", "100.64.0.2") + 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", + ) - p1s, err := h.getPeers(m1) + peersOfMachine1BeforeShare, err := app.getPeers(machine1) c.Assert(err, check.IsNil) - c.Assert(len(p1s), check.Equals, 0) + c.Assert(len(peersOfMachine1BeforeShare), check.Equals, 0) - err = h.AddSharedMachineToNamespace(m2, n1) + err = app.AddSharedMachineToNamespace(machine2, namespace1) c.Assert(err, check.IsNil) - p1sAfter, err := h.getPeers(m1) + peersOfMachine1AfterShare, err := app.getPeers(machine1) c.Assert(err, check.IsNil) - c.Assert(len(p1sAfter), check.Equals, 1) - c.Assert(p1sAfter[0].Name, check.Equals, "test_get_shared_nodes_2") + 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) { - n1, m1 := CreateNodeNamespace(c, "shared1", "test_get_shared_nodes_1", "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", "100.64.0.1") - _, m2 := CreateNodeNamespace(c, "shared2", "test_get_shared_nodes_2", "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", "100.64.0.2") - _, m3 := CreateNodeNamespace(c, "shared3", "test_get_shared_nodes_3", "6e704bee83eb93db6fc2c417d7882964cd3f8cc87082cbb645982e34020c76c8", "100.64.0.3") + 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 := h.CreatePreAuthKey(n1.Name, false, false, nil) + pak4, err := app.CreatePreAuthKey(namespace1.Name, false, false, nil) c.Assert(err, check.IsNil) - m4 := &Machine{ + machine4 := &Machine{ ID: 4, MachineKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f", NodeKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f", DiscoKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f", Name: "test_get_shared_nodes_4", - NamespaceID: n1.ID, + NamespaceID: namespace1.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, IPAddress: "100.64.0.4", AuthKeyID: uint(pak4.ID), } - h.db.Save(m4) + app.db.Save(machine4) - _, err = h.GetMachine(n1.Name, m4.Name) + _, err = app.GetMachine(namespace1.Name, machine4.Name) c.Assert(err, check.IsNil) - p1s, err := h.getPeers(m1) + peersOfMachine1BeforeShare, err := app.getPeers(machine1) c.Assert(err, check.IsNil) - c.Assert(len(p1s), check.Equals, 1) // node1 can see node4 - c.Assert(p1s[0].Name, check.Equals, m4.Name) + c.Assert(len(peersOfMachine1BeforeShare), check.Equals, 1) // node1 can see node4 + c.Assert(peersOfMachine1BeforeShare[0].Name, check.Equals, machine4.Name) - err = h.AddSharedMachineToNamespace(m2, n1) + err = app.AddSharedMachineToNamespace(machine2, namespace1) c.Assert(err, check.IsNil) - p1sAfter, err := h.getPeers(m1) + peersOfMachine1AfterShare, err := app.getPeers(machine1) c.Assert(err, check.IsNil) - c.Assert(len(p1sAfter), check.Equals, 2) // node1 can see node2 (shared) and node4 (same namespace) - c.Assert(p1sAfter[0].Name, check.Equals, m2.Name) - c.Assert(p1sAfter[1].Name, check.Equals, m4.Name) + 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) - node1shared, err := h.getShared(m1) + sharedOfMachine1, err := app.getShared(machine1) c.Assert(err, check.IsNil) - c.Assert(len(node1shared), check.Equals, 1) // node1 can see node2 as shared - c.Assert(node1shared[0].Name, check.Equals, m2.Name) + c.Assert(len(sharedOfMachine1), check.Equals, 1) // node1 can see node2 as shared + c.Assert(sharedOfMachine1[0].Name, check.Equals, machine2.Name) - pAlone, err := h.getPeers(m3) + peersOfMachine3, err := app.getPeers(machine3) c.Assert(err, check.IsNil) - c.Assert(len(pAlone), check.Equals, 0) // node3 is alone + c.Assert(len(peersOfMachine3), check.Equals, 0) // node3 is alone - pSharedTo, err := h.getPeers(m2) + peersOfMachine2, err := app.getPeers(machine2) c.Assert(err, check.IsNil) - c.Assert(len(pSharedTo), check.Equals, 2) // node2 should see node1 (sharedTo) and node4 (sharedTo), as is shared in namespace1 - c.Assert(pSharedTo[0].Name, check.Equals, m1.Name) - c.Assert(pSharedTo[1].Name, check.Equals, m4.Name) + 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) { - n1, m1 := CreateNodeNamespace(c, "shared1", "test_get_shared_nodes_1", "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", "100.64.0.1") - _, m2 := CreateNodeNamespace(c, "shared2", "test_get_shared_nodes_2", "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", "100.64.0.2") - _, m3 := CreateNodeNamespace(c, "shared3", "test_get_shared_nodes_3", "6e704bee83eb93db6fc2c417d7882964cd3f8cc87082cbb645982e34020c76c8", "100.64.0.3") + 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 := h.CreatePreAuthKey(n1.Name, false, false, nil) + pak4n1, err := app.CreatePreAuthKey(namespace1.Name, false, false, nil) c.Assert(err, check.IsNil) - m4 := &Machine{ + machine4 := &Machine{ ID: 4, MachineKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f", NodeKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f", DiscoKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f", Name: "test_get_shared_nodes_4", - NamespaceID: n1.ID, + NamespaceID: namespace1.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, IPAddress: "100.64.0.4", AuthKeyID: uint(pak4n1.ID), } - h.db.Save(m4) + app.db.Save(machine4) - _, err = h.GetMachine(n1.Name, m4.Name) + _, err = app.GetMachine(namespace1.Name, machine4.Name) c.Assert(err, check.IsNil) - p1s, err := h.getPeers(m1) + peersOfMachine1BeforeShare, err := app.getPeers(machine1) c.Assert(err, check.IsNil) - c.Assert(len(p1s), check.Equals, 1) // nodes 1 and 4 - c.Assert(p1s[0].Name, check.Equals, m4.Name) + c.Assert(len(peersOfMachine1BeforeShare), check.Equals, 1) // nodes 1 and 4 + c.Assert(peersOfMachine1BeforeShare[0].Name, check.Equals, machine4.Name) - err = h.AddSharedMachineToNamespace(m2, n1) + err = app.AddSharedMachineToNamespace(machine2, namespace1) c.Assert(err, check.IsNil) - p1sAfter, err := h.getPeers(m1) + peersOfMachine1AfterShare, err := app.getPeers(machine1) c.Assert(err, check.IsNil) - c.Assert(len(p1sAfter), check.Equals, 2) // nodes 1, 2, 4 - c.Assert(p1sAfter[0].Name, check.Equals, m2.Name) - c.Assert(p1sAfter[1].Name, check.Equals, m4.Name) + 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) - node1shared, err := h.getShared(m1) + sharedOfMachine1, err := app.getShared(machine1) c.Assert(err, check.IsNil) - c.Assert(len(node1shared), check.Equals, 1) // nodes 1, 2, 4 - c.Assert(node1shared[0].Name, check.Equals, m2.Name) + c.Assert(len(sharedOfMachine1), check.Equals, 1) // nodes 1, 2, 4 + c.Assert(sharedOfMachine1[0].Name, check.Equals, machine2.Name) - pAlone, err := h.getPeers(m3) + peersOfMachine3, err := app.getPeers(machine3) c.Assert(err, check.IsNil) - c.Assert(len(pAlone), check.Equals, 0) // node 3 is alone + c.Assert(len(peersOfMachine3), check.Equals, 0) // node 3 is alone - sharedMachines, err := h.ListSharedMachinesInNamespace(n1.Name) + sharedMachinesInNamespace1, err := app.ListSharedMachinesInNamespace( + namespace1.Name, + ) c.Assert(err, check.IsNil) - c.Assert(len(*sharedMachines), check.Equals, 1) + c.Assert(len(sharedMachinesInNamespace1), check.Equals, 1) - err = h.DeleteMachine(m2) + err = app.DeleteMachine(machine2) c.Assert(err, check.IsNil) - sharedMachines, err = h.ListSharedMachinesInNamespace(n1.Name) + sharedMachinesInNamespace1, err = app.ListSharedMachinesInNamespace(namespace1.Name) c.Assert(err, check.IsNil) - c.Assert(len(*sharedMachines), check.Equals, 0) + c.Assert(len(sharedMachinesInNamespace1), check.Equals, 0) } diff --git a/swagger.go b/swagger.go new file mode 100644 index 00000000..bad348db --- /dev/null +++ b/swagger.go @@ -0,0 +1,69 @@ +package headscale + +import ( + "bytes" + _ "embed" + "html/template" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/rs/zerolog/log" +) + +//go:embed gen/openapiv2/headscale/v1/headscale.swagger.json +var apiV1JSON []byte + +func SwaggerUI(ctx *gin.Context) { + swaggerTemplate := template.Must(template.New("swagger").Parse(` + + + + + + + + +
+ + +`)) + + var payload bytes.Buffer + if err := swaggerTemplate.Execute(&payload, struct{}{}); err != nil { + log.Error(). + Caller(). + Err(err). + Msg("Could not render Swagger") + ctx.Data( + http.StatusInternalServerError, + "text/html; charset=utf-8", + []byte("Could not render Swagger"), + ) + + return + } + + ctx.Data(http.StatusOK, "text/html; charset=utf-8", payload.Bytes()) +} + +func SwaggerAPIv1(ctx *gin.Context) { + ctx.Data(http.StatusOK, "application/json; charset=utf-8", apiV1JSON) +} diff --git a/tools.go b/tools.go new file mode 100644 index 00000000..287c1230 --- /dev/null +++ b/tools.go @@ -0,0 +1,12 @@ +//go:build tools +// +build tools + +package tools + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway" + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2" + _ "github.com/infobloxopen/protoc-gen-gorm" + _ "google.golang.org/grpc/cmd/protoc-gen-go-grpc" + _ "google.golang.org/protobuf/cmd/protoc-gen-go" +) diff --git a/utils.go b/utils.go index cbe1d870..7011e63d 100644 --- a/utils.go +++ b/utils.go @@ -6,72 +6,131 @@ package headscale import ( - "crypto/rand" + "context" "encoding/json" "fmt" - "io" + "net" "strings" - "golang.org/x/crypto/nacl/box" + "github.com/rs/zerolog/log" "inet.af/netaddr" "tailscale.com/tailcfg" - "tailscale.com/types/wgkey" + "tailscale.com/types/key" ) +const ( + errCannotDecryptReponse = Error("cannot decrypt response") + errCouldNotAllocateIP = Error("could not find any suitable IP") + + // These constants are copied from the upstream tailscale.com/types/key + // library, because they are not exported. + // https://github.com/tailscale/tailscale/tree/main/types/key + + // nodePublicHexPrefix is the prefix used to identify a + // hex-encoded node public key. + // + // This prefix is used in the control protocol, so cannot be + // changed. + nodePublicHexPrefix = "nodekey:" + + // machinePublicHexPrefix is the prefix used to identify a + // hex-encoded machine public key. + // + // This prefix is used in the control protocol, so cannot be + // changed. + machinePublicHexPrefix = "mkey:" + + // discoPublicHexPrefix is the prefix used to identify a + // hex-encoded disco public key. + // + // This prefix is used in the control protocol, so cannot be + // changed. + discoPublicHexPrefix = "discokey:" + + // privateKey prefix. + privateHexPrefix = "privkey:" +) + +func MachinePublicKeyStripPrefix(machineKey key.MachinePublic) string { + return strings.TrimPrefix(machineKey.String(), machinePublicHexPrefix) +} + +func NodePublicKeyStripPrefix(nodeKey key.NodePublic) string { + return strings.TrimPrefix(nodeKey.String(), nodePublicHexPrefix) +} + +func DiscoPublicKeyStripPrefix(discoKey key.DiscoPublic) string { + return strings.TrimPrefix(discoKey.String(), discoPublicHexPrefix) +} + +func MachinePublicKeyEnsurePrefix(machineKey string) string { + if !strings.HasPrefix(machineKey, machinePublicHexPrefix) { + return machinePublicHexPrefix + machineKey + } + + return machineKey +} + +func NodePublicKeyEnsurePrefix(nodeKey string) string { + if !strings.HasPrefix(nodeKey, nodePublicHexPrefix) { + return nodePublicHexPrefix + nodeKey + } + + return nodeKey +} + +func DiscoPublicKeyEnsurePrefix(discoKey string) string { + if !strings.HasPrefix(discoKey, discoPublicHexPrefix) { + return discoPublicHexPrefix + discoKey + } + + return discoKey +} + +func PrivateKeyEnsurePrefix(privateKey string) string { + if !strings.HasPrefix(privateKey, privateHexPrefix) { + return privateHexPrefix + privateKey + } + + return privateKey +} + // Error is used to compare errors as per https://dave.cheney.net/2016/04/07/constant-errors type Error string func (e Error) Error() string { return string(e) } -func decode(msg []byte, v interface{}, pubKey *wgkey.Key, privKey *wgkey.Private) error { - return decodeMsg(msg, v, pubKey, privKey) -} +func decode( + msg []byte, + output interface{}, + pubKey *key.MachinePublic, + privKey *key.MachinePrivate, +) error { + log.Trace().Int("length", len(msg)).Msg("Trying to decrypt") -func decodeMsg(msg []byte, v interface{}, pubKey *wgkey.Key, privKey *wgkey.Private) error { - decrypted, err := decryptMsg(msg, pubKey, privKey) - if err != nil { + decrypted, ok := privKey.OpenFrom(*pubKey, msg) + if !ok { + return errCannotDecryptReponse + } + + if err := json.Unmarshal(decrypted, output); err != nil { return err } - // fmt.Println(string(decrypted)) - if err := json.Unmarshal(decrypted, v); err != nil { - return fmt.Errorf("response: %v", err) - } + return nil } -func decryptMsg(msg []byte, pubKey *wgkey.Key, privKey *wgkey.Private) ([]byte, error) { - var nonce [24]byte - if len(msg) < len(nonce)+1 { - return nil, fmt.Errorf("response missing nonce, len=%d", len(msg)) - } - copy(nonce[:], msg) - msg = msg[len(nonce):] - - pub, pri := (*[32]byte)(pubKey), (*[32]byte)(privKey) - decrypted, ok := box.Open(nil, msg, &nonce, pub, pri) - if !ok { - return nil, fmt.Errorf("cannot decrypt response") - } - return decrypted, nil -} - -func encode(v interface{}, pubKey *wgkey.Key, privKey *wgkey.Private) ([]byte, error) { +func encode( + v interface{}, + pubKey *key.MachinePublic, + privKey *key.MachinePrivate, +) ([]byte, error) { b, err := json.Marshal(v) if err != nil { return nil, err } - return encodeMsg(b, pubKey, privKey) -} - -func encodeMsg(b []byte, pubKey *wgkey.Key, privKey *wgkey.Private) ([]byte, error) { - var nonce [24]byte - if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { - panic(err) - } - pub, pri := (*[32]byte)(pubKey), (*[32]byte)(privKey) - msg := box.Seal(nonce[:], b, &nonce, pub, pri) - return msg, nil + return privKey.SealTo(*pubKey, b), nil } func (h *Headscale) getAvailableIP() (*netaddr.IP, error) { @@ -87,7 +146,7 @@ func (h *Headscale) getAvailableIP() (*netaddr.IP, error) { for { if !ipPrefix.Contains(ip) { - return nil, fmt.Errorf("could not find any suitable IP in %s", ipPrefix) + return nil, errCouldNotAllocateIP } // Some OS (including Linux) does not like when IPs ends with 0 or 255, which @@ -96,13 +155,14 @@ func (h *Headscale) getAvailableIP() (*netaddr.IP, error) { ipRaw := ip.As4() if ipRaw[3] == 0 || ipRaw[3] == 255 { ip = ip.Next() + continue } if ip.IsZero() && ip.IsLoopback() { - ip = ip.Next() + continue } @@ -123,7 +183,7 @@ func (h *Headscale) getUsedIPs() ([]netaddr.IP, error) { if addr != "" { ip, err := netaddr.ParseIP(addr) if err != nil { - return nil, fmt.Errorf("failed to parse ip from database, %w", err) + return nil, fmt.Errorf("failed to parse ip from database: %w", err) } ips[index] = ip @@ -154,5 +214,50 @@ func tailNodesToString(nodes []*tailcfg.Node) string { } func tailMapResponseToString(resp tailcfg.MapResponse) string { - return fmt.Sprintf("{ Node: %s, Peers: %s }", resp.Node.Name, tailNodesToString(resp.Peers)) + return fmt.Sprintf( + "{ Node: %s, Peers: %s }", + resp.Node.Name, + tailNodesToString(resp.Peers), + ) +} + +func GrpcSocketDialer(ctx context.Context, addr string) (net.Conn, error) { + var d net.Dialer + + return d.DialContext(ctx, "unix", addr) +} + +func ipPrefixToString(prefixes []netaddr.IPPrefix) []string { + result := make([]string, len(prefixes)) + + for index, prefix := range prefixes { + result[index] = prefix.String() + } + + return result +} + +func stringToIPPrefix(prefixes []string) ([]netaddr.IPPrefix, error) { + result := make([]netaddr.IPPrefix, len(prefixes)) + + for index, prefixStr := range prefixes { + prefix, err := netaddr.ParseIPPrefix(prefixStr) + if err != nil { + return []netaddr.IPPrefix{}, err + } + + result[index] = prefix + } + + return result, nil +} + +func containsIPPrefix(prefixes []netaddr.IPPrefix, prefix netaddr.IPPrefix) bool { + for _, p := range prefixes { + if prefix == p { + return true + } + } + + return false } diff --git a/utils_test.go b/utils_test.go index f50cd117..95722a83 100644 --- a/utils_test.go +++ b/utils_test.go @@ -6,7 +6,7 @@ import ( ) func (s *Suite) TestGetAvailableIp(c *check.C) { - ip, err := h.getAvailableIP() + ip, err := app.getAvailableIP() c.Assert(err, check.IsNil) @@ -16,33 +16,33 @@ func (s *Suite) TestGetAvailableIp(c *check.C) { } func (s *Suite) TestGetUsedIps(c *check.C) { - ip, err := h.getAvailableIP() + ip, err := app.getAvailableIP() c.Assert(err, check.IsNil) - n, err := h.CreateNamespace("test_ip") + namespace, err := app.CreateNamespace("test_ip") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, false, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) c.Assert(err, check.IsNil) - _, err = h.GetMachine("test", "testmachine") + _, err = app.GetMachine("test", "testmachine") c.Assert(err, check.NotNil) - m := Machine{ + machine := Machine{ ID: 0, MachineKey: "foo", NodeKey: "bar", DiscoKey: "faa", Name: "testmachine", - NamespaceID: n.ID, + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), IPAddress: ip.String(), } - h.db.Save(&m) + app.db.Save(&machine) - ips, err := h.getUsedIPs() + ips, err := app.getUsedIPs() c.Assert(err, check.IsNil) @@ -50,42 +50,42 @@ func (s *Suite) TestGetUsedIps(c *check.C) { c.Assert(ips[0], check.Equals, expected) - m1, err := h.GetMachineByID(0) + machine1, err := app.GetMachineByID(0) c.Assert(err, check.IsNil) - c.Assert(m1.IPAddress, check.Equals, expected.String()) + c.Assert(machine1.IPAddress, check.Equals, expected.String()) } func (s *Suite) TestGetMultiIp(c *check.C) { - n, err := h.CreateNamespace("test-ip-multi") + namespace, err := app.CreateNamespace("test-ip-multi") c.Assert(err, check.IsNil) - for i := 1; i <= 350; i++ { - ip, err := h.getAvailableIP() + for index := 1; index <= 350; index++ { + ip, err := app.getAvailableIP() c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, false, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) c.Assert(err, check.IsNil) - _, err = h.GetMachine("test", "testmachine") + _, err = app.GetMachine("test", "testmachine") c.Assert(err, check.NotNil) - m := Machine{ - ID: uint64(i), + machine := Machine{ + ID: uint64(index), MachineKey: "foo", NodeKey: "bar", DiscoKey: "faa", Name: "testmachine", - NamespaceID: n.ID, + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), IPAddress: ip.String(), } - h.db.Save(&m) + app.db.Save(&machine) } - ips, err := h.getUsedIPs() + ips, err := app.getUsedIPs() c.Assert(err, check.IsNil) @@ -96,59 +96,67 @@ func (s *Suite) TestGetMultiIp(c *check.C) { c.Assert(ips[300], check.Equals, netaddr.MustParseIP("10.27.1.47")) // Check that we can read back the IPs - m1, err := h.GetMachineByID(1) + machine1, err := app.GetMachineByID(1) c.Assert(err, check.IsNil) - c.Assert(m1.IPAddress, check.Equals, netaddr.MustParseIP("10.27.0.1").String()) + c.Assert( + machine1.IPAddress, + check.Equals, + netaddr.MustParseIP("10.27.0.1").String(), + ) - m50, err := h.GetMachineByID(50) + machine50, err := app.GetMachineByID(50) c.Assert(err, check.IsNil) - c.Assert(m50.IPAddress, check.Equals, netaddr.MustParseIP("10.27.0.50").String()) + c.Assert( + machine50.IPAddress, + check.Equals, + netaddr.MustParseIP("10.27.0.50").String(), + ) expectedNextIP := netaddr.MustParseIP("10.27.1.97") - nextIP, err := h.getAvailableIP() + nextIP, err := app.getAvailableIP() c.Assert(err, check.IsNil) c.Assert(nextIP.String(), check.Equals, expectedNextIP.String()) // If we call get Available again, we should receive // the same IP, as it has not been reserved. - nextIP2, err := h.getAvailableIP() + nextIP2, err := app.getAvailableIP() c.Assert(err, check.IsNil) c.Assert(nextIP2.String(), check.Equals, expectedNextIP.String()) } func (s *Suite) TestGetAvailableIpMachineWithoutIP(c *check.C) { - ip, err := h.getAvailableIP() + ip, err := app.getAvailableIP() c.Assert(err, check.IsNil) expected := netaddr.MustParseIP("10.27.0.1") c.Assert(ip.String(), check.Equals, expected.String()) - n, err := h.CreateNamespace("test_ip") + namespace, err := app.CreateNamespace("test_ip") c.Assert(err, check.IsNil) - pak, err := h.CreatePreAuthKey(n.Name, false, false, nil) + pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) c.Assert(err, check.IsNil) - _, err = h.GetMachine("test", "testmachine") + _, err = app.GetMachine("test", "testmachine") c.Assert(err, check.NotNil) - m := Machine{ + machine := Machine{ ID: 0, MachineKey: "foo", NodeKey: "bar", DiscoKey: "faa", Name: "testmachine", - NamespaceID: n.ID, + NamespaceID: namespace.ID, Registered: true, - RegisterMethod: "authKey", + RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } - h.db.Save(&m) + app.db.Save(&machine) - ip2, err := h.getAvailableIP() + ip2, err := app.getAvailableIP() c.Assert(err, check.IsNil) c.Assert(ip2.String(), check.Equals, expected.String())