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
+
+ |
@@ -98,6 +158,8 @@ Please have a look at the documentation under [`docs/`](docs/).
Aaron Bieber
|
+
+
@@ -105,8 +167,6 @@ Please have a look at the documentation under [`docs/`](docs/).
Paul Tötterman
|
-
-
@@ -142,6 +202,8 @@ Please have a look at the documentation under [`docs/`](docs/).
Felix Kronlage-Dammers
|
+
+
@@ -149,8 +211,6 @@ Please have a look at the documentation under [`docs/`](docs/).
Felix Yan
|
-
-
@@ -186,6 +246,8 @@ Please have a look at the documentation under [`docs/`](docs/).
Tjerk Woudsma
|
+
+
@@ -193,8 +255,6 @@ Please have a look at the documentation under [`docs/`](docs/).
Zakhar Bessarab
|
-
-
@@ -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())