Merge branch 'configurable-mtls' of github.com:arch4ngel/headscale into configurable-mtls
This commit is contained in:
commit
af25aa75d9
43 changed files with 955 additions and 543 deletions
16
.github/workflows/build.yml
vendored
16
.github/workflows/build.yml
vendored
|
@ -14,22 +14,38 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- 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
|
- name: Setup Go
|
||||||
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: "1.17"
|
go-version: "1.17"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
run: |
|
run: |
|
||||||
go version
|
go version
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install -y make
|
sudo apt install -y make
|
||||||
|
|
||||||
- name: Run build
|
- name: Run build
|
||||||
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
run: make build
|
run: make build
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
with:
|
with:
|
||||||
name: headscale-linux
|
name: headscale-linux
|
||||||
path: headscale
|
path: headscale
|
||||||
|
|
31
.github/workflows/lint.yml
vendored
31
.github/workflows/lint.yml
vendored
|
@ -8,8 +8,21 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- 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: golangci-lint
|
- name: golangci-lint
|
||||||
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
uses: golangci/golangci-lint-action@v2
|
uses: golangci/golangci-lint-action@v2
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
|
@ -24,8 +37,26 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- 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
|
- name: Prettify code
|
||||||
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
uses: creyD/prettier_action@v4.0
|
uses: creyD/prettier_action@v4.0
|
||||||
with:
|
with:
|
||||||
prettier_options: >-
|
prettier_options: >-
|
||||||
|
|
19
.github/workflows/test-integration.yml
vendored
19
.github/workflows/test-integration.yml
vendored
|
@ -3,21 +3,30 @@ name: CI
|
||||||
on: [pull_request]
|
on: [pull_request]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# The "build" workflow
|
|
||||||
integration-test:
|
integration-test:
|
||||||
# The type of runner that the job will run on
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
|
||||||
steps:
|
steps:
|
||||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
|
||||||
- uses: actions/checkout@v2
|
- 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
|
- name: Setup Go
|
||||||
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: "1.17"
|
go-version: "1.17"
|
||||||
|
|
||||||
- name: Run Integration tests
|
- name: Run Integration tests
|
||||||
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
run: go test -tags integration -timeout 30m
|
run: go test -tags integration -timeout 30m
|
||||||
|
|
24
.github/workflows/test.yml
vendored
24
.github/workflows/test.yml
vendored
|
@ -3,31 +3,41 @@ name: CI
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# The "build" workflow
|
|
||||||
test:
|
test:
|
||||||
# The type of runner that the job will run on
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
|
||||||
steps:
|
steps:
|
||||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
|
||||||
- uses: actions/checkout@v2
|
- 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
|
- name: Setup Go
|
||||||
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: "1.17" # The Go version to download (if necessary) and use.
|
go-version: "1.17"
|
||||||
|
|
||||||
# Install all the dependencies
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
run: |
|
run: |
|
||||||
go version
|
go version
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install -y make
|
sudo apt install -y make
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
run: make test
|
run: make test
|
||||||
|
|
||||||
- name: Run build
|
- name: Run build
|
||||||
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
run: make
|
run: make
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
# This is an example .goreleaser.yml file with some sane defaults.
|
---
|
||||||
# Make sure to check the documentation at http://goreleaser.com
|
|
||||||
before:
|
before:
|
||||||
hooks:
|
hooks:
|
||||||
- go mod tidy
|
- go mod tidy -compat=1.17
|
||||||
|
|
||||||
release:
|
release:
|
||||||
prerelease: auto
|
prerelease: auto
|
||||||
|
@ -33,7 +32,7 @@ builds:
|
||||||
goarch:
|
goarch:
|
||||||
- arm
|
- arm
|
||||||
goarm:
|
goarm:
|
||||||
- 7
|
- "7"
|
||||||
env:
|
env:
|
||||||
- CC=arm-linux-gnueabihf-gcc
|
- CC=arm-linux-gnueabihf-gcc
|
||||||
- CXX=arm-linux-gnueabihf-g++
|
- CXX=arm-linux-gnueabihf-g++
|
||||||
|
|
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -2,6 +2,18 @@
|
||||||
|
|
||||||
**TBD (TBD):**
|
**TBD (TBD):**
|
||||||
|
|
||||||
|
**0.13.0 (2022-xx-xx):**
|
||||||
|
|
||||||
|
**Features**:
|
||||||
|
|
||||||
|
- Add IPv6 support to the prefix assigned to namespaces
|
||||||
|
|
||||||
|
**Changes**:
|
||||||
|
|
||||||
|
- `ip_prefix` is now superseded by `ip_prefixes` in the configuration [#208](https://github.com/juanfont/headscale/pull/208)
|
||||||
|
|
||||||
|
**0.12.4 (2022-01-29):**
|
||||||
|
|
||||||
**Changes**:
|
**Changes**:
|
||||||
|
|
||||||
- Make gRPC Unix Socket permissions configurable [#292](https://github.com/juanfont/headscale/pull/292)
|
- Make gRPC Unix Socket permissions configurable [#292](https://github.com/juanfont/headscale/pull/292)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Builder image
|
# Builder image
|
||||||
FROM golang:1.17.6-bullseye AS build
|
FROM docker.io/golang:1.17.1-bullseye AS build
|
||||||
ENV GOPATH /go
|
ENV GOPATH /go
|
||||||
WORKDIR /go/src/headscale
|
WORKDIR /go/src/headscale
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Builder image
|
# Builder image
|
||||||
FROM golang:1.17.6-alpine AS build
|
FROM docker.io/golang:1.17.1-alpine AS build
|
||||||
ENV GOPATH /go
|
ENV GOPATH /go
|
||||||
WORKDIR /go/src/headscale
|
WORKDIR /go/src/headscale
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ RUN strip /go/bin/headscale
|
||||||
RUN test -e /go/bin/headscale
|
RUN test -e /go/bin/headscale
|
||||||
|
|
||||||
# Production image
|
# Production image
|
||||||
FROM alpine:latest
|
FROM docker.io/alpine:latest
|
||||||
|
|
||||||
COPY --from=build /go/bin/headscale /bin/headscale
|
COPY --from=build /go/bin/headscale /bin/headscale
|
||||||
ENV TZ UTC
|
ENV TZ UTC
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Builder image
|
# Builder image
|
||||||
FROM golang:1.17.1-bullseye AS build
|
FROM docker.io/golang:1.17.1-bullseye AS build
|
||||||
ENV GOPATH /go
|
ENV GOPATH /go
|
||||||
WORKDIR /go/src/headscale
|
WORKDIR /go/src/headscale
|
||||||
|
|
||||||
|
|
|
@ -7,5 +7,5 @@ RUN apt-get update \
|
||||||
&& curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.gpg | apt-key add - \
|
&& curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.gpg | apt-key add - \
|
||||||
&& curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.list | tee /etc/apt/sources.list.d/tailscale.list \
|
&& curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.list | tee /etc/apt/sources.list.d/tailscale.list \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get install -y tailscale=${TAILSCALE_VERSION} \
|
&& apt-get install -y tailscale=${TAILSCALE_VERSION} dnsutils \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -18,7 +18,7 @@ test:
|
||||||
@go test -coverprofile=coverage.out ./...
|
@go test -coverprofile=coverage.out ./...
|
||||||
|
|
||||||
test_integration:
|
test_integration:
|
||||||
go test -tags integration -timeout 30m ./...
|
go test -tags integration -timeout 30m -count=1 ./...
|
||||||
|
|
||||||
test_integration_cli:
|
test_integration_cli:
|
||||||
go test -tags integration -v integration_cli_test.go integration_common_test.go
|
go test -tags integration -v integration_cli_test.go integration_common_test.go
|
||||||
|
|
6
acls.go
6
acls.go
|
@ -188,7 +188,7 @@ func (h *Headscale) expandAlias(alias string) ([]string, error) {
|
||||||
return nil, errInvalidNamespace
|
return nil, errInvalidNamespace
|
||||||
}
|
}
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
ips = append(ips, node.IPAddress)
|
ips = append(ips, node.IPAddresses.ToStringSlice()...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ func (h *Headscale) expandAlias(alias string) ([]string, error) {
|
||||||
// FIXME: Check TagOwners allows this
|
// FIXME: Check TagOwners allows this
|
||||||
for _, t := range hostinfo.RequestTags {
|
for _, t := range hostinfo.RequestTags {
|
||||||
if alias[4:] == t {
|
if alias[4:] == t {
|
||||||
ips = append(ips, machine.IPAddress)
|
ips = append(ips, machine.IPAddresses.ToStringSlice()...)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -241,7 +241,7 @@ func (h *Headscale) expandAlias(alias string) ([]string, error) {
|
||||||
}
|
}
|
||||||
ips := []string{}
|
ips := []string{}
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
ips = append(ips, n.IPAddress)
|
ips = append(ips, n.IPAddresses.ToStringSlice()...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ips, nil
|
return ips, nil
|
||||||
|
|
50
acls_test.go
50
acls_test.go
|
@ -61,9 +61,9 @@ func (s *Suite) TestPortRange(c *check.C) {
|
||||||
c.Assert(rules, check.NotNil)
|
c.Assert(rules, check.NotNil)
|
||||||
|
|
||||||
c.Assert(rules, check.HasLen, 1)
|
c.Assert(rules, check.HasLen, 1)
|
||||||
c.Assert((rules)[0].DstPorts, 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.First, check.Equals, uint16(5400))
|
||||||
c.Assert((rules)[0].DstPorts[0].Ports.Last, check.Equals, uint16(5500))
|
c.Assert(rules[0].DstPorts[0].Ports.Last, check.Equals, uint16(5500))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestPortWildcard(c *check.C) {
|
func (s *Suite) TestPortWildcard(c *check.C) {
|
||||||
|
@ -75,11 +75,11 @@ func (s *Suite) TestPortWildcard(c *check.C) {
|
||||||
c.Assert(rules, check.NotNil)
|
c.Assert(rules, check.NotNil)
|
||||||
|
|
||||||
c.Assert(rules, check.HasLen, 1)
|
c.Assert(rules, check.HasLen, 1)
|
||||||
c.Assert((rules)[0].DstPorts, 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.First, check.Equals, uint16(0))
|
||||||
c.Assert((rules)[0].DstPorts[0].Ports.Last, check.Equals, uint16(65535))
|
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, check.HasLen, 1)
|
||||||
c.Assert((rules)[0].SrcIPs[0], check.Equals, "*")
|
c.Assert(rules[0].SrcIPs[0], check.Equals, "*")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestPortNamespace(c *check.C) {
|
func (s *Suite) TestPortNamespace(c *check.C) {
|
||||||
|
@ -91,7 +91,7 @@ func (s *Suite) TestPortNamespace(c *check.C) {
|
||||||
|
|
||||||
_, err = app.GetMachine("testnamespace", "testmachine")
|
_, err = app.GetMachine("testnamespace", "testmachine")
|
||||||
c.Assert(err, check.NotNil)
|
c.Assert(err, check.NotNil)
|
||||||
ip, _ := app.getAvailableIP()
|
ips, _ := app.getAvailableIPs()
|
||||||
machine := Machine{
|
machine := Machine{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
MachineKey: "foo",
|
MachineKey: "foo",
|
||||||
|
@ -101,7 +101,7 @@ func (s *Suite) TestPortNamespace(c *check.C) {
|
||||||
NamespaceID: namespace.ID,
|
NamespaceID: namespace.ID,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: ip.String(),
|
IPAddresses: ips,
|
||||||
AuthKeyID: uint(pak.ID),
|
AuthKeyID: uint(pak.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(&machine)
|
app.db.Save(&machine)
|
||||||
|
@ -116,12 +116,13 @@ func (s *Suite) TestPortNamespace(c *check.C) {
|
||||||
c.Assert(rules, check.NotNil)
|
c.Assert(rules, check.NotNil)
|
||||||
|
|
||||||
c.Assert(rules, check.HasLen, 1)
|
c.Assert(rules, check.HasLen, 1)
|
||||||
c.Assert((rules)[0].DstPorts, 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.First, check.Equals, uint16(0))
|
||||||
c.Assert((rules)[0].DstPorts[0].Ports.Last, check.Equals, uint16(65535))
|
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, check.HasLen, 1)
|
||||||
c.Assert((rules)[0].SrcIPs[0], check.Not(check.Equals), "not an ip")
|
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(len(ips), check.Equals, 1)
|
||||||
|
c.Assert(rules[0].SrcIPs[0], check.Equals, ips[0].String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestPortGroup(c *check.C) {
|
func (s *Suite) TestPortGroup(c *check.C) {
|
||||||
|
@ -133,7 +134,7 @@ func (s *Suite) TestPortGroup(c *check.C) {
|
||||||
|
|
||||||
_, err = app.GetMachine("testnamespace", "testmachine")
|
_, err = app.GetMachine("testnamespace", "testmachine")
|
||||||
c.Assert(err, check.NotNil)
|
c.Assert(err, check.NotNil)
|
||||||
ip, _ := app.getAvailableIP()
|
ips, _ := app.getAvailableIPs()
|
||||||
machine := Machine{
|
machine := Machine{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
MachineKey: "foo",
|
MachineKey: "foo",
|
||||||
|
@ -143,7 +144,7 @@ func (s *Suite) TestPortGroup(c *check.C) {
|
||||||
NamespaceID: namespace.ID,
|
NamespaceID: namespace.ID,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: ip.String(),
|
IPAddresses: ips,
|
||||||
AuthKeyID: uint(pak.ID),
|
AuthKeyID: uint(pak.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(&machine)
|
app.db.Save(&machine)
|
||||||
|
@ -156,10 +157,11 @@ func (s *Suite) TestPortGroup(c *check.C) {
|
||||||
c.Assert(rules, check.NotNil)
|
c.Assert(rules, check.NotNil)
|
||||||
|
|
||||||
c.Assert(rules, check.HasLen, 1)
|
c.Assert(rules, check.HasLen, 1)
|
||||||
c.Assert((rules)[0].DstPorts, 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.First, check.Equals, uint16(0))
|
||||||
c.Assert((rules)[0].DstPorts[0].Ports.Last, check.Equals, uint16(65535))
|
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, check.HasLen, 1)
|
||||||
c.Assert((rules)[0].SrcIPs[0], check.Not(check.Equals), "not an ip")
|
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(len(ips), check.Equals, 1)
|
||||||
|
c.Assert(rules[0].SrcIPs[0], check.Equals, ips[0].String())
|
||||||
}
|
}
|
||||||
|
|
15
api.go
15
api.go
|
@ -497,6 +497,7 @@ func (h *Headscale) handleMachineRegistrationNew(
|
||||||
ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody)
|
ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: check if any locks are needed around IP allocation.
|
||||||
func (h *Headscale) handleAuthKey(
|
func (h *Headscale) handleAuthKey(
|
||||||
ctx *gin.Context,
|
ctx *gin.Context,
|
||||||
machineKey key.MachinePublic,
|
machineKey key.MachinePublic,
|
||||||
|
@ -554,14 +555,14 @@ func (h *Headscale) handleAuthKey(
|
||||||
log.Debug().
|
log.Debug().
|
||||||
Str("func", "handleAuthKey").
|
Str("func", "handleAuthKey").
|
||||||
Str("machine", machine.Name).
|
Str("machine", machine.Name).
|
||||||
Msg("Authentication key was valid, proceeding to acquire an IP address")
|
Msg("Authentication key was valid, proceeding to acquire IP addresses")
|
||||||
ip, err := h.getAvailableIP()
|
ips, err := h.getAvailableIPs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Caller().
|
Caller().
|
||||||
Str("func", "handleAuthKey").
|
Str("func", "handleAuthKey").
|
||||||
Str("machine", machine.Name).
|
Str("machine", machine.Name).
|
||||||
Msg("Failed to find an available IP")
|
Msg("Failed to find an available IP address")
|
||||||
machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name).
|
machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name).
|
||||||
Inc()
|
Inc()
|
||||||
|
|
||||||
|
@ -570,12 +571,12 @@ func (h *Headscale) handleAuthKey(
|
||||||
log.Info().
|
log.Info().
|
||||||
Str("func", "handleAuthKey").
|
Str("func", "handleAuthKey").
|
||||||
Str("machine", machine.Name).
|
Str("machine", machine.Name).
|
||||||
Str("ip", ip.String()).
|
Str("ips", strings.Join(ips.ToStringSlice(), ",")).
|
||||||
Msgf("Assigning %s to %s", ip, machine.Name)
|
Msgf("Assigning %s to %s", strings.Join(ips.ToStringSlice(), ","), machine.Name)
|
||||||
|
|
||||||
machine.Expiry = ®isterRequest.Expiry
|
machine.Expiry = ®isterRequest.Expiry
|
||||||
machine.AuthKeyID = uint(pak.ID)
|
machine.AuthKeyID = uint(pak.ID)
|
||||||
machine.IPAddress = ip.String()
|
machine.IPAddresses = ips
|
||||||
machine.NamespaceID = pak.NamespaceID
|
machine.NamespaceID = pak.NamespaceID
|
||||||
|
|
||||||
machine.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey)
|
machine.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey)
|
||||||
|
@ -610,6 +611,6 @@ func (h *Headscale) handleAuthKey(
|
||||||
log.Info().
|
log.Info().
|
||||||
Str("func", "handleAuthKey").
|
Str("func", "handleAuthKey").
|
||||||
Str("machine", machine.Name).
|
Str("machine", machine.Name).
|
||||||
Str("ip", machine.IPAddress).
|
Str("ips", strings.Join(machine.IPAddresses.ToStringSlice(), ", ")).
|
||||||
Msg("Successfully authenticated via AuthKey")
|
Msg("Successfully authenticated via AuthKey")
|
||||||
}
|
}
|
||||||
|
|
6
app.go
6
app.go
|
@ -73,7 +73,7 @@ type Config struct {
|
||||||
ServerURL string
|
ServerURL string
|
||||||
Addr string
|
Addr string
|
||||||
EphemeralNodeInactivityTimeout time.Duration
|
EphemeralNodeInactivityTimeout time.Duration
|
||||||
IPPrefix netaddr.IPPrefix
|
IPPrefixes []netaddr.IPPrefix
|
||||||
PrivateKeyPath string
|
PrivateKeyPath string
|
||||||
BaseDomain string
|
BaseDomain string
|
||||||
|
|
||||||
|
@ -204,9 +204,7 @@ func NewHeadscale(cfg Config) (*Headscale, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if app.cfg.DNSConfig != nil && app.cfg.DNSConfig.Proxied { // if MagicDNS
|
if app.cfg.DNSConfig != nil && app.cfg.DNSConfig.Proxied { // if MagicDNS
|
||||||
magicDNSDomains := generateMagicDNSRootDomains(
|
magicDNSDomains := generateMagicDNSRootDomains(app.cfg.IPPrefixes)
|
||||||
app.cfg.IPPrefix,
|
|
||||||
)
|
|
||||||
// we might have routes already from Split DNS
|
// we might have routes already from Split DNS
|
||||||
if app.cfg.DNSConfig.Routes == nil {
|
if app.cfg.DNSConfig.Routes == nil {
|
||||||
app.cfg.DNSConfig.Routes = make(map[string][]dnstype.Resolver)
|
app.cfg.DNSConfig.Routes = make(map[string][]dnstype.Resolver)
|
||||||
|
|
|
@ -41,7 +41,9 @@ func (s *Suite) ResetDB(c *check.C) {
|
||||||
c.Fatal(err)
|
c.Fatal(err)
|
||||||
}
|
}
|
||||||
cfg := Config{
|
cfg := Config{
|
||||||
IPPrefix: netaddr.MustParseIPPrefix("10.27.0.0/23"),
|
IPPrefixes: []netaddr.IPPrefix{
|
||||||
|
netaddr.MustParseIPPrefix("10.27.0.0/23"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
app = Headscale{
|
app = Headscale{
|
||||||
|
|
10
cli_test.go
10
cli_test.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gopkg.in/check.v1"
|
"gopkg.in/check.v1"
|
||||||
|
"inet.af/netaddr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Suite) TestRegisterMachine(c *check.C) {
|
func (s *Suite) TestRegisterMachine(c *check.C) {
|
||||||
|
@ -19,16 +20,17 @@ func (s *Suite) TestRegisterMachine(c *check.C) {
|
||||||
DiscoKey: "faa",
|
DiscoKey: "faa",
|
||||||
Name: "testmachine",
|
Name: "testmachine",
|
||||||
NamespaceID: namespace.ID,
|
NamespaceID: namespace.ID,
|
||||||
IPAddress: "10.0.0.1",
|
IPAddresses: []netaddr.IP{netaddr.MustParseIP("10.0.0.1")},
|
||||||
Expiry: &now,
|
Expiry: &now,
|
||||||
}
|
}
|
||||||
app.db.Save(&machine)
|
err = app.db.Save(&machine).Error
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
_, err = app.GetMachine("test", "testmachine")
|
_, err = app.GetMachine(namespace.Name, machine.Name)
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
machineAfterRegistering, err := app.RegisterMachine(
|
machineAfterRegistering, err := app.RegisterMachine(
|
||||||
"8ce002a935f8c394e55e78fbbb410576575ff8ec5cfa2e627e4b807f1be15b0e",
|
machine.MachineKey,
|
||||||
namespace.Name,
|
namespace.Name,
|
||||||
)
|
)
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
survey "github.com/AlecAivazis/survey/v2"
|
survey "github.com/AlecAivazis/survey/v2"
|
||||||
|
@ -459,7 +460,7 @@ func nodesToPtables(
|
||||||
"Name",
|
"Name",
|
||||||
"NodeKey",
|
"NodeKey",
|
||||||
"Namespace",
|
"Namespace",
|
||||||
"IP address",
|
"IP addresses",
|
||||||
"Ephemeral",
|
"Ephemeral",
|
||||||
"Last seen",
|
"Last seen",
|
||||||
"Online",
|
"Online",
|
||||||
|
@ -523,7 +524,7 @@ func nodesToPtables(
|
||||||
machine.Name,
|
machine.Name,
|
||||||
nodeKey.ShortString(),
|
nodeKey.ShortString(),
|
||||||
namespace,
|
namespace,
|
||||||
machine.IpAddress,
|
strings.Join(machine.IpAddresses, ", "),
|
||||||
strconv.FormatBool(ephemeral),
|
strconv.FormatBool(ephemeral),
|
||||||
lastSeenTime,
|
lastSeenTime,
|
||||||
online,
|
online,
|
||||||
|
|
|
@ -48,8 +48,6 @@ func LoadConfig(path string) error {
|
||||||
viper.SetDefault("tls_letsencrypt_challenge_type", "HTTP-01")
|
viper.SetDefault("tls_letsencrypt_challenge_type", "HTTP-01")
|
||||||
viper.SetDefault("tls_client_auth_mode", "relaxed")
|
viper.SetDefault("tls_client_auth_mode", "relaxed")
|
||||||
|
|
||||||
viper.SetDefault("ip_prefix", "100.64.0.0/10")
|
|
||||||
|
|
||||||
viper.SetDefault("log_level", "info")
|
viper.SetDefault("log_level", "info")
|
||||||
|
|
||||||
viper.SetDefault("dns_config", nil)
|
viper.SetDefault("dns_config", nil)
|
||||||
|
@ -235,10 +233,57 @@ func getHeadscaleConfig() headscale.Config {
|
||||||
dnsConfig, baseDomain := GetDNSConfig()
|
dnsConfig, baseDomain := GetDNSConfig()
|
||||||
derpConfig := GetDERPConfig()
|
derpConfig := GetDERPConfig()
|
||||||
|
|
||||||
|
configuredPrefixes := viper.GetStringSlice("ip_prefixes")
|
||||||
|
parsedPrefixes := make([]netaddr.IPPrefix, 0, len(configuredPrefixes)+1)
|
||||||
|
|
||||||
|
legacyPrefixField := viper.GetString("ip_prefix")
|
||||||
|
if len(legacyPrefixField) > 0 {
|
||||||
|
log.
|
||||||
|
Warn().
|
||||||
|
Msgf(
|
||||||
|
"%s, %s",
|
||||||
|
"use of 'ip_prefix' for configuration is deprecated",
|
||||||
|
"please see 'ip_prefixes' in the shipped example.",
|
||||||
|
)
|
||||||
|
legacyPrefix, err := netaddr.ParseIPPrefix(legacyPrefixField)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("failed to parse ip_prefix: %w", err))
|
||||||
|
}
|
||||||
|
parsedPrefixes = append(parsedPrefixes, legacyPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, prefixInConfig := range configuredPrefixes {
|
||||||
|
prefix, err := netaddr.ParseIPPrefix(prefixInConfig)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("failed to parse ip_prefixes[%d]: %w", i, err))
|
||||||
|
}
|
||||||
|
parsedPrefixes = append(parsedPrefixes, prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
prefixes := make([]netaddr.IPPrefix, 0, len(parsedPrefixes))
|
||||||
|
{
|
||||||
|
// dedup
|
||||||
|
normalizedPrefixes := make(map[string]int, len(parsedPrefixes))
|
||||||
|
for i, p := range parsedPrefixes {
|
||||||
|
normalized, _ := p.Range().Prefix()
|
||||||
|
normalizedPrefixes[normalized.String()] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert back to list
|
||||||
|
for _, i := range normalizedPrefixes {
|
||||||
|
prefixes = append(prefixes, parsedPrefixes[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(prefixes) < 1 {
|
||||||
|
prefixes = append(prefixes, netaddr.MustParseIPPrefix("100.64.0.0/10"))
|
||||||
|
log.Warn().Msgf("'ip_prefixes' not configured, falling back to default: %v", prefixes)
|
||||||
|
}
|
||||||
|
|
||||||
return headscale.Config{
|
return headscale.Config{
|
||||||
ServerURL: viper.GetString("server_url"),
|
ServerURL: viper.GetString("server_url"),
|
||||||
Addr: viper.GetString("listen_addr"),
|
Addr: viper.GetString("listen_addr"),
|
||||||
IPPrefix: netaddr.MustParseIPPrefix(viper.GetString("ip_prefix")),
|
IPPrefixes: prefixes,
|
||||||
PrivateKeyPath: absPath(viper.GetString("private_key_path")),
|
PrivateKeyPath: absPath(viper.GetString("private_key_path")),
|
||||||
BaseDomain: baseDomain,
|
BaseDomain: baseDomain,
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,13 @@ listen_addr: 0.0.0.0:8080
|
||||||
# autogenerated if it's missing
|
# autogenerated if it's missing
|
||||||
private_key_path: /var/lib/headscale/private.key
|
private_key_path: /var/lib/headscale/private.key
|
||||||
|
|
||||||
|
# List of IP prefixes to allocate tailaddresses from.
|
||||||
|
# Each prefix consists of either an IPv4 or IPv6 address,
|
||||||
|
# and the associated prefix length, delimited by a slash.
|
||||||
|
ip_prefixes:
|
||||||
|
- fd7a:115c:a1e0::/48
|
||||||
|
- 100.64.0.0/10
|
||||||
|
|
||||||
# DERP is a relay system that Tailscale uses when a direct
|
# DERP is a relay system that Tailscale uses when a direct
|
||||||
# connection cannot be established.
|
# connection cannot be established.
|
||||||
# https://tailscale.com/blog/how-tailscale-works/#encrypted-tcp-relays-derp
|
# https://tailscale.com/blog/how-tailscale-works/#encrypted-tcp-relays-derp
|
||||||
|
|
8
db.go
8
db.go
|
@ -28,20 +28,26 @@ func (h *Headscale) initDB() error {
|
||||||
h.db = db
|
h.db = db
|
||||||
|
|
||||||
if h.dbType == Postgres {
|
if h.dbType == Postgres {
|
||||||
db.Exec("create extension if not exists \"uuid-ossp\";")
|
db.Exec(`create extension if not exists "uuid-ossp";`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ = db.Migrator().RenameColumn(&Machine{}, "ip_address", "ip_addresses")
|
||||||
|
|
||||||
err = db.AutoMigrate(&Machine{})
|
err = db.AutoMigrate(&Machine{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.AutoMigrate(&KV{})
|
err = db.AutoMigrate(&KV{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.AutoMigrate(&Namespace{})
|
err = db.AutoMigrate(&Namespace{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.AutoMigrate(&PreAuthKey{})
|
err = db.AutoMigrate(&PreAuthKey{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
82
dns.go
82
dns.go
|
@ -14,6 +14,11 @@ const (
|
||||||
ByteSize = 8
|
ByteSize = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ipv4AddressLength = 32
|
||||||
|
ipv6AddressLength = 128
|
||||||
|
)
|
||||||
|
|
||||||
// generateMagicDNSRootDomains generates a list of DNS entries to be included in `Routes` in `MapResponse`.
|
// 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
|
// 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.
|
// server (listening in 100.100.100.100 udp/53) should be used for.
|
||||||
|
@ -34,14 +39,28 @@ const (
|
||||||
|
|
||||||
// From the netmask we can find out the wildcard bits (the bits that are not set in the netmask).
|
// 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.
|
// This allows us to then calculate the subnets included in the subsequent class block and generate the entries.
|
||||||
func generateMagicDNSRootDomains(
|
func generateMagicDNSRootDomains(ipPrefixes []netaddr.IPPrefix) []dnsname.FQDN {
|
||||||
ipPrefix netaddr.IPPrefix,
|
fqdns := make([]dnsname.FQDN, 0, len(ipPrefixes))
|
||||||
) []dnsname.FQDN {
|
for _, ipPrefix := range ipPrefixes {
|
||||||
// TODO(juanfont): we are not handing out IPv6 addresses yet
|
var generateDNSRoot func(netaddr.IPPrefix) []dnsname.FQDN
|
||||||
// and in fact this is Tailscale.com's range (note the fd7a:115c:a1e0: range in the fc00::/7 network)
|
switch ipPrefix.IP().BitLen() {
|
||||||
ipv6base := dnsname.FQDN("0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa.")
|
case ipv4AddressLength:
|
||||||
fqdns := []dnsname.FQDN{ipv6base}
|
generateDNSRoot = generateIPv4DNSRootDomain
|
||||||
|
|
||||||
|
case ipv6AddressLength:
|
||||||
|
generateDNSRoot = generateIPv6DNSRootDomain
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unsupported IP version with address length %d", ipPrefix.IP().BitLen()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fqdns = append(fqdns, generateDNSRoot(ipPrefix)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fqdns
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIPv4DNSRootDomain(ipPrefix netaddr.IPPrefix) []dnsname.FQDN {
|
||||||
// Conversion to the std lib net.IPnet, a bit easier to operate
|
// Conversion to the std lib net.IPnet, a bit easier to operate
|
||||||
netRange := ipPrefix.IPNet()
|
netRange := ipPrefix.IPNet()
|
||||||
maskBits, _ := netRange.Mask.Size()
|
maskBits, _ := netRange.Mask.Size()
|
||||||
|
@ -65,6 +84,7 @@ func generateMagicDNSRootDomains(
|
||||||
rdnsSlice = append(rdnsSlice, "in-addr.arpa.")
|
rdnsSlice = append(rdnsSlice, "in-addr.arpa.")
|
||||||
rdnsBase := strings.Join(rdnsSlice, ".")
|
rdnsBase := strings.Join(rdnsSlice, ".")
|
||||||
|
|
||||||
|
fqdns := make([]dnsname.FQDN, 0, max-min+1)
|
||||||
for i := min; i <= max; i++ {
|
for i := min; i <= max; i++ {
|
||||||
fqdn, err := dnsname.ToFQDN(fmt.Sprintf("%d.%s", i, rdnsBase))
|
fqdn, err := dnsname.ToFQDN(fmt.Sprintf("%d.%s", i, rdnsBase))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -76,6 +96,54 @@ func generateMagicDNSRootDomains(
|
||||||
return fqdns
|
return fqdns
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateIPv6DNSRootDomain(ipPrefix netaddr.IPPrefix) []dnsname.FQDN {
|
||||||
|
const nibbleLen = 4
|
||||||
|
|
||||||
|
maskBits, _ := ipPrefix.IPNet().Mask.Size()
|
||||||
|
expanded := ipPrefix.IP().StringExpanded()
|
||||||
|
nibbleStr := strings.Map(func(r rune) rune {
|
||||||
|
if r == ':' {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}, expanded)
|
||||||
|
|
||||||
|
// TODO?: that does not look the most efficient implementation,
|
||||||
|
// but the inputs are not so long as to cause problems,
|
||||||
|
// and from what I can see, the generateMagicDNSRootDomains
|
||||||
|
// function is called only once over the lifetime of a server process.
|
||||||
|
prefixConstantParts := []string{}
|
||||||
|
for i := 0; i < maskBits/nibbleLen; i++ {
|
||||||
|
prefixConstantParts = append([]string{string(nibbleStr[i])}, prefixConstantParts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
makeDomain := func(variablePrefix ...string) (dnsname.FQDN, error) {
|
||||||
|
prefix := strings.Join(append(variablePrefix, prefixConstantParts...), ".")
|
||||||
|
|
||||||
|
return dnsname.ToFQDN(fmt.Sprintf("%s.ip6.arpa", prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
var fqdns []dnsname.FQDN
|
||||||
|
if maskBits%4 == 0 {
|
||||||
|
dom, _ := makeDomain()
|
||||||
|
fqdns = append(fqdns, dom)
|
||||||
|
} else {
|
||||||
|
domCount := 1 << (maskBits % nibbleLen)
|
||||||
|
fqdns = make([]dnsname.FQDN, 0, domCount)
|
||||||
|
for i := 0; i < domCount; i++ {
|
||||||
|
varNibble := fmt.Sprintf("%x", i)
|
||||||
|
dom, err := makeDomain(varNibble)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fqdns = append(fqdns, dom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fqdns
|
||||||
|
}
|
||||||
|
|
||||||
func getMapResponseDNSConfig(
|
func getMapResponseDNSConfig(
|
||||||
dnsConfigOrig *tailcfg.DNSConfig,
|
dnsConfigOrig *tailcfg.DNSConfig,
|
||||||
baseDomain string,
|
baseDomain string,
|
||||||
|
|
62
dns_test.go
62
dns_test.go
|
@ -10,8 +10,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Suite) TestMagicDNSRootDomains100(c *check.C) {
|
func (s *Suite) TestMagicDNSRootDomains100(c *check.C) {
|
||||||
prefix := netaddr.MustParseIPPrefix("100.64.0.0/10")
|
prefixes := []netaddr.IPPrefix{
|
||||||
domains := generateMagicDNSRootDomains(prefix)
|
netaddr.MustParseIPPrefix("100.64.0.0/10"),
|
||||||
|
}
|
||||||
|
domains := generateMagicDNSRootDomains(prefixes)
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
|
@ -45,8 +47,10 @@ func (s *Suite) TestMagicDNSRootDomains100(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestMagicDNSRootDomains172(c *check.C) {
|
func (s *Suite) TestMagicDNSRootDomains172(c *check.C) {
|
||||||
prefix := netaddr.MustParseIPPrefix("172.16.0.0/16")
|
prefixes := []netaddr.IPPrefix{
|
||||||
domains := generateMagicDNSRootDomains(prefix)
|
netaddr.MustParseIPPrefix("172.16.0.0/16"),
|
||||||
|
}
|
||||||
|
domains := generateMagicDNSRootDomains(prefixes)
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
|
@ -69,6 +73,40 @@ func (s *Suite) TestMagicDNSRootDomains172(c *check.C) {
|
||||||
c.Assert(found, check.Equals, true)
|
c.Assert(found, check.Equals, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Happens when netmask is a multiple of 4 bits (sounds likely).
|
||||||
|
func (s *Suite) TestMagicDNSRootDomainsIPv6Single(c *check.C) {
|
||||||
|
prefixes := []netaddr.IPPrefix{
|
||||||
|
netaddr.MustParseIPPrefix("fd7a:115c:a1e0::/48"),
|
||||||
|
}
|
||||||
|
domains := generateMagicDNSRootDomains(prefixes)
|
||||||
|
|
||||||
|
c.Assert(len(domains), check.Equals, 1)
|
||||||
|
c.Assert(domains[0].WithTrailingDot(), check.Equals, "0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Suite) TestMagicDNSRootDomainsIPv6SingleMultiple(c *check.C) {
|
||||||
|
prefixes := []netaddr.IPPrefix{
|
||||||
|
netaddr.MustParseIPPrefix("fd7a:115c:a1e0::/50"),
|
||||||
|
}
|
||||||
|
domains := generateMagicDNSRootDomains(prefixes)
|
||||||
|
|
||||||
|
yieldsRoot := func(dom string) bool {
|
||||||
|
for _, candidate := range domains {
|
||||||
|
if candidate.WithTrailingDot() == dom {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Assert(len(domains), check.Equals, 4)
|
||||||
|
c.Assert(yieldsRoot("0.0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa."), check.Equals, true)
|
||||||
|
c.Assert(yieldsRoot("1.0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa."), check.Equals, true)
|
||||||
|
c.Assert(yieldsRoot("2.0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa."), check.Equals, true)
|
||||||
|
c.Assert(yieldsRoot("3.0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa."), check.Equals, true)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
|
func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
|
||||||
namespaceShared1, err := app.CreateNamespace("shared1")
|
namespaceShared1, err := app.CreateNamespace("shared1")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
@ -124,7 +162,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
|
||||||
Namespace: *namespaceShared1,
|
Namespace: *namespaceShared1,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: "100.64.0.1",
|
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")},
|
||||||
AuthKeyID: uint(preAuthKeyInShared1.ID),
|
AuthKeyID: uint(preAuthKeyInShared1.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(machineInShared1)
|
app.db.Save(machineInShared1)
|
||||||
|
@ -142,7 +180,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
|
||||||
Namespace: *namespaceShared2,
|
Namespace: *namespaceShared2,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: "100.64.0.2",
|
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")},
|
||||||
AuthKeyID: uint(preAuthKeyInShared2.ID),
|
AuthKeyID: uint(preAuthKeyInShared2.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(machineInShared2)
|
app.db.Save(machineInShared2)
|
||||||
|
@ -160,7 +198,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
|
||||||
Namespace: *namespaceShared3,
|
Namespace: *namespaceShared3,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: "100.64.0.3",
|
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")},
|
||||||
AuthKeyID: uint(preAuthKeyInShared3.ID),
|
AuthKeyID: uint(preAuthKeyInShared3.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(machineInShared3)
|
app.db.Save(machineInShared3)
|
||||||
|
@ -178,7 +216,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
|
||||||
Namespace: *namespaceShared1,
|
Namespace: *namespaceShared1,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: "100.64.0.4",
|
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")},
|
||||||
AuthKeyID: uint(PreAuthKey2InShared1.ID),
|
AuthKeyID: uint(PreAuthKey2InShared1.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(machine2InShared1)
|
app.db.Save(machine2InShared1)
|
||||||
|
@ -273,7 +311,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
|
||||||
Namespace: *namespaceShared1,
|
Namespace: *namespaceShared1,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: "100.64.0.1",
|
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")},
|
||||||
AuthKeyID: uint(preAuthKeyInShared1.ID),
|
AuthKeyID: uint(preAuthKeyInShared1.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(machineInShared1)
|
app.db.Save(machineInShared1)
|
||||||
|
@ -291,7 +329,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
|
||||||
Namespace: *namespaceShared2,
|
Namespace: *namespaceShared2,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: "100.64.0.2",
|
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")},
|
||||||
AuthKeyID: uint(preAuthKeyInShared2.ID),
|
AuthKeyID: uint(preAuthKeyInShared2.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(machineInShared2)
|
app.db.Save(machineInShared2)
|
||||||
|
@ -309,7 +347,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
|
||||||
Namespace: *namespaceShared3,
|
Namespace: *namespaceShared3,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: "100.64.0.3",
|
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")},
|
||||||
AuthKeyID: uint(preAuthKeyInShared3.ID),
|
AuthKeyID: uint(preAuthKeyInShared3.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(machineInShared3)
|
app.db.Save(machineInShared3)
|
||||||
|
@ -327,7 +365,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
|
||||||
Namespace: *namespaceShared1,
|
Namespace: *namespaceShared1,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: "100.64.0.4",
|
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")},
|
||||||
AuthKeyID: uint(preAuthKey2InShared1.ID),
|
AuthKeyID: uint(preAuthKey2InShared1.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(machine2InShared1)
|
app.db.Save(machine2InShared1)
|
||||||
|
|
|
@ -44,7 +44,7 @@ touch /var/lib/headscale/db.sqlite
|
||||||
touch /etc/headscale/config.yaml
|
touch /etc/headscale/config.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
It is **strongly recommended** to copy and modifiy the [example configuration](../config-example.yaml)
|
It is **strongly recommended** to copy and modify the [example configuration](../config-example.yaml)
|
||||||
from the [headscale repository](../)
|
from the [headscale repository](../)
|
||||||
|
|
||||||
6. Start the headscale server:
|
6. Start the headscale server:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.27.1
|
||||||
// protoc v3.18.1
|
// protoc v3.17.3
|
||||||
// source: headscale/v1/device.proto
|
// source: headscale/v1/device.proto
|
||||||
|
|
||||||
package v1
|
package v1
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.27.1
|
||||||
// protoc v3.18.1
|
// protoc v3.17.3
|
||||||
// source: headscale/v1/headscale.proto
|
// source: headscale/v1/headscale.proto
|
||||||
|
|
||||||
package v1
|
package v1
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.27.1
|
||||||
// protoc v3.18.1
|
// protoc v3.17.3
|
||||||
// source: headscale/v1/machine.proto
|
// source: headscale/v1/machine.proto
|
||||||
|
|
||||||
package v1
|
package v1
|
||||||
|
@ -82,7 +82,7 @@ type Machine struct {
|
||||||
MachineKey string `protobuf:"bytes,2,opt,name=machine_key,json=machineKey,proto3" json:"machine_key,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"`
|
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"`
|
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"`
|
IpAddresses []string `protobuf:"bytes,5,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ip_addresses,omitempty"`
|
||||||
Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"`
|
Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
Namespace *Namespace `protobuf:"bytes,7,opt,name=namespace,proto3" json:"namespace,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"`
|
Registered bool `protobuf:"varint,8,opt,name=registered,proto3" json:"registered,omitempty"`
|
||||||
|
@ -154,11 +154,11 @@ func (x *Machine) GetDiscoKey() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Machine) GetIpAddress() string {
|
func (x *Machine) GetIpAddresses() []string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.IpAddress
|
return x.IpAddresses
|
||||||
}
|
}
|
||||||
return ""
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Machine) GetName() string {
|
func (x *Machine) GetName() string {
|
||||||
|
@ -1026,129 +1026,129 @@ var file_headscale_v1_machine_proto_rawDesc = []byte{
|
||||||
0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70,
|
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,
|
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,
|
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,
|
0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfd, 0x04, 0x0a, 0x07, 0x4d, 0x61, 0x63,
|
||||||
0x68, 0x69, 0x6e, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x0a,
|
||||||
0x0a, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28,
|
0x0c, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x05, 0x20,
|
||||||
0x09, 0x52, 0x09, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04,
|
0x03, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73,
|
||||||
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
|
0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
|
||||||
0x12, 0x35, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x07, 0x20,
|
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63,
|
||||||
0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e,
|
0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63,
|
||||||
0x76, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x09, 0x6e, 0x61,
|
0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
|
||||||
0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x67, 0x69, 0x73,
|
0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72,
|
||||||
0x74, 0x65, 0x72, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x72, 0x65, 0x67,
|
0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||||
0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, 0x45, 0x0a, 0x0f, 0x72, 0x65, 0x67, 0x69, 0x73,
|
0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, 0x45, 0x0a, 0x0f, 0x72,
|
||||||
0x74, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e,
|
0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x09,
|
||||||
0x32, 0x1c, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e,
|
0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65,
|
||||||
0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x0e,
|
0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68,
|
||||||
0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x37,
|
0x6f, 0x64, 0x52, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68,
|
||||||
0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28,
|
0x6f, 0x64, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18,
|
||||||
0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||||
0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x6c,
|
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
|
||||||
0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x50, 0x0a, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x5f,
|
0x70, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x50, 0x0a, 0x16, 0x6c,
|
||||||
0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74,
|
0x61, 0x73, 0x74, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x5f, 0x75,
|
||||||
0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
|
||||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,
|
||||||
0x61, 0x6d, 0x70, 0x52, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73,
|
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x75, 0x63,
|
||||||
0x66, 0x75, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x06, 0x65, 0x78, 0x70,
|
0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a,
|
||||||
0x69, 0x72, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
|
||||||
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,
|
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,
|
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72,
|
||||||
0x65, 0x64, 0x41, 0x74, 0x22, 0x48, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72,
|
0x79, 0x12, 0x3a, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65,
|
||||||
0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c,
|
0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63,
|
||||||
0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65,
|
||||||
0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03,
|
0x79, 0x52, 0x0a, 0x70, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x39, 0x0a,
|
||||||
0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x4a,
|
0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28,
|
||||||
0x0a, 0x17, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
|
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,
|
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,
|
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,
|
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,
|
0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x33, 0x0a, 0x13, 0x4c, 0x69,
|
||||||
0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||||
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,
|
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,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22,
|
||||||
0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
|
0x49, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52,
|
||||||
0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69,
|
||||||
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18,
|
0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64,
|
||||||
0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x22, 0x4d, 0x0a,
|
0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
|
||||||
0x1a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68,
|
0x52, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x52, 0x0a, 0x13, 0x53, 0x68,
|
||||||
0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d,
|
0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||||
0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68,
|
0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18,
|
||||||
0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68,
|
0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64,
|
||||||
0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2a, 0x82, 0x01, 0x0a,
|
0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20,
|
||||||
0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12,
|
0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x47,
|
||||||
0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48,
|
0x0a, 0x14, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65,
|
||||||
0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00,
|
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e,
|
||||||
0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54,
|
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63,
|
||||||
0x48, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x17,
|
0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x07,
|
||||||
0x0a, 0x13, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f,
|
0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x54, 0x0a, 0x15, 0x55, 0x6e, 0x73, 0x68, 0x61,
|
||||||
0x44, 0x5f, 0x43, 0x4c, 0x49, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x47, 0x49, 0x53,
|
0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x4f, 0x49, 0x44, 0x43, 0x10,
|
0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01,
|
||||||
0x03, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
|
0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x12,
|
||||||
0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61,
|
0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01,
|
||||||
0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72,
|
0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x49, 0x0a,
|
||||||
0x6f, 0x74, 0x6f, 0x33,
|
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 (
|
var (
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.27.1
|
||||||
// protoc v3.18.1
|
// protoc v3.17.3
|
||||||
// source: headscale/v1/namespace.proto
|
// source: headscale/v1/namespace.proto
|
||||||
|
|
||||||
package v1
|
package v1
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.27.1
|
||||||
// protoc v3.18.1
|
// protoc v3.17.3
|
||||||
// source: headscale/v1/preauthkey.proto
|
// source: headscale/v1/preauthkey.proto
|
||||||
|
|
||||||
package v1
|
package v1
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.27.1
|
||||||
// protoc v3.18.1
|
// protoc v3.17.3
|
||||||
// source: headscale/v1/routes.proto
|
// source: headscale/v1/routes.proto
|
||||||
|
|
||||||
package v1
|
package v1
|
||||||
|
|
|
@ -775,8 +775,11 @@
|
||||||
"discoKey": {
|
"discoKey": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"ipAddress": {
|
"ipAddresses": {
|
||||||
"type": "string"
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|
|
@ -8,22 +8,48 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"inet.af/netaddr"
|
||||||
|
|
||||||
"github.com/ory/dockertest/v3"
|
"github.com/ory/dockertest/v3"
|
||||||
"github.com/ory/dockertest/v3/docker"
|
"github.com/ory/dockertest/v3/docker"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DOCKER_EXECUTE_TIMEOUT = 10 * time.Second
|
const DOCKER_EXECUTE_TIMEOUT = 10 * time.Second
|
||||||
|
|
||||||
|
var IpPrefix4 = netaddr.MustParseIPPrefix("100.64.0.0/10")
|
||||||
|
var IpPrefix6 = netaddr.MustParseIPPrefix("fd7a:115c:a1e0::/48")
|
||||||
|
|
||||||
|
type ExecuteCommandConfig struct {
|
||||||
|
timeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExecuteCommandOption func(*ExecuteCommandConfig) error
|
||||||
|
|
||||||
|
func ExecuteCommandTimeout(timeout time.Duration) ExecuteCommandOption {
|
||||||
|
return ExecuteCommandOption(func(conf *ExecuteCommandConfig) error {
|
||||||
|
conf.timeout = timeout
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func ExecuteCommand(
|
func ExecuteCommand(
|
||||||
resource *dockertest.Resource,
|
resource *dockertest.Resource,
|
||||||
cmd []string,
|
cmd []string,
|
||||||
env []string,
|
env []string,
|
||||||
|
options ...ExecuteCommandOption,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
var stdout bytes.Buffer
|
var stdout bytes.Buffer
|
||||||
var stderr bytes.Buffer
|
var stderr bytes.Buffer
|
||||||
|
|
||||||
// TODO(kradalby): Make configurable
|
execConfig := ExecuteCommandConfig{
|
||||||
timeout := DOCKER_EXECUTE_TIMEOUT
|
timeout: DOCKER_EXECUTE_TIMEOUT,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range options {
|
||||||
|
if err := opt(&execConfig); err != nil {
|
||||||
|
return "", fmt.Errorf("execute-command/options: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type result struct {
|
type result struct {
|
||||||
exitCode int
|
exitCode int
|
||||||
|
@ -62,16 +88,33 @@ func ExecuteCommand(
|
||||||
}
|
}
|
||||||
|
|
||||||
return stdout.String(), nil
|
return stdout.String(), nil
|
||||||
case <-time.After(timeout):
|
case <-time.After(execConfig.timeout):
|
||||||
|
|
||||||
return "", fmt.Errorf("command timed out after %s", timeout)
|
return "", fmt.Errorf("command timed out after %s", execConfig.timeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DockerRestartPolicy(config *docker.HostConfig) {
|
func DockerRestartPolicy(config *docker.HostConfig) {
|
||||||
// set AutoRemove to true so that stopped container goes away by itself
|
// set AutoRemove to true so that stopped container goes away by itself on error *immediately*.
|
||||||
config.AutoRemove = true
|
// when set to false, containers remain until the end of the integration test.
|
||||||
|
config.AutoRemove = false
|
||||||
config.RestartPolicy = docker.RestartPolicy{
|
config.RestartPolicy = docker.RestartPolicy{
|
||||||
Name: "no",
|
Name: "no",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DockerAllowLocalIPv6(config *docker.HostConfig) {
|
||||||
|
if config.Sysctls == nil {
|
||||||
|
config.Sysctls = make(map[string]string, 1)
|
||||||
|
}
|
||||||
|
config.Sysctls["net.ipv6.conf.all.disable_ipv6"] = "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
func DockerAllowNetworkAdministration(config *docker.HostConfig) {
|
||||||
|
config.CapAdd = append(config.CapAdd, "NET_ADMIN")
|
||||||
|
config.Mounts = append(config.Mounts, docker.HostMount{
|
||||||
|
Type: "bind",
|
||||||
|
Source: "/dev/net/tun",
|
||||||
|
Target: "/dev/net/tun",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -164,9 +164,7 @@ func (s *IntegrationTestSuite) tailscaleContainer(
|
||||||
Name: hostname,
|
Name: hostname,
|
||||||
Networks: []*dockertest.Network{&s.network},
|
Networks: []*dockertest.Network{&s.network},
|
||||||
Cmd: []string{
|
Cmd: []string{
|
||||||
"tailscaled",
|
"tailscaled", "--tun=tsdev",
|
||||||
"--tun=userspace-networking",
|
|
||||||
"--socks5-server=localhost:1055",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,6 +172,8 @@ func (s *IntegrationTestSuite) tailscaleContainer(
|
||||||
tailscaleBuildOptions,
|
tailscaleBuildOptions,
|
||||||
tailscaleOptions,
|
tailscaleOptions,
|
||||||
DockerRestartPolicy,
|
DockerRestartPolicy,
|
||||||
|
DockerAllowLocalIPv6,
|
||||||
|
DockerAllowNetworkAdministration,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Could not start resource: %s", err)
|
log.Fatalf("Could not start resource: %s", err)
|
||||||
|
@ -372,70 +372,74 @@ func (s *IntegrationTestSuite) TestListNodes() {
|
||||||
|
|
||||||
func (s *IntegrationTestSuite) TestGetIpAddresses() {
|
func (s *IntegrationTestSuite) TestGetIpAddresses() {
|
||||||
for _, scales := range s.namespaces {
|
for _, scales := range s.namespaces {
|
||||||
ipPrefix := netaddr.MustParseIPPrefix("100.64.0.0/10")
|
|
||||||
ips, err := getIPs(scales.tailscales)
|
ips, err := getIPs(scales.tailscales)
|
||||||
assert.Nil(s.T(), err)
|
assert.Nil(s.T(), err)
|
||||||
|
|
||||||
for hostname := range scales.tailscales {
|
for hostname, _ := range scales.tailscales {
|
||||||
s.T().Run(hostname, func(t *testing.T) {
|
ips := ips[hostname]
|
||||||
ip, ok := ips[hostname]
|
for _, ip := range ips {
|
||||||
|
s.T().Run(hostname, func(t *testing.T) {
|
||||||
|
assert.NotNil(t, ip)
|
||||||
|
|
||||||
assert.True(t, ok)
|
fmt.Printf("IP for %s: %s\n", hostname, ip)
|
||||||
assert.NotNil(t, ip)
|
|
||||||
|
|
||||||
fmt.Printf("IP for %s: %s\n", hostname, ip)
|
// c.Assert(ip.Valid(), check.IsTrue)
|
||||||
|
assert.True(t, ip.Is4() || ip.Is6())
|
||||||
// c.Assert(ip.Valid(), check.IsTrue)
|
switch {
|
||||||
assert.True(t, ip.Is4())
|
case ip.Is4():
|
||||||
assert.True(t, ipPrefix.Contains(ip))
|
assert.True(t, IpPrefix4.Contains(ip))
|
||||||
})
|
case ip.Is6():
|
||||||
|
assert.True(t, IpPrefix6.Contains(ip))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(kradalby): fix this test
|
// TODO(kradalby): fix this test
|
||||||
// We need some way to impot ipnstate.Status from multiple go packages.
|
// We need some way to import ipnstate.Status from multiple go packages.
|
||||||
// Currently it will only work with 1.18.x since that is the last
|
// Currently it will only work with 1.18.x since that is the last
|
||||||
// version we have in go.mod
|
// version we have in go.mod
|
||||||
// func (s *IntegrationTestSuite) TestStatus() {
|
// func (s *IntegrationTestSuite) TestStatus() {
|
||||||
// for _, scales := range s.namespaces {
|
// for _, scales := range s.namespaces {
|
||||||
// ips, err := getIPs(scales.tailscales)
|
// ips, err := getIPs(scales.tailscales)
|
||||||
// assert.Nil(s.T(), err)
|
// assert.Nil(s.T(), err)
|
||||||
//
|
//
|
||||||
// for hostname, tailscale := range scales.tailscales {
|
// for hostname, tailscale := range scales.tailscales {
|
||||||
// s.T().Run(hostname, func(t *testing.T) {
|
// s.T().Run(hostname, func(t *testing.T) {
|
||||||
// command := []string{"tailscale", "status", "--json"}
|
// command := []string{"tailscale", "status", "--json"}
|
||||||
//
|
//
|
||||||
// fmt.Printf("Getting status for %s\n", hostname)
|
// fmt.Printf("Getting status for %s\n", hostname)
|
||||||
// result, err := ExecuteCommand(
|
// result, err := ExecuteCommand(
|
||||||
// &tailscale,
|
// &tailscale,
|
||||||
// command,
|
// command,
|
||||||
// []string{},
|
// []string{},
|
||||||
// )
|
// )
|
||||||
// assert.Nil(t, err)
|
// assert.Nil(t, err)
|
||||||
//
|
//
|
||||||
// var status ipnstate.Status
|
// var status ipnstate.Status
|
||||||
// err = json.Unmarshal([]byte(result), &status)
|
// err = json.Unmarshal([]byte(result), &status)
|
||||||
// assert.Nil(s.T(), err)
|
// assert.Nil(s.T(), err)
|
||||||
//
|
//
|
||||||
// // TODO(kradalby): Replace this check with peer length of SAME namespace
|
// // TODO(kradalby): Replace this check with peer length of SAME namespace
|
||||||
// // Check if we have as many nodes in status
|
// // Check if we have as many nodes in status
|
||||||
// // as we have IPs/tailscales
|
// // as we have IPs/tailscales
|
||||||
// // lines := strings.Split(result, "\n")
|
// // lines := strings.Split(result, "\n")
|
||||||
// // assert.Equal(t, len(ips), len(lines)-1)
|
// // assert.Equal(t, len(ips), len(lines)-1)
|
||||||
// // assert.Equal(t, len(scales.tailscales), len(lines)-1)
|
// // assert.Equal(t, len(scales.tailscales), len(lines)-1)
|
||||||
//
|
//
|
||||||
// peerIps := getIPsfromIPNstate(status)
|
// peerIps := getIPsfromIPNstate(status)
|
||||||
//
|
//
|
||||||
// // Check that all hosts is present in all hosts status
|
// // Check that all hosts is present in all hosts status
|
||||||
// for ipHostname, ip := range ips {
|
// for ipHostname, ip := range ips {
|
||||||
// if hostname != ipHostname {
|
// if hostname != ipHostname {
|
||||||
// assert.Contains(t, peerIps, ip)
|
// assert.Contains(t, peerIps, ip)
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// })
|
// })
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
func getIPsfromIPNstate(status ipnstate.Status) []netaddr.IP {
|
func getIPsfromIPNstate(status ipnstate.Status) []netaddr.IP {
|
||||||
|
@ -448,16 +452,19 @@ func getIPsfromIPNstate(status ipnstate.Status) []netaddr.IP {
|
||||||
return ips
|
return ips
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *IntegrationTestSuite) TestPingAllPeers() {
|
func (s *IntegrationTestSuite) TestPingAllPeersByAddress() {
|
||||||
for _, scales := range s.namespaces {
|
for _, scales := range s.namespaces {
|
||||||
ips, err := getIPs(scales.tailscales)
|
ips, err := getIPs(scales.tailscales)
|
||||||
assert.Nil(s.T(), err)
|
assert.Nil(s.T(), err)
|
||||||
|
|
||||||
for hostname, tailscale := range scales.tailscales {
|
for hostname, tailscale := range scales.tailscales {
|
||||||
for peername, ip := range ips {
|
for peername, peerIPs := range ips {
|
||||||
s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) {
|
for i, ip := range peerIPs {
|
||||||
// We currently cant ping ourselves, so skip that.
|
// We currently cant ping ourselves, so skip that.
|
||||||
if peername != hostname {
|
if peername == hostname {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.T().Run(fmt.Sprintf("%s-%s-%d", hostname, peername, i), func(t *testing.T) {
|
||||||
// We are only interested in "direct ping" which means what we
|
// We are only interested in "direct ping" which means what we
|
||||||
// might need a couple of more attempts before reaching the node.
|
// might need a couple of more attempts before reaching the node.
|
||||||
command := []string{
|
command := []string{
|
||||||
|
@ -469,9 +476,8 @@ func (s *IntegrationTestSuite) TestPingAllPeers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf(
|
fmt.Printf(
|
||||||
"Pinging from %s (%s) to %s (%s)\n",
|
"Pinging from %s to %s (%s)\n",
|
||||||
hostname,
|
hostname,
|
||||||
ips[hostname],
|
|
||||||
peername,
|
peername,
|
||||||
ip,
|
ip,
|
||||||
)
|
)
|
||||||
|
@ -483,8 +489,8 @@ func (s *IntegrationTestSuite) TestPingAllPeers() {
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
fmt.Printf("Result for %s: %s\n", hostname, result)
|
fmt.Printf("Result for %s: %s\n", hostname, result)
|
||||||
assert.Contains(t, result, "pong")
|
assert.Contains(t, result, "pong")
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -553,17 +559,17 @@ func (s *IntegrationTestSuite) TestSharedNodes() {
|
||||||
// TODO(juanfont): We have to find out why do we need to wait
|
// TODO(juanfont): We have to find out why do we need to wait
|
||||||
time.Sleep(100 * time.Second) // Wait for the nodes to receive updates
|
time.Sleep(100 * time.Second) // Wait for the nodes to receive updates
|
||||||
|
|
||||||
mainIps, err := getIPs(main.tailscales)
|
|
||||||
assert.Nil(s.T(), err)
|
|
||||||
|
|
||||||
sharedIps, err := getIPs(shared.tailscales)
|
sharedIps, err := getIPs(shared.tailscales)
|
||||||
assert.Nil(s.T(), err)
|
assert.Nil(s.T(), err)
|
||||||
|
|
||||||
for hostname, tailscale := range main.tailscales {
|
for hostname, tailscale := range main.tailscales {
|
||||||
for peername, ip := range sharedIps {
|
for peername, peerIPs := range sharedIps {
|
||||||
s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) {
|
for i, ip := range peerIPs {
|
||||||
// We currently cant ping ourselves, so skip that.
|
// We currently cant ping ourselves, so skip that.
|
||||||
if peername != hostname {
|
if peername == hostname {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.T().Run(fmt.Sprintf("%s-%s-%d", hostname, peername, i), func(t *testing.T) {
|
||||||
// We are only interested in "direct ping" which means what we
|
// We are only interested in "direct ping" which means what we
|
||||||
// might need a couple of more attempts before reaching the node.
|
// might need a couple of more attempts before reaching the node.
|
||||||
command := []string{
|
command := []string{
|
||||||
|
@ -575,9 +581,8 @@ func (s *IntegrationTestSuite) TestSharedNodes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf(
|
fmt.Printf(
|
||||||
"Pinging from %s (%s) to %s (%s)\n",
|
"Pinging from %s to %s (%s)\n",
|
||||||
hostname,
|
hostname,
|
||||||
mainIps[hostname],
|
|
||||||
peername,
|
peername,
|
||||||
ip,
|
ip,
|
||||||
)
|
)
|
||||||
|
@ -589,8 +594,8 @@ func (s *IntegrationTestSuite) TestSharedNodes() {
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
fmt.Printf("Result for %s: %s\n", hostname, result)
|
fmt.Printf("Result for %s: %s\n", hostname, result)
|
||||||
assert.Contains(t, result, "pong")
|
assert.Contains(t, result, "pong")
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -599,9 +604,19 @@ func (s *IntegrationTestSuite) TestTailDrop() {
|
||||||
for _, scales := range s.namespaces {
|
for _, scales := range s.namespaces {
|
||||||
ips, err := getIPs(scales.tailscales)
|
ips, err := getIPs(scales.tailscales)
|
||||||
assert.Nil(s.T(), err)
|
assert.Nil(s.T(), err)
|
||||||
apiURLs, err := getAPIURLs(scales.tailscales)
|
|
||||||
assert.Nil(s.T(), err)
|
assert.Nil(s.T(), err)
|
||||||
|
|
||||||
|
retry := func(times int, sleepInverval time.Duration, doWork func() error) (err error) {
|
||||||
|
for attempts := 0; attempts < times; attempts++ {
|
||||||
|
err = doWork()
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Sleep(sleepInverval)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for hostname, tailscale := range scales.tailscales {
|
for hostname, tailscale := range scales.tailscales {
|
||||||
command := []string{"touch", fmt.Sprintf("/tmp/file_from_%s", hostname)}
|
command := []string{"touch", fmt.Sprintf("/tmp/file_from_%s", hostname)}
|
||||||
_, err := ExecuteCommand(
|
_, err := ExecuteCommand(
|
||||||
|
@ -610,63 +625,31 @@ func (s *IntegrationTestSuite) TestTailDrop() {
|
||||||
[]string{},
|
[]string{},
|
||||||
)
|
)
|
||||||
assert.Nil(s.T(), err)
|
assert.Nil(s.T(), err)
|
||||||
for peername, ip := range ips {
|
for peername, _ := range ips {
|
||||||
|
if peername == hostname {
|
||||||
|
continue
|
||||||
|
}
|
||||||
s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) {
|
s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) {
|
||||||
if peername != hostname {
|
command := []string{
|
||||||
// Under normal circumstances, we should be able to send a file
|
"tailscale", "file", "cp",
|
||||||
// using `tailscale file cp` - but not in userspace networking mode
|
fmt.Sprintf("/tmp/file_from_%s", hostname),
|
||||||
// So curl!
|
fmt.Sprintf("%s:", peername),
|
||||||
peerAPI, ok := apiURLs[ip]
|
|
||||||
assert.True(t, ok)
|
|
||||||
|
|
||||||
// TODO(juanfont): We still have some issues with the test infrastructure, so
|
|
||||||
// lets run curl multiple times until it works.
|
|
||||||
attempts := 0
|
|
||||||
var err error
|
|
||||||
for {
|
|
||||||
command := []string{
|
|
||||||
"curl",
|
|
||||||
"--retry-connrefused",
|
|
||||||
"--retry-delay",
|
|
||||||
"30",
|
|
||||||
"--retry",
|
|
||||||
"10",
|
|
||||||
"--connect-timeout",
|
|
||||||
"60",
|
|
||||||
"-X",
|
|
||||||
"PUT",
|
|
||||||
"--upload-file",
|
|
||||||
fmt.Sprintf("/tmp/file_from_%s", 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(
|
|
||||||
&tailscale,
|
|
||||||
command,
|
|
||||||
[]string{"ALL_PROXY=socks5://localhost:1055"},
|
|
||||||
)
|
|
||||||
if err == nil {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
attempts++
|
|
||||||
if attempts > 10 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert.Nil(t, err)
|
|
||||||
}
|
}
|
||||||
|
retry(10, 1*time.Second, func() error {
|
||||||
|
fmt.Printf(
|
||||||
|
"Sending file from %s to %s\n",
|
||||||
|
hostname,
|
||||||
|
peername,
|
||||||
|
)
|
||||||
|
_, err := ExecuteCommand(
|
||||||
|
&tailscale,
|
||||||
|
command,
|
||||||
|
[]string{},
|
||||||
|
ExecuteCommandTimeout(60*time.Second),
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
assert.Nil(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -684,32 +667,70 @@ func (s *IntegrationTestSuite) TestTailDrop() {
|
||||||
)
|
)
|
||||||
assert.Nil(s.T(), err)
|
assert.Nil(s.T(), err)
|
||||||
for peername, ip := range ips {
|
for peername, ip := range ips {
|
||||||
|
if peername == hostname {
|
||||||
|
continue
|
||||||
|
}
|
||||||
s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) {
|
s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) {
|
||||||
if peername != hostname {
|
command := []string{
|
||||||
command := []string{
|
"ls",
|
||||||
"ls",
|
fmt.Sprintf("/tmp/file_from_%s", peername),
|
||||||
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(
|
|
||||||
&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),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
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,
|
||||||
|
fmt.Sprintf("/tmp/file_from_%s\n", peername),
|
||||||
|
result,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IntegrationTestSuite) TestPingAllPeersByHostname() {
|
||||||
|
for namespace, scales := range s.namespaces {
|
||||||
|
ips, err := getIPs(scales.tailscales)
|
||||||
|
assert.Nil(s.T(), err)
|
||||||
|
for hostname, tailscale := range scales.tailscales {
|
||||||
|
for peername, _ := range ips {
|
||||||
|
if peername == hostname {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) {
|
||||||
|
command := []string{
|
||||||
|
"tailscale", "ping",
|
||||||
|
"--timeout=10s",
|
||||||
|
"--c=20",
|
||||||
|
"--until-direct=true",
|
||||||
|
fmt.Sprintf("%s.%s.headscale.net", peername, namespace),
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(
|
||||||
|
"Pinging using hostname from %s to %s\n",
|
||||||
|
hostname,
|
||||||
|
peername,
|
||||||
|
)
|
||||||
|
result, err := ExecuteCommand(
|
||||||
|
&tailscale,
|
||||||
|
command,
|
||||||
|
[]string{},
|
||||||
|
)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
fmt.Printf("Result for %s: %s\n", hostname, result)
|
||||||
|
assert.Contains(t, result, "pong")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -721,32 +742,31 @@ func (s *IntegrationTestSuite) TestMagicDNS() {
|
||||||
ips, err := getIPs(scales.tailscales)
|
ips, err := getIPs(scales.tailscales)
|
||||||
assert.Nil(s.T(), err)
|
assert.Nil(s.T(), err)
|
||||||
for hostname, tailscale := range scales.tailscales {
|
for hostname, tailscale := range scales.tailscales {
|
||||||
for peername, ip := range ips {
|
for peername, ips := range ips {
|
||||||
|
if peername == hostname {
|
||||||
|
continue
|
||||||
|
}
|
||||||
s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) {
|
s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) {
|
||||||
if peername != hostname {
|
command := []string{
|
||||||
command := []string{
|
"tailscale", "ip",
|
||||||
"tailscale", "ping",
|
fmt.Sprintf("%s.%s.headscale.net", peername, namespace),
|
||||||
"--timeout=10s",
|
}
|
||||||
"--c=20",
|
|
||||||
"--until-direct=true",
|
|
||||||
fmt.Sprintf("%s.%s.headscale.net", peername, namespace),
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf(
|
fmt.Printf(
|
||||||
"Pinging using Hostname (magicdns) from %s (%s) to %s (%s)\n",
|
"Resolving name %s from %s\n",
|
||||||
hostname,
|
peername,
|
||||||
ips[hostname],
|
hostname,
|
||||||
peername,
|
)
|
||||||
ip,
|
result, err := ExecuteCommand(
|
||||||
)
|
&tailscale,
|
||||||
result, err := ExecuteCommand(
|
command,
|
||||||
&tailscale,
|
[]string{},
|
||||||
command,
|
)
|
||||||
[]string{},
|
assert.Nil(t, err)
|
||||||
)
|
fmt.Printf("Result for %s: %s\n", hostname, result)
|
||||||
assert.Nil(t, err)
|
|
||||||
fmt.Printf("Result for %s: %s\n", hostname, result)
|
for _, ip := range ips {
|
||||||
assert.Contains(t, result, "pong")
|
assert.Contains(t, result, ip.String())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -754,8 +774,8 @@ func (s *IntegrationTestSuite) TestMagicDNS() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getIPs(tailscales map[string]dockertest.Resource) (map[string]netaddr.IP, error) {
|
func getIPs(tailscales map[string]dockertest.Resource) (map[string][]netaddr.IP, error) {
|
||||||
ips := make(map[string]netaddr.IP)
|
ips := make(map[string][]netaddr.IP)
|
||||||
for hostname, tailscale := range tailscales {
|
for hostname, tailscale := range tailscales {
|
||||||
command := []string{"tailscale", "ip"}
|
command := []string{"tailscale", "ip"}
|
||||||
|
|
||||||
|
@ -768,12 +788,17 @@ func getIPs(tailscales map[string]dockertest.Resource) (map[string]netaddr.IP, e
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ip, err := netaddr.ParseIP(strings.TrimSuffix(result, "\n"))
|
for _, address := range strings.Split(result, "\n") {
|
||||||
if err != nil {
|
address = strings.TrimSuffix(address, "\n")
|
||||||
return nil, err
|
if len(address) < 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ip, err := netaddr.ParseIP(address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ips[hostname] = append(ips[hostname], ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
ips[hostname] = ip
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ips, nil
|
return ips, nil
|
||||||
|
|
|
@ -2,6 +2,9 @@ log_level: trace
|
||||||
acl_policy_path: ""
|
acl_policy_path: ""
|
||||||
db_type: sqlite3
|
db_type: sqlite3
|
||||||
ephemeral_node_inactivity_timeout: 30m
|
ephemeral_node_inactivity_timeout: 30m
|
||||||
|
ip_prefixes:
|
||||||
|
- fd7a:115c:a1e0::/48
|
||||||
|
- 100.64.0.0/10
|
||||||
dns_config:
|
dns_config:
|
||||||
base_domain: headscale.net
|
base_domain: headscale.net
|
||||||
magic_dns: true
|
magic_dns: true
|
||||||
|
|
91
machine.go
91
machine.go
|
@ -1,6 +1,7 @@
|
||||||
package headscale
|
package headscale
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -23,6 +24,7 @@ const (
|
||||||
errMachineNotFound = Error("machine not found")
|
errMachineNotFound = Error("machine not found")
|
||||||
errMachineAlreadyRegistered = Error("machine already registered")
|
errMachineAlreadyRegistered = Error("machine already registered")
|
||||||
errMachineRouteIsNotAvailable = Error("route is not available on machine")
|
errMachineRouteIsNotAvailable = Error("route is not available on machine")
|
||||||
|
errMachineAddressesInvalid = Error("failed to parse machine addresses")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Machine is a Headscale client.
|
// Machine is a Headscale client.
|
||||||
|
@ -31,7 +33,7 @@ type Machine struct {
|
||||||
MachineKey string `gorm:"type:varchar(64);unique_index"`
|
MachineKey string `gorm:"type:varchar(64);unique_index"`
|
||||||
NodeKey string
|
NodeKey string
|
||||||
DiscoKey string
|
DiscoKey string
|
||||||
IPAddress string
|
IPAddresses MachineAddresses
|
||||||
Name string
|
Name string
|
||||||
NamespaceID uint
|
NamespaceID uint
|
||||||
Namespace Namespace `gorm:"foreignKey:NamespaceID"`
|
Namespace Namespace `gorm:"foreignKey:NamespaceID"`
|
||||||
|
@ -64,6 +66,47 @@ func (machine Machine) isRegistered() bool {
|
||||||
return machine.Registered
|
return machine.Registered
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MachineAddresses []netaddr.IP
|
||||||
|
|
||||||
|
func (ma MachineAddresses) ToStringSlice() []string {
|
||||||
|
strSlice := make([]string, 0, len(ma))
|
||||||
|
for _, addr := range ma {
|
||||||
|
strSlice = append(strSlice, addr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return strSlice
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ma *MachineAddresses) Scan(destination interface{}) error {
|
||||||
|
switch value := destination.(type) {
|
||||||
|
case string:
|
||||||
|
addresses := strings.Split(value, ",")
|
||||||
|
*ma = (*ma)[:0]
|
||||||
|
for _, addr := range addresses {
|
||||||
|
if len(addr) < 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parsed, err := netaddr.ParseIP(addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*ma = append(*ma, parsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value return json value, implement driver.Valuer interface.
|
||||||
|
func (ma MachineAddresses) Value() (driver.Value, error) {
|
||||||
|
addresses := strings.Join(ma.ToStringSlice(), ",")
|
||||||
|
|
||||||
|
return addresses, nil
|
||||||
|
}
|
||||||
|
|
||||||
// isExpired returns whether the machine registration has expired.
|
// isExpired returns whether the machine registration has expired.
|
||||||
func (machine Machine) isExpired() bool {
|
func (machine Machine) isExpired() bool {
|
||||||
// If Expiry is not set, the client has not indicated that
|
// If Expiry is not set, the client has not indicated that
|
||||||
|
@ -385,14 +428,18 @@ func (h *Headscale) isOutdated(machine *Machine) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
lastChange := h.getLastStateChange(namespaces...)
|
lastChange := h.getLastStateChange(namespaces...)
|
||||||
|
lastUpdate := machine.CreatedAt
|
||||||
|
if machine.LastSuccessfulUpdate != nil {
|
||||||
|
lastUpdate = *machine.LastSuccessfulUpdate
|
||||||
|
}
|
||||||
log.Trace().
|
log.Trace().
|
||||||
Caller().
|
Caller().
|
||||||
Str("machine", machine.Name).
|
Str("machine", machine.Name).
|
||||||
Time("last_successful_update", *machine.LastSuccessfulUpdate).
|
Time("last_successful_update", lastChange).
|
||||||
Time("last_state_change", lastChange).
|
Time("last_state_change", lastUpdate).
|
||||||
Msgf("Checking if %s is missing updates", machine.Name)
|
Msgf("Checking if %s is missing updates", machine.Name)
|
||||||
|
|
||||||
return machine.LastSuccessfulUpdate.Before(lastChange)
|
return lastUpdate.Before(lastChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (machine Machine) String() string {
|
func (machine Machine) String() string {
|
||||||
|
@ -478,22 +525,12 @@ func (machine Machine) toNode(
|
||||||
}
|
}
|
||||||
|
|
||||||
addrs := []netaddr.IPPrefix{}
|
addrs := []netaddr.IPPrefix{}
|
||||||
ip, err := netaddr.ParseIPPrefix(fmt.Sprintf("%s/32", machine.IPAddress))
|
for _, machineAddress := range machine.IPAddresses {
|
||||||
if err != nil {
|
ip := netaddr.IPPrefixFrom(machineAddress, machineAddress.BitLen())
|
||||||
log.Trace().
|
addrs = append(addrs, ip)
|
||||||
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([]netaddr.IPPrefix{}, addrs...) // 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 {
|
if includeRoutes {
|
||||||
routesStr := []string{}
|
routesStr := []string{}
|
||||||
|
@ -600,11 +637,11 @@ func (machine *Machine) toProto() *v1.Machine {
|
||||||
Id: machine.ID,
|
Id: machine.ID,
|
||||||
MachineKey: machine.MachineKey,
|
MachineKey: machine.MachineKey,
|
||||||
|
|
||||||
NodeKey: machine.NodeKey,
|
NodeKey: machine.NodeKey,
|
||||||
DiscoKey: machine.DiscoKey,
|
DiscoKey: machine.DiscoKey,
|
||||||
IpAddress: machine.IPAddress,
|
IpAddresses: machine.IPAddresses.ToStringSlice(),
|
||||||
Name: machine.Name,
|
Name: machine.Name,
|
||||||
Namespace: machine.Namespace.toProto(),
|
Namespace: machine.Namespace.toProto(),
|
||||||
|
|
||||||
Registered: machine.Registered,
|
Registered: machine.Registered,
|
||||||
|
|
||||||
|
@ -703,7 +740,7 @@ func (h *Headscale) RegisterMachine(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ip, err := h.getAvailableIP()
|
ips, err := h.getAvailableIPs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Caller().
|
Caller().
|
||||||
|
@ -717,10 +754,10 @@ func (h *Headscale) RegisterMachine(
|
||||||
log.Trace().
|
log.Trace().
|
||||||
Caller().
|
Caller().
|
||||||
Str("machine", machine.Name).
|
Str("machine", machine.Name).
|
||||||
Str("ip", ip.String()).
|
Str("ip", strings.Join(ips.ToStringSlice(), ",")).
|
||||||
Msg("Found IP for host")
|
Msg("Found IP for host")
|
||||||
|
|
||||||
machine.IPAddress = ip.String()
|
machine.IPAddresses = ips
|
||||||
machine.NamespaceID = namespace.ID
|
machine.NamespaceID = namespace.ID
|
||||||
machine.Registered = true
|
machine.Registered = true
|
||||||
machine.RegisterMethod = RegisterMethodCLI
|
machine.RegisterMethod = RegisterMethodCLI
|
||||||
|
@ -730,7 +767,7 @@ func (h *Headscale) RegisterMachine(
|
||||||
log.Trace().
|
log.Trace().
|
||||||
Caller().
|
Caller().
|
||||||
Str("machine", machine.Name).
|
Str("machine", machine.Name).
|
||||||
Str("ip", ip.String()).
|
Str("ip", strings.Join(ips.ToStringSlice(), ",")).
|
||||||
Msg("Machine registered with the database")
|
Msg("Machine registered with the database")
|
||||||
|
|
||||||
return machine, nil
|
return machine, nil
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gopkg.in/check.v1"
|
"gopkg.in/check.v1"
|
||||||
|
"inet.af/netaddr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Suite) TestGetMachine(c *check.C) {
|
func (s *Suite) TestGetMachine(c *check.C) {
|
||||||
|
@ -199,3 +200,24 @@ func (s *Suite) TestExpireMachine(c *check.C) {
|
||||||
|
|
||||||
c.Assert(machineFromDB.isExpired(), check.Equals, true)
|
c.Assert(machineFromDB.isExpired(), check.Equals, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Suite) TestSerdeAddressStrignSlice(c *check.C) {
|
||||||
|
input := MachineAddresses([]netaddr.IP{
|
||||||
|
netaddr.MustParseIP("192.0.2.1"),
|
||||||
|
netaddr.MustParseIP("2001:db8::1"),
|
||||||
|
})
|
||||||
|
serialized, err := input.Value()
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
if serial, ok := serialized.(string); ok {
|
||||||
|
c.Assert(serial, check.Equals, "192.0.2.1,2001:db8::1")
|
||||||
|
}
|
||||||
|
|
||||||
|
var deserialized MachineAddresses
|
||||||
|
err = deserialized.Scan(serialized)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
c.Assert(len(deserialized), check.Equals, len(input))
|
||||||
|
for i := range deserialized {
|
||||||
|
c.Assert(deserialized[i], check.Equals, input[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"gopkg.in/check.v1"
|
"gopkg.in/check.v1"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"inet.af/netaddr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Suite) TestCreateAndDestroyNamespace(c *check.C) {
|
func (s *Suite) TestCreateAndDestroyNamespace(c *check.C) {
|
||||||
|
@ -146,7 +147,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
|
||||||
Namespace: *namespaceShared1,
|
Namespace: *namespaceShared1,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: "100.64.0.1",
|
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")},
|
||||||
AuthKeyID: uint(preAuthKeyShared1.ID),
|
AuthKeyID: uint(preAuthKeyShared1.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(machineInShared1)
|
app.db.Save(machineInShared1)
|
||||||
|
@ -164,7 +165,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
|
||||||
Namespace: *namespaceShared2,
|
Namespace: *namespaceShared2,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: "100.64.0.2",
|
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")},
|
||||||
AuthKeyID: uint(preAuthKeyShared2.ID),
|
AuthKeyID: uint(preAuthKeyShared2.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(machineInShared2)
|
app.db.Save(machineInShared2)
|
||||||
|
@ -182,7 +183,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
|
||||||
Namespace: *namespaceShared3,
|
Namespace: *namespaceShared3,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: "100.64.0.3",
|
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")},
|
||||||
AuthKeyID: uint(preAuthKeyShared3.ID),
|
AuthKeyID: uint(preAuthKeyShared3.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(machineInShared3)
|
app.db.Save(machineInShared3)
|
||||||
|
@ -200,7 +201,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
|
||||||
Namespace: *namespaceShared1,
|
Namespace: *namespaceShared1,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: "100.64.0.4",
|
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")},
|
||||||
AuthKeyID: uint(preAuthKey2Shared1.ID),
|
AuthKeyID: uint(preAuthKey2Shared1.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(machine2InShared1)
|
app.db.Save(machine2InShared1)
|
||||||
|
|
5
oidc.go
5
oidc.go
|
@ -126,6 +126,7 @@ var oidcCallbackTemplate = template.Must(
|
||||||
</html>`),
|
</html>`),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: Why is the entire machine registration logic duplicated here?
|
||||||
// OIDCCallback handles the callback from the OIDC endpoint
|
// OIDCCallback handles the callback from the OIDC endpoint
|
||||||
// Retrieves the mkey from the state cache and adds the machine to the users email namespace
|
// 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: A confirmation page for new machines should be added to avoid phishing vulnerabilities
|
||||||
|
@ -316,7 +317,7 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ip, err := h.getAvailableIP()
|
ips, err := h.getAvailableIPs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Caller().
|
Caller().
|
||||||
|
@ -330,7 +331,7 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
machine.IPAddress = ip.String()
|
machine.IPAddresses = ips
|
||||||
machine.NamespaceID = namespace.ID
|
machine.NamespaceID = namespace.ID
|
||||||
machine.Registered = true
|
machine.Registered = true
|
||||||
machine.RegisterMethod = RegisterMethodOIDC
|
machine.RegisterMethod = RegisterMethodOIDC
|
||||||
|
|
105
poll.go
105
poll.go
|
@ -1,8 +1,10 @@
|
||||||
package headscale
|
package headscale
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
@ -154,14 +156,33 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
|
||||||
Str("id", ctx.Param("id")).
|
Str("id", ctx.Param("id")).
|
||||||
Str("machine", machine.Name).
|
Str("machine", machine.Name).
|
||||||
Msg("Loading or creating update channel")
|
Msg("Loading or creating update channel")
|
||||||
updateChan := make(chan struct{})
|
|
||||||
|
|
||||||
pollDataChan := make(chan []byte)
|
// TODO: could probably remove all that duplication once generics land.
|
||||||
|
closeChanWithLog := func(channel interface{}, name string) {
|
||||||
|
log.Trace().
|
||||||
|
Str("handler", "PollNetMap").
|
||||||
|
Str("machine", machine.Name).
|
||||||
|
Str("channel", "Done").
|
||||||
|
Msg(fmt.Sprintf("Closing %s channel", name))
|
||||||
|
|
||||||
|
switch c := channel.(type) {
|
||||||
|
case (chan struct{}):
|
||||||
|
close(c)
|
||||||
|
|
||||||
|
case (chan []byte):
|
||||||
|
close(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const chanSize = 8
|
||||||
|
updateChan := make(chan struct{}, chanSize)
|
||||||
|
defer closeChanWithLog(updateChan, "updateChan")
|
||||||
|
|
||||||
|
pollDataChan := make(chan []byte, chanSize)
|
||||||
|
defer closeChanWithLog(pollDataChan, "pollDataChan")
|
||||||
|
|
||||||
keepAliveChan := make(chan []byte)
|
keepAliveChan := make(chan []byte)
|
||||||
|
defer closeChanWithLog(keepAliveChan, "keepAliveChan")
|
||||||
cancelKeepAlive := make(chan struct{})
|
|
||||||
defer close(cancelKeepAlive)
|
|
||||||
|
|
||||||
if req.OmitPeers && !req.Stream {
|
if req.OmitPeers && !req.Stream {
|
||||||
log.Info().
|
log.Info().
|
||||||
|
@ -174,7 +195,7 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
|
||||||
// even tho the comments in the tailscale code dont explicitly say so.
|
// even tho the comments in the tailscale code dont explicitly say so.
|
||||||
updateRequestsFromNode.WithLabelValues(machine.Name, machine.Namespace.Name, "endpoint-update").
|
updateRequestsFromNode.WithLabelValues(machine.Name, machine.Namespace.Name, "endpoint-update").
|
||||||
Inc()
|
Inc()
|
||||||
go func() { updateChan <- struct{}{} }()
|
updateChan <- struct{}{}
|
||||||
|
|
||||||
return
|
return
|
||||||
} else if req.OmitPeers && req.Stream {
|
} else if req.OmitPeers && req.Stream {
|
||||||
|
@ -195,7 +216,7 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
|
||||||
Str("handler", "PollNetMap").
|
Str("handler", "PollNetMap").
|
||||||
Str("machine", machine.Name).
|
Str("machine", machine.Name).
|
||||||
Msg("Sending initial map")
|
Msg("Sending initial map")
|
||||||
go func() { pollDataChan <- data }()
|
pollDataChan <- data
|
||||||
|
|
||||||
log.Info().
|
log.Info().
|
||||||
Str("handler", "PollNetMap").
|
Str("handler", "PollNetMap").
|
||||||
|
@ -203,7 +224,7 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
|
||||||
Msg("Notifying peers")
|
Msg("Notifying peers")
|
||||||
updateRequestsFromNode.WithLabelValues(machine.Name, machine.Namespace.Name, "full-update").
|
updateRequestsFromNode.WithLabelValues(machine.Name, machine.Namespace.Name, "full-update").
|
||||||
Inc()
|
Inc()
|
||||||
go func() { updateChan <- struct{}{} }()
|
updateChan <- struct{}{}
|
||||||
|
|
||||||
h.PollNetMapStream(
|
h.PollNetMapStream(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -213,7 +234,6 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
|
||||||
pollDataChan,
|
pollDataChan,
|
||||||
keepAliveChan,
|
keepAliveChan,
|
||||||
updateChan,
|
updateChan,
|
||||||
cancelKeepAlive,
|
|
||||||
)
|
)
|
||||||
log.Trace().
|
log.Trace().
|
||||||
Str("handler", "PollNetMap").
|
Str("handler", "PollNetMap").
|
||||||
|
@ -233,16 +253,20 @@ func (h *Headscale) PollNetMapStream(
|
||||||
pollDataChan chan []byte,
|
pollDataChan chan []byte,
|
||||||
keepAliveChan chan []byte,
|
keepAliveChan chan []byte,
|
||||||
updateChan chan struct{},
|
updateChan chan struct{},
|
||||||
cancelKeepAlive chan struct{},
|
|
||||||
) {
|
) {
|
||||||
go h.scheduledPollWorker(
|
{
|
||||||
cancelKeepAlive,
|
ctx, cancel := context.WithCancel(ctx.Request.Context())
|
||||||
updateChan,
|
defer cancel()
|
||||||
keepAliveChan,
|
|
||||||
machineKey,
|
go h.scheduledPollWorker(
|
||||||
mapRequest,
|
ctx,
|
||||||
machine,
|
updateChan,
|
||||||
)
|
keepAliveChan,
|
||||||
|
machineKey,
|
||||||
|
mapRequest,
|
||||||
|
machine,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
ctx.Stream(func(writer io.Writer) bool {
|
ctx.Stream(func(writer io.Writer) bool {
|
||||||
log.Trace().
|
log.Trace().
|
||||||
|
@ -392,10 +416,14 @@ func (h *Headscale) PollNetMapStream(
|
||||||
updateRequestsReceivedOnChannel.WithLabelValues(machine.Name, machine.Namespace.Name).
|
updateRequestsReceivedOnChannel.WithLabelValues(machine.Name, machine.Namespace.Name).
|
||||||
Inc()
|
Inc()
|
||||||
if h.isOutdated(machine) {
|
if h.isOutdated(machine) {
|
||||||
|
var lastUpdate time.Time
|
||||||
|
if machine.LastSuccessfulUpdate != nil {
|
||||||
|
lastUpdate = *machine.LastSuccessfulUpdate
|
||||||
|
}
|
||||||
log.Debug().
|
log.Debug().
|
||||||
Str("handler", "PollNetMapStream").
|
Str("handler", "PollNetMapStream").
|
||||||
Str("machine", machine.Name).
|
Str("machine", machine.Name).
|
||||||
Time("last_successful_update", *machine.LastSuccessfulUpdate).
|
Time("last_successful_update", lastUpdate).
|
||||||
Time("last_state_change", h.getLastStateChange(machine.Namespace.Name)).
|
Time("last_state_change", h.getLastStateChange(machine.Namespace.Name)).
|
||||||
Msgf("There has been updates since the last successful update to %s", machine.Name)
|
Msgf("There has been updates since the last successful update to %s", machine.Name)
|
||||||
data, err := h.getMapResponse(machineKey, mapRequest, machine)
|
data, err := h.getMapResponse(machineKey, mapRequest, machine)
|
||||||
|
@ -464,10 +492,14 @@ func (h *Headscale) PollNetMapStream(
|
||||||
Msg("Cannot update machine LastSuccessfulUpdate")
|
Msg("Cannot update machine LastSuccessfulUpdate")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
var lastUpdate time.Time
|
||||||
|
if machine.LastSuccessfulUpdate != nil {
|
||||||
|
lastUpdate = *machine.LastSuccessfulUpdate
|
||||||
|
}
|
||||||
log.Trace().
|
log.Trace().
|
||||||
Str("handler", "PollNetMapStream").
|
Str("handler", "PollNetMapStream").
|
||||||
Str("machine", machine.Name).
|
Str("machine", machine.Name).
|
||||||
Time("last_successful_update", *machine.LastSuccessfulUpdate).
|
Time("last_successful_update", lastUpdate).
|
||||||
Time("last_state_change", h.getLastStateChange(machine.Namespace.Name)).
|
Time("last_state_change", h.getLastStateChange(machine.Namespace.Name)).
|
||||||
Msgf("%s is up to date", machine.Name)
|
Msgf("%s is up to date", machine.Name)
|
||||||
}
|
}
|
||||||
|
@ -507,42 +539,13 @@ func (h *Headscale) PollNetMapStream(
|
||||||
Msg("Cannot update machine LastSeen")
|
Msg("Cannot update machine LastSeen")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace().
|
|
||||||
Str("handler", "PollNetMapStream").
|
|
||||||
Str("machine", machine.Name).
|
|
||||||
Str("channel", "Done").
|
|
||||||
Msg("Cancelling keepAlive channel")
|
|
||||||
cancelKeepAlive <- struct{}{}
|
|
||||||
|
|
||||||
log.Trace().
|
|
||||||
Str("handler", "PollNetMapStream").
|
|
||||||
Str("machine", machine.Name).
|
|
||||||
Str("channel", "Done").
|
|
||||||
Msg("Closing update channel")
|
|
||||||
// h.closeUpdateChannel(m)
|
|
||||||
close(updateChan)
|
|
||||||
|
|
||||||
log.Trace().
|
|
||||||
Str("handler", "PollNetMapStream").
|
|
||||||
Str("machine", machine.Name).
|
|
||||||
Str("channel", "Done").
|
|
||||||
Msg("Closing pollData channel")
|
|
||||||
close(pollDataChan)
|
|
||||||
|
|
||||||
log.Trace().
|
|
||||||
Str("handler", "PollNetMapStream").
|
|
||||||
Str("machine", machine.Name).
|
|
||||||
Str("channel", "Done").
|
|
||||||
Msg("Closing keepAliveChan channel")
|
|
||||||
close(keepAliveChan)
|
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Headscale) scheduledPollWorker(
|
func (h *Headscale) scheduledPollWorker(
|
||||||
cancelChan <-chan struct{},
|
ctx context.Context,
|
||||||
updateChan chan<- struct{},
|
updateChan chan<- struct{},
|
||||||
keepAliveChan chan<- []byte,
|
keepAliveChan chan<- []byte,
|
||||||
machineKey key.MachinePublic,
|
machineKey key.MachinePublic,
|
||||||
|
@ -554,7 +557,7 @@ func (h *Headscale) scheduledPollWorker(
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-cancelChan:
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
|
|
||||||
case <-keepAliveTicker.C:
|
case <-keepAliveTicker.C:
|
||||||
|
|
|
@ -18,7 +18,7 @@ message Machine {
|
||||||
string machine_key = 2;
|
string machine_key = 2;
|
||||||
string node_key = 3;
|
string node_key = 3;
|
||||||
string disco_key = 4;
|
string disco_key = 4;
|
||||||
string ip_address = 5;
|
repeated string ip_addresses = 5;
|
||||||
string name = 6;
|
string name = 6;
|
||||||
Namespace namespace = 7;
|
Namespace namespace = 7;
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package headscale
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gopkg.in/check.v1"
|
"gopkg.in/check.v1"
|
||||||
|
"inet.af/netaddr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateNodeNamespace(
|
func CreateNodeNamespace(
|
||||||
|
@ -26,7 +27,7 @@ func CreateNodeNamespace(
|
||||||
NamespaceID: namespace.ID,
|
NamespaceID: namespace.ID,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: ip,
|
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")},
|
||||||
AuthKeyID: uint(pak1.ID),
|
AuthKeyID: uint(pak1.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(machine)
|
app.db.Save(machine)
|
||||||
|
@ -214,7 +215,7 @@ func (s *Suite) TestComplexSharingAcrossNamespaces(c *check.C) {
|
||||||
NamespaceID: namespace1.ID,
|
NamespaceID: namespace1.ID,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: "100.64.0.4",
|
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")},
|
||||||
AuthKeyID: uint(pak4.ID),
|
AuthKeyID: uint(pak4.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(machine4)
|
app.db.Save(machine4)
|
||||||
|
@ -294,7 +295,7 @@ func (s *Suite) TestDeleteSharedMachine(c *check.C) {
|
||||||
NamespaceID: namespace1.ID,
|
NamespaceID: namespace1.ID,
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
IPAddress: "100.64.0.4",
|
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")},
|
||||||
AuthKeyID: uint(pak4n1.ID),
|
AuthKeyID: uint(pak4n1.ID),
|
||||||
}
|
}
|
||||||
app.db.Save(machine4)
|
app.db.Save(machine4)
|
||||||
|
|
77
utils.go
77
utils.go
|
@ -133,61 +133,78 @@ func encode(
|
||||||
return privKey.SealTo(*pubKey, b), nil
|
return privKey.SealTo(*pubKey, b), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Headscale) getAvailableIP() (*netaddr.IP, error) {
|
func (h *Headscale) getAvailableIPs() (ips MachineAddresses, err error) {
|
||||||
ipPrefix := h.cfg.IPPrefix
|
ipPrefixes := h.cfg.IPPrefixes
|
||||||
|
for _, ipPrefix := range ipPrefixes {
|
||||||
|
var ip *netaddr.IP
|
||||||
|
ip, err = h.getAvailableIP(ipPrefix)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ips = append(ips, *ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetIPPrefixEndpoints(na netaddr.IPPrefix) (network, broadcast netaddr.IP) {
|
||||||
|
ipRange := na.Range()
|
||||||
|
network = ipRange.From()
|
||||||
|
broadcast = ipRange.To()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Is this concurrency safe?
|
||||||
|
// What would happen if multiple hosts were to register at the same time?
|
||||||
|
// Would we attempt to assign the same addresses to multiple nodes?
|
||||||
|
func (h *Headscale) getAvailableIP(ipPrefix netaddr.IPPrefix) (*netaddr.IP, error) {
|
||||||
usedIps, err := h.getUsedIPs()
|
usedIps, err := h.getUsedIPs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ipPrefixNetworkAddress, ipPrefixBroadcastAddress := GetIPPrefixEndpoints(ipPrefix)
|
||||||
|
|
||||||
// Get the first IP in our prefix
|
// Get the first IP in our prefix
|
||||||
ip := ipPrefix.IP()
|
ip := ipPrefixNetworkAddress.Next()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if !ipPrefix.Contains(ip) {
|
if !ipPrefix.Contains(ip) {
|
||||||
return nil, errCouldNotAllocateIP
|
return nil, errCouldNotAllocateIP
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some OS (including Linux) does not like when IPs ends with 0 or 255, which
|
switch {
|
||||||
// is typically called network or broadcast. Lets avoid them and continue
|
case ip.Compare(ipPrefixBroadcastAddress) == 0:
|
||||||
// to look when we get one of those traditionally reserved IPs.
|
fallthrough
|
||||||
ipRaw := ip.As4()
|
case containsIPs(usedIps, ip):
|
||||||
if ipRaw[3] == 0 || ipRaw[3] == 255 {
|
fallthrough
|
||||||
|
case ip.IsZero() || ip.IsLoopback():
|
||||||
ip = ip.Next()
|
ip = ip.Next()
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
|
||||||
|
|
||||||
if ip.IsZero() &&
|
default:
|
||||||
ip.IsLoopback() {
|
|
||||||
ip = ip.Next()
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !containsIPs(usedIps, ip) {
|
|
||||||
return &ip, nil
|
return &ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ip = ip.Next()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Headscale) getUsedIPs() ([]netaddr.IP, error) {
|
func (h *Headscale) getUsedIPs() ([]netaddr.IP, error) {
|
||||||
var addresses []string
|
// FIXME: This really deserves a better data model,
|
||||||
h.db.Model(&Machine{}).Pluck("ip_address", &addresses)
|
// but this was quick to get running and it should be enough
|
||||||
|
// to begin experimenting with a dual stack tailnet.
|
||||||
|
var addressesSlices []string
|
||||||
|
h.db.Model(&Machine{}).Pluck("ip_addresses", &addressesSlices)
|
||||||
|
|
||||||
ips := make([]netaddr.IP, len(addresses))
|
ips := make([]netaddr.IP, 0, len(h.cfg.IPPrefixes)*len(addressesSlices))
|
||||||
for index, addr := range addresses {
|
for _, slice := range addressesSlices {
|
||||||
if addr != "" {
|
var a MachineAddresses
|
||||||
ip, err := netaddr.ParseIP(addr)
|
err := a.Scan(slice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse ip from database: %w", err)
|
return nil, fmt.Errorf("failed to read ip from database: %w", err)
|
||||||
}
|
|
||||||
|
|
||||||
ips[index] = ip
|
|
||||||
}
|
}
|
||||||
|
ips = append(ips, a...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ips, nil
|
return ips, nil
|
||||||
|
|
|
@ -6,17 +6,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Suite) TestGetAvailableIp(c *check.C) {
|
func (s *Suite) TestGetAvailableIp(c *check.C) {
|
||||||
ip, err := app.getAvailableIP()
|
ips, err := app.getAvailableIPs()
|
||||||
|
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
expected := netaddr.MustParseIP("10.27.0.1")
|
expected := netaddr.MustParseIP("10.27.0.1")
|
||||||
|
|
||||||
c.Assert(ip.String(), check.Equals, expected.String())
|
c.Assert(len(ips), check.Equals, 1)
|
||||||
|
c.Assert(ips[0].String(), check.Equals, expected.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestGetUsedIps(c *check.C) {
|
func (s *Suite) TestGetUsedIps(c *check.C) {
|
||||||
ip, err := app.getAvailableIP()
|
ips, err := app.getAvailableIPs()
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
namespace, err := app.CreateNamespace("test_ip")
|
namespace, err := app.CreateNamespace("test_ip")
|
||||||
|
@ -38,22 +39,24 @@ func (s *Suite) TestGetUsedIps(c *check.C) {
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
AuthKeyID: uint(pak.ID),
|
AuthKeyID: uint(pak.ID),
|
||||||
IPAddress: ip.String(),
|
IPAddresses: ips,
|
||||||
}
|
}
|
||||||
app.db.Save(&machine)
|
app.db.Save(&machine)
|
||||||
|
|
||||||
ips, err := app.getUsedIPs()
|
usedIps, err := app.getUsedIPs()
|
||||||
|
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
expected := netaddr.MustParseIP("10.27.0.1")
|
expected := netaddr.MustParseIP("10.27.0.1")
|
||||||
|
|
||||||
c.Assert(ips[0], check.Equals, expected)
|
c.Assert(len(usedIps), check.Equals, 1)
|
||||||
|
c.Assert(usedIps[0], check.Equals, expected)
|
||||||
|
|
||||||
machine1, err := app.GetMachineByID(0)
|
machine1, err := app.GetMachineByID(0)
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
c.Assert(machine1.IPAddress, check.Equals, expected.String())
|
c.Assert(len(machine1.IPAddresses), check.Equals, 1)
|
||||||
|
c.Assert(machine1.IPAddresses[0], check.Equals, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestGetMultiIp(c *check.C) {
|
func (s *Suite) TestGetMultiIp(c *check.C) {
|
||||||
|
@ -61,7 +64,7 @@ func (s *Suite) TestGetMultiIp(c *check.C) {
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
for index := 1; index <= 350; index++ {
|
for index := 1; index <= 350; index++ {
|
||||||
ip, err := app.getAvailableIP()
|
ips, err := app.getAvailableIPs()
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
|
pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
|
||||||
|
@ -80,59 +83,64 @@ func (s *Suite) TestGetMultiIp(c *check.C) {
|
||||||
Registered: true,
|
Registered: true,
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
AuthKeyID: uint(pak.ID),
|
AuthKeyID: uint(pak.ID),
|
||||||
IPAddress: ip.String(),
|
IPAddresses: ips,
|
||||||
}
|
}
|
||||||
app.db.Save(&machine)
|
app.db.Save(&machine)
|
||||||
}
|
}
|
||||||
|
|
||||||
ips, err := app.getUsedIPs()
|
usedIps, err := app.getUsedIPs()
|
||||||
|
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
c.Assert(len(ips), check.Equals, 350)
|
c.Assert(len(usedIps), check.Equals, 350)
|
||||||
|
|
||||||
c.Assert(ips[0], check.Equals, netaddr.MustParseIP("10.27.0.1"))
|
c.Assert(usedIps[0], check.Equals, netaddr.MustParseIP("10.27.0.1"))
|
||||||
c.Assert(ips[9], check.Equals, netaddr.MustParseIP("10.27.0.10"))
|
c.Assert(usedIps[9], check.Equals, netaddr.MustParseIP("10.27.0.10"))
|
||||||
c.Assert(ips[300], check.Equals, netaddr.MustParseIP("10.27.1.47"))
|
c.Assert(usedIps[300], check.Equals, netaddr.MustParseIP("10.27.1.45"))
|
||||||
|
|
||||||
// Check that we can read back the IPs
|
// Check that we can read back the IPs
|
||||||
machine1, err := app.GetMachineByID(1)
|
machine1, err := app.GetMachineByID(1)
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
c.Assert(len(machine1.IPAddresses), check.Equals, 1)
|
||||||
c.Assert(
|
c.Assert(
|
||||||
machine1.IPAddress,
|
machine1.IPAddresses[0],
|
||||||
check.Equals,
|
check.Equals,
|
||||||
netaddr.MustParseIP("10.27.0.1").String(),
|
netaddr.MustParseIP("10.27.0.1"),
|
||||||
)
|
)
|
||||||
|
|
||||||
machine50, err := app.GetMachineByID(50)
|
machine50, err := app.GetMachineByID(50)
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
c.Assert(len(machine50.IPAddresses), check.Equals, 1)
|
||||||
c.Assert(
|
c.Assert(
|
||||||
machine50.IPAddress,
|
machine50.IPAddresses[0],
|
||||||
check.Equals,
|
check.Equals,
|
||||||
netaddr.MustParseIP("10.27.0.50").String(),
|
netaddr.MustParseIP("10.27.0.50"),
|
||||||
)
|
)
|
||||||
|
|
||||||
expectedNextIP := netaddr.MustParseIP("10.27.1.97")
|
expectedNextIP := netaddr.MustParseIP("10.27.1.95")
|
||||||
nextIP, err := app.getAvailableIP()
|
nextIP, err := app.getAvailableIPs()
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
c.Assert(nextIP.String(), check.Equals, expectedNextIP.String())
|
c.Assert(len(nextIP), check.Equals, 1)
|
||||||
|
c.Assert(nextIP[0].String(), check.Equals, expectedNextIP.String())
|
||||||
|
|
||||||
// If we call get Available again, we should receive
|
// If we call get Available again, we should receive
|
||||||
// the same IP, as it has not been reserved.
|
// the same IP, as it has not been reserved.
|
||||||
nextIP2, err := app.getAvailableIP()
|
nextIP2, err := app.getAvailableIPs()
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
c.Assert(nextIP2.String(), check.Equals, expectedNextIP.String())
|
c.Assert(len(nextIP2), check.Equals, 1)
|
||||||
|
c.Assert(nextIP2[0].String(), check.Equals, expectedNextIP.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestGetAvailableIpMachineWithoutIP(c *check.C) {
|
func (s *Suite) TestGetAvailableIpMachineWithoutIP(c *check.C) {
|
||||||
ip, err := app.getAvailableIP()
|
ips, err := app.getAvailableIPs()
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
expected := netaddr.MustParseIP("10.27.0.1")
|
expected := netaddr.MustParseIP("10.27.0.1")
|
||||||
|
|
||||||
c.Assert(ip.String(), check.Equals, expected.String())
|
c.Assert(len(ips), check.Equals, 1)
|
||||||
|
c.Assert(ips[0].String(), check.Equals, expected.String())
|
||||||
|
|
||||||
namespace, err := app.CreateNamespace("test_ip")
|
namespace, err := app.CreateNamespace("test_ip")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
@ -156,8 +164,9 @@ func (s *Suite) TestGetAvailableIpMachineWithoutIP(c *check.C) {
|
||||||
}
|
}
|
||||||
app.db.Save(&machine)
|
app.db.Save(&machine)
|
||||||
|
|
||||||
ip2, err := app.getAvailableIP()
|
ips2, err := app.getAvailableIPs()
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
c.Assert(ip2.String(), check.Equals, expected.String())
|
c.Assert(len(ips2), check.Equals, 1)
|
||||||
|
c.Assert(ips2[0].String(), check.Equals, expected.String())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue