mirror of
https://github.com/juanfont/headscale.git
synced 2025-08-20 22:37:28 +00:00
Compare commits
32 Commits
v0.15.0-be
...
mandatory-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cd2914ab3b | ||
![]() |
e85b97143c | ||
![]() |
db9ba17920 | ||
![]() |
d5ce7d7523 | ||
![]() |
2e6687209b | ||
![]() |
b781446e86 | ||
![]() |
1c9b1c0579 | ||
![]() |
ade9552736 | ||
![]() |
68403cb76e | ||
![]() |
537ecb8db0 | ||
![]() |
8f5875efe4 | ||
![]() |
98ac88d5ef | ||
![]() |
d13338a9fb | ||
![]() |
1579ffb66a | ||
![]() |
0bfa5302a7 | ||
![]() |
b8aad5451d | ||
![]() |
61440c42d3 | ||
![]() |
18ee6274e1 | ||
![]() |
0abfbdc18a | ||
![]() |
082a852c5e | ||
![]() |
af081e9fd3 | ||
![]() |
8b5e8b7dfc | ||
![]() |
62d7fae056 | ||
![]() |
dd219d0ff6 | ||
![]() |
397b6fc4bf | ||
![]() |
55d746d3f5 | ||
![]() |
c364c2a382 | ||
![]() |
e540679dbd | ||
![]() |
86b329d8bf | ||
![]() |
7bdd7748e4 | ||
![]() |
0426212348 | ||
![]() |
85cf443ac6 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
|||||||
if: steps.changed-files.outputs.any_changed == 'true'
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: "1.17.7"
|
go-version: "1.18.0"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
if: steps.changed-files.outputs.any_changed == 'true'
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.17.7
|
go-version: 1.18.0
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
|
2
.github/workflows/test-integration.yml
vendored
2
.github/workflows/test-integration.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
|||||||
if: steps.changed-files.outputs.any_changed == 'true'
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: "1.17.7"
|
go-version: "1.18.0"
|
||||||
|
|
||||||
- name: Run Integration tests
|
- name: Run Integration tests
|
||||||
if: steps.changed-files.outputs.any_changed == 'true'
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
|||||||
if: steps.changed-files.outputs.any_changed == 'true'
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: "1.17.7"
|
go-version: "1.18.0"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
if: steps.changed-files.outputs.any_changed == 'true'
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
# Builder image
|
# Builder image
|
||||||
FROM docker.io/golang:1.17.8-bullseye AS build
|
FROM docker.io/golang:1.18.0-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 docker.io/golang:1.17.8-alpine AS build
|
FROM docker.io/golang:1.18.0-alpine 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 docker.io/golang:1.17.8-bullseye AS build
|
FROM docker.io/golang:1.18.0-bullseye AS build
|
||||||
ENV GOPATH /go
|
ENV GOPATH /go
|
||||||
WORKDIR /go/src/headscale
|
WORKDIR /go/src/headscale
|
||||||
|
|
||||||
|
43
README.md
43
README.md
@@ -250,6 +250,13 @@ make build
|
|||||||
<sub style="font-size:14px"><b>Hoàng Đức Hiếu</b></sub>
|
<sub style="font-size:14px"><b>Hoàng Đức Hiếu</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/mevansam>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/403630?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Mevan Samaratunga/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Mevan Samaratunga</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
<a href=https://github.com/dragetd>
|
<a href=https://github.com/dragetd>
|
||||||
<img src=https://avatars.githubusercontent.com/u/3639577?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Michael G./>
|
<img src=https://avatars.githubusercontent.com/u/3639577?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Michael G./>
|
||||||
@@ -271,6 +278,8 @@ make build
|
|||||||
<sub style="font-size:14px"><b>Casey Marshall</b></sub>
|
<sub style="font-size:14px"><b>Casey Marshall</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
<a href=https://github.com/SilverBut>
|
<a href=https://github.com/SilverBut>
|
||||||
<img src=https://avatars.githubusercontent.com/u/6560655?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Silver Bullet/>
|
<img src=https://avatars.githubusercontent.com/u/6560655?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Silver Bullet/>
|
||||||
@@ -278,8 +287,6 @@ make build
|
|||||||
<sub style="font-size:14px"><b>Silver Bullet</b></sub>
|
<sub style="font-size:14px"><b>Silver Bullet</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
<a href=https://github.com/majst01>
|
<a href=https://github.com/majst01>
|
||||||
<img src=https://avatars.githubusercontent.com/u/410110?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Stefan Majer/>
|
<img src=https://avatars.githubusercontent.com/u/410110?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Stefan Majer/>
|
||||||
@@ -315,6 +322,8 @@ make build
|
|||||||
<sub style="font-size:14px"><b>Artem Klevtsov</b></sub>
|
<sub style="font-size:14px"><b>Artem Klevtsov</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
<a href=https://github.com/awoimbee>
|
<a href=https://github.com/awoimbee>
|
||||||
<img src=https://avatars.githubusercontent.com/u/22431493?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Arthur Woimbée/>
|
<img src=https://avatars.githubusercontent.com/u/22431493?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Arthur Woimbée/>
|
||||||
@@ -322,8 +331,6 @@ make build
|
|||||||
<sub style="font-size:14px"><b>Arthur Woimbée</b></sub>
|
<sub style="font-size:14px"><b>Arthur Woimbée</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
<a href=https://github.com/stensonb>
|
<a href=https://github.com/stensonb>
|
||||||
<img src=https://avatars.githubusercontent.com/u/933389?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Bryan Stenson/>
|
<img src=https://avatars.githubusercontent.com/u/933389?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Bryan Stenson/>
|
||||||
@@ -359,6 +366,8 @@ make build
|
|||||||
<sub style="font-size:14px"><b>Jamie Greeff</b></sub>
|
<sub style="font-size:14px"><b>Jamie Greeff</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
<a href=https://github.com/jimt>
|
<a href=https://github.com/jimt>
|
||||||
<img src=https://avatars.githubusercontent.com/u/180326?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Jim Tittsler/>
|
<img src=https://avatars.githubusercontent.com/u/180326?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Jim Tittsler/>
|
||||||
@@ -366,8 +375,6 @@ make build
|
|||||||
<sub style="font-size:14px"><b>Jim Tittsler</b></sub>
|
<sub style="font-size:14px"><b>Jim Tittsler</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
<a href=https://github.com/piec>
|
<a href=https://github.com/piec>
|
||||||
<img src=https://avatars.githubusercontent.com/u/781471?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Pierre Carru/>
|
<img src=https://avatars.githubusercontent.com/u/781471?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Pierre Carru/>
|
||||||
@@ -403,6 +410,8 @@ make build
|
|||||||
<sub style="font-size:14px"><b>Shaanan Cohney</b></sub>
|
<sub style="font-size:14px"><b>Shaanan Cohney</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
<a href=https://github.com/m-tanner-dev0>
|
<a href=https://github.com/m-tanner-dev0>
|
||||||
<img src=https://avatars.githubusercontent.com/u/97977342?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Tanner/>
|
<img src=https://avatars.githubusercontent.com/u/97977342?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Tanner/>
|
||||||
@@ -410,8 +419,6 @@ make build
|
|||||||
<sub style="font-size:14px"><b>Tanner</b></sub>
|
<sub style="font-size:14px"><b>Tanner</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
<a href=https://github.com/Teteros>
|
<a href=https://github.com/Teteros>
|
||||||
<img src=https://avatars.githubusercontent.com/u/5067989?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Teteros/>
|
<img src=https://avatars.githubusercontent.com/u/5067989?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Teteros/>
|
||||||
@@ -440,6 +447,15 @@ make build
|
|||||||
<sub style="font-size:14px"><b>Tjerk Woudsma</b></sub>
|
<sub style="font-size:14px"><b>Tjerk Woudsma</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/y0ngb1n>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/25719408?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Yang Bin/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Yang Bin</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
<a href=https://github.com/zekker6>
|
<a href=https://github.com/zekker6>
|
||||||
<img src=https://avatars.githubusercontent.com/u/1367798?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Zakhar Bessarab/>
|
<img src=https://avatars.githubusercontent.com/u/1367798?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Zakhar Bessarab/>
|
||||||
@@ -454,8 +470,13 @@ make build
|
|||||||
<sub style="font-size:14px"><b>ZiYuan</b></sub>
|
<sub style="font-size:14px"><b>ZiYuan</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
<tr>
|
<a href=https://github.com/bravechamp>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/48980452?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=bravechamp/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>bravechamp</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
<a href=https://github.com/derelm>
|
<a href=https://github.com/derelm>
|
||||||
<img src=https://avatars.githubusercontent.com/u/465155?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=derelm/>
|
<img src=https://avatars.githubusercontent.com/u/465155?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=derelm/>
|
||||||
@@ -477,6 +498,8 @@ make build
|
|||||||
<sub style="font-size:14px"><b>lion24</b></sub>
|
<sub style="font-size:14px"><b>lion24</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
<a href=https://github.com/pernila>
|
<a href=https://github.com/pernila>
|
||||||
<img src=https://avatars.githubusercontent.com/u/12460060?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=pernila/>
|
<img src=https://avatars.githubusercontent.com/u/12460060?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=pernila/>
|
||||||
|
77
api.go
77
api.go
@@ -568,8 +568,13 @@ func (h *Headscale) handleAuthKey(
|
|||||||
Str("func", "handleAuthKey").
|
Str("func", "handleAuthKey").
|
||||||
Str("machine", registerRequest.Hostinfo.Hostname).
|
Str("machine", registerRequest.Hostinfo.Hostname).
|
||||||
Msg("Failed authentication via AuthKey")
|
Msg("Failed authentication via AuthKey")
|
||||||
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
|
|
||||||
Inc()
|
if pak != nil {
|
||||||
|
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
|
||||||
|
Inc()
|
||||||
|
} else {
|
||||||
|
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error").Inc()
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -580,35 +585,51 @@ func (h *Headscale) handleAuthKey(
|
|||||||
Msg("Authentication key was valid, proceeding to acquire IP addresses")
|
Msg("Authentication key was valid, proceeding to acquire IP addresses")
|
||||||
|
|
||||||
nodeKey := NodePublicKeyStripPrefix(registerRequest.NodeKey)
|
nodeKey := NodePublicKeyStripPrefix(registerRequest.NodeKey)
|
||||||
now := time.Now().UTC()
|
|
||||||
|
|
||||||
machineToRegister := Machine{
|
// retrieve machine information if it exist
|
||||||
Name: registerRequest.Hostinfo.Hostname,
|
// The error is not important, because if it does not
|
||||||
NamespaceID: pak.Namespace.ID,
|
// exist, then this is a new machine and we will move
|
||||||
MachineKey: machineKeyStr,
|
// on to registration.
|
||||||
RegisterMethod: RegisterMethodAuthKey,
|
machine, _ := h.GetMachineByMachineKey(machineKey)
|
||||||
Expiry: ®isterRequest.Expiry,
|
if machine != nil {
|
||||||
NodeKey: nodeKey,
|
log.Trace().
|
||||||
LastSeen: &now,
|
|
||||||
AuthKeyID: uint(pak.ID),
|
|
||||||
}
|
|
||||||
|
|
||||||
machine, err := h.RegisterMachine(
|
|
||||||
machineToRegister,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().
|
|
||||||
Caller().
|
Caller().
|
||||||
Err(err).
|
Str("machine", machine.Name).
|
||||||
Msg("could not register machine")
|
Msg("machine already registered, refreshing with new auth key")
|
||||||
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
|
|
||||||
Inc()
|
|
||||||
ctx.String(
|
|
||||||
http.StatusInternalServerError,
|
|
||||||
"could not register machine",
|
|
||||||
)
|
|
||||||
|
|
||||||
return
|
machine.NodeKey = nodeKey
|
||||||
|
machine.AuthKeyID = uint(pak.ID)
|
||||||
|
h.RefreshMachine(machine, registerRequest.Expiry)
|
||||||
|
} else {
|
||||||
|
now := time.Now().UTC()
|
||||||
|
machineToRegister := Machine{
|
||||||
|
Name: registerRequest.Hostinfo.Hostname,
|
||||||
|
NamespaceID: pak.Namespace.ID,
|
||||||
|
MachineKey: machineKeyStr,
|
||||||
|
RegisterMethod: RegisterMethodAuthKey,
|
||||||
|
Expiry: ®isterRequest.Expiry,
|
||||||
|
NodeKey: nodeKey,
|
||||||
|
LastSeen: &now,
|
||||||
|
AuthKeyID: uint(pak.ID),
|
||||||
|
}
|
||||||
|
|
||||||
|
machine, err = h.RegisterMachine(
|
||||||
|
machineToRegister,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().
|
||||||
|
Caller().
|
||||||
|
Err(err).
|
||||||
|
Msg("could not register machine")
|
||||||
|
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
|
||||||
|
Inc()
|
||||||
|
ctx.String(
|
||||||
|
http.StatusInternalServerError,
|
||||||
|
"could not register machine",
|
||||||
|
)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h.UsePreAuthKey(pak)
|
h.UsePreAuthKey(pak)
|
||||||
|
25
app.go
25
app.go
@@ -47,6 +47,14 @@ import (
|
|||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
errSTUNAddressNotSet = Error("STUN address not set")
|
||||||
|
errUnsupportedDatabase = Error("unsupported DB")
|
||||||
|
errUnsupportedLetsEncryptChallengeType = Error(
|
||||||
|
"unknown value for Lets Encrypt challenge type",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AuthPrefix = "Bearer "
|
AuthPrefix = "Bearer "
|
||||||
Postgres = "postgres"
|
Postgres = "postgres"
|
||||||
@@ -58,11 +66,6 @@ const (
|
|||||||
registerCacheExpiration = time.Minute * 15
|
registerCacheExpiration = time.Minute * 15
|
||||||
registerCacheCleanup = time.Minute * 20
|
registerCacheCleanup = time.Minute * 20
|
||||||
|
|
||||||
errUnsupportedDatabase = Error("unsupported DB")
|
|
||||||
errUnsupportedLetsEncryptChallengeType = Error(
|
|
||||||
"unknown value for Lets Encrypt challenge type",
|
|
||||||
)
|
|
||||||
|
|
||||||
DisabledClientAuth = "disabled"
|
DisabledClientAuth = "disabled"
|
||||||
RelaxedClientAuth = "relaxed"
|
RelaxedClientAuth = "relaxed"
|
||||||
EnforcedClientAuth = "enforced"
|
EnforcedClientAuth = "enforced"
|
||||||
@@ -124,7 +127,6 @@ type DERPConfig struct {
|
|||||||
ServerRegionID int
|
ServerRegionID int
|
||||||
ServerRegionCode string
|
ServerRegionCode string
|
||||||
ServerRegionName string
|
ServerRegionName string
|
||||||
STUNEnabled bool
|
|
||||||
STUNAddr string
|
STUNAddr string
|
||||||
URLs []url.URL
|
URLs []url.URL
|
||||||
Paths []string
|
Paths []string
|
||||||
@@ -409,8 +411,6 @@ func (h *Headscale) httpAuthenticationMiddleware(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.AbortWithStatus(http.StatusUnauthorized)
|
|
||||||
|
|
||||||
valid, err := h.ValidateAPIKey(strings.TrimPrefix(authHeader, AuthPrefix))
|
valid, err := h.ValidateAPIKey(strings.TrimPrefix(authHeader, AuthPrefix))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
@@ -502,10 +502,13 @@ func (h *Headscale) Serve() error {
|
|||||||
h.DERPMap = GetDERPMap(h.cfg.DERP)
|
h.DERPMap = GetDERPMap(h.cfg.DERP)
|
||||||
|
|
||||||
if h.cfg.DERP.ServerEnabled {
|
if h.cfg.DERP.ServerEnabled {
|
||||||
h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region
|
// When embedded DERP is enabled we always need a STUN server
|
||||||
if h.cfg.DERP.STUNEnabled {
|
if h.cfg.DERP.STUNAddr == "" {
|
||||||
go h.ServeSTUN()
|
return errSTUNAddressNotSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region
|
||||||
|
go h.ServeSTUN()
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.cfg.DERP.AutoUpdate {
|
if h.cfg.DERP.AutoUpdate {
|
||||||
|
@@ -55,6 +55,9 @@ func LoadConfig(path string) error {
|
|||||||
|
|
||||||
viper.SetDefault("dns_config", nil)
|
viper.SetDefault("dns_config", nil)
|
||||||
|
|
||||||
|
viper.SetDefault("derp.server.enabled", false)
|
||||||
|
viper.SetDefault("derp.server.stun.enabled", true)
|
||||||
|
|
||||||
viper.SetDefault("unix_socket", "/var/run/headscale.sock")
|
viper.SetDefault("unix_socket", "/var/run/headscale.sock")
|
||||||
viper.SetDefault("unix_socket_permission", "0o770")
|
viper.SetDefault("unix_socket_permission", "0o770")
|
||||||
|
|
||||||
@@ -121,8 +124,11 @@ func GetDERPConfig() headscale.DERPConfig {
|
|||||||
serverRegionID := viper.GetInt("derp.server.region_id")
|
serverRegionID := viper.GetInt("derp.server.region_id")
|
||||||
serverRegionCode := viper.GetString("derp.server.region_code")
|
serverRegionCode := viper.GetString("derp.server.region_code")
|
||||||
serverRegionName := viper.GetString("derp.server.region_name")
|
serverRegionName := viper.GetString("derp.server.region_name")
|
||||||
stunEnabled := viper.GetBool("derp.server.stun.enabled")
|
stunAddr := viper.GetString("derp.server.stun_listen_addr")
|
||||||
stunAddr := viper.GetString("derp.server.stun.listen_addr")
|
|
||||||
|
if serverEnabled && stunAddr == "" {
|
||||||
|
log.Fatal().Msg("derp.server.stun_listen_addr must be set if derp.server.enabled is true")
|
||||||
|
}
|
||||||
|
|
||||||
urlStrs := viper.GetStringSlice("derp.urls")
|
urlStrs := viper.GetStringSlice("derp.urls")
|
||||||
|
|
||||||
@@ -149,7 +155,6 @@ func GetDERPConfig() headscale.DERPConfig {
|
|||||||
ServerRegionID: serverRegionID,
|
ServerRegionID: serverRegionID,
|
||||||
ServerRegionCode: serverRegionCode,
|
ServerRegionCode: serverRegionCode,
|
||||||
ServerRegionName: serverRegionName,
|
ServerRegionName: serverRegionName,
|
||||||
STUNEnabled: stunEnabled,
|
|
||||||
STUNAddr: stunAddr,
|
STUNAddr: stunAddr,
|
||||||
URLs: urls,
|
URLs: urls,
|
||||||
Paths: paths,
|
Paths: paths,
|
||||||
|
@@ -69,11 +69,11 @@ derp:
|
|||||||
region_code: "headscale"
|
region_code: "headscale"
|
||||||
region_name: "Headscale Embedded DERP"
|
region_name: "Headscale Embedded DERP"
|
||||||
|
|
||||||
# If enabled, also listens in UDP at the configured address for STUN connections to help on NAT traversal
|
# Listens in UDP at the configured address for STUN connections to help on NAT traversal.
|
||||||
|
# When the embedded DERP server is enabled stun_listen_addr MUST be defined.
|
||||||
|
#
|
||||||
# For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/
|
# For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/
|
||||||
stun:
|
stun_listen_addr: "0.0.0.0:3478"
|
||||||
enabled: false
|
|
||||||
listen_addr: "0.0.0.0:3478"
|
|
||||||
|
|
||||||
# List of externally available DERP maps encoded in JSON
|
# List of externally available DERP maps encoded in JSON
|
||||||
urls:
|
urls:
|
||||||
|
@@ -77,17 +77,15 @@ func (h *Headscale) generateRegionLocalDERP() (tailcfg.DERPRegion, error) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.cfg.DERP.STUNEnabled {
|
_, portSTUNStr, err := net.SplitHostPort(h.cfg.DERP.STUNAddr)
|
||||||
_, portStr, err := net.SplitHostPort(h.cfg.DERP.STUNAddr)
|
if err != nil {
|
||||||
if err != nil {
|
return tailcfg.DERPRegion{}, err
|
||||||
return tailcfg.DERPRegion{}, err
|
|
||||||
}
|
|
||||||
port, err := strconv.Atoi(portStr)
|
|
||||||
if err != nil {
|
|
||||||
return tailcfg.DERPRegion{}, err
|
|
||||||
}
|
|
||||||
localDERPregion.Nodes[0].STUNPort = port
|
|
||||||
}
|
}
|
||||||
|
portSTUN, err := strconv.Atoi(portSTUNStr)
|
||||||
|
if err != nil {
|
||||||
|
return tailcfg.DERPRegion{}, err
|
||||||
|
}
|
||||||
|
localDERPregion.Nodes[0].STUNPort = portSTUN
|
||||||
|
|
||||||
return localDERPregion, nil
|
return localDERPregion, nil
|
||||||
}
|
}
|
||||||
|
362
docs/proposals/001-acls.md
Normal file
362
docs/proposals/001-acls.md
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
# ACLs
|
||||||
|
|
||||||
|
A key component of tailscale is the notion of Tailnet. This notion is hidden
|
||||||
|
but the implications that it have on how to use tailscale are not.
|
||||||
|
|
||||||
|
For tailscale an [tailnet](https://tailscale.com/kb/1136/tailnet/) is the
|
||||||
|
following:
|
||||||
|
|
||||||
|
> For personal users, you are a tailnet of many devices and one person. Each
|
||||||
|
> device gets a private Tailscale IP address in the CGNAT range and every
|
||||||
|
> device can talk directly to every other device, wherever they are on the
|
||||||
|
> internet.
|
||||||
|
>
|
||||||
|
> For businesses and organizations, a tailnet is many devices and many users.
|
||||||
|
> It can be based on your Microsoft Active Directory, your Google Workspace, a
|
||||||
|
> GitHub organization, Okta tenancy, or other identity provider namespace. All
|
||||||
|
> of the devices and users in your tailnet can be seen by the tailnet
|
||||||
|
> administrators in the Tailscale admin console. There you can apply
|
||||||
|
> tailnet-wide configuration, such as ACLs that affect visibility of devices
|
||||||
|
> inside your tailnet, DNS settings, and more.
|
||||||
|
|
||||||
|
## Current implementation and issues
|
||||||
|
|
||||||
|
Currently in headscale, the namespaces are used both as tailnet and users. The
|
||||||
|
issue is that if we want to use the ACL's we can't use both at the same time.
|
||||||
|
|
||||||
|
Tailnet's cannot communicate with each others. So we can't have an ACL that
|
||||||
|
authorize tailnet (namespace) A to talk to tailnet (namespace) B.
|
||||||
|
|
||||||
|
We also can't write ACLs based on the users (namespaces in headscale) since all
|
||||||
|
devices belong to the same user.
|
||||||
|
|
||||||
|
With the current implementation the only ACL that we can user is to associate
|
||||||
|
each headscale IP to a host manually then write the ACLs according to this
|
||||||
|
manual mapping.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"hosts": {
|
||||||
|
"host1": "100.64.0.1",
|
||||||
|
"server": "100.64.0.2"
|
||||||
|
},
|
||||||
|
"acls": [
|
||||||
|
{ "action": "accept", "users": ["host1"], "ports": ["host2:80,443"] }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
While this works, it requires a lot of manual editing on the configuration and
|
||||||
|
to keep track of all devices IP address.
|
||||||
|
|
||||||
|
## Proposition for a next implementation
|
||||||
|
|
||||||
|
In order to ease the use of ACL's we need to split the tailnet and users
|
||||||
|
notion.
|
||||||
|
|
||||||
|
A solution could be to consider a headscale server (in it's entirety) as a
|
||||||
|
tailnet.
|
||||||
|
|
||||||
|
For personal users the default behavior could either allow all communications
|
||||||
|
between all namespaces (like tailscale) or dissallow all communications between
|
||||||
|
namespaces (current behavior).
|
||||||
|
|
||||||
|
For businesses and organisations, viewing a headscale instance a single tailnet
|
||||||
|
would allow users (namespace) to talk to each other with the ACLs. As described
|
||||||
|
in tailscale's documentation [[1]], a server should be tagged and personnal
|
||||||
|
devices should be tied to a user. Translated in headscale's terms each user can
|
||||||
|
have multiple devices and all those devices should be in the same namespace.
|
||||||
|
The servers should be tagged and used as such.
|
||||||
|
|
||||||
|
This implementation would render useless the sharing feature that is currently
|
||||||
|
implemented since an ACL could do the same. Simplifying to only one user
|
||||||
|
interface to do one thing is easier and less confusing for the users.
|
||||||
|
|
||||||
|
To better suit the ACLs in this proposition, it's advised to consider that each
|
||||||
|
namespaces belong to one person. This person can have multiple devices, they
|
||||||
|
will all be considered as the same user in the ACLs. OIDC feature wouldn't need
|
||||||
|
to map people to namespace, just create a namespace if the person isn't
|
||||||
|
registered yet.
|
||||||
|
|
||||||
|
As a sidenote, users would like to write ACLs as YAML. We should offer users
|
||||||
|
the ability to rules in either format (HuJSON or YAML).
|
||||||
|
|
||||||
|
[1]: https://tailscale.com/kb/1068/acl-tags/
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Let's build an example use case for a small business (It may be the place where
|
||||||
|
ACL's are the most useful).
|
||||||
|
|
||||||
|
We have a small company with a boss, an admin, two developper and an intern.
|
||||||
|
|
||||||
|
The boss should have access to all servers but not to the users hosts. Admin
|
||||||
|
should also have access to all hosts except that their permissions should be
|
||||||
|
limited to maintaining the hosts (for example purposes). The developers can do
|
||||||
|
anything they want on dev hosts, but only watch on productions hosts. Intern
|
||||||
|
can only interact with the development servers.
|
||||||
|
|
||||||
|
Each user have at least a device connected to the network and we have some
|
||||||
|
servers.
|
||||||
|
|
||||||
|
- database.prod
|
||||||
|
- database.dev
|
||||||
|
- app-server1.prod
|
||||||
|
- app-server1.dev
|
||||||
|
- billing.internal
|
||||||
|
|
||||||
|
### Current headscale implementation
|
||||||
|
|
||||||
|
Let's create some namespaces
|
||||||
|
|
||||||
|
```bash
|
||||||
|
headscale namespaces create prod
|
||||||
|
headscale namespaces create dev
|
||||||
|
headscale namespaces create internal
|
||||||
|
headscale namespaces create users
|
||||||
|
|
||||||
|
headscale nodes register -n users boss-computer
|
||||||
|
headscale nodes register -n users admin1-computer
|
||||||
|
headscale nodes register -n users dev1-computer
|
||||||
|
headscale nodes register -n users dev1-phone
|
||||||
|
headscale nodes register -n users dev2-computer
|
||||||
|
headscale nodes register -n users intern1-computer
|
||||||
|
|
||||||
|
headscale nodes register -n prod database
|
||||||
|
headscale nodes register -n prod app-server1
|
||||||
|
|
||||||
|
headscale nodes register -n dev database
|
||||||
|
headscale nodes register -n dev app-server1
|
||||||
|
|
||||||
|
headscale nodes register -n internal billing
|
||||||
|
|
||||||
|
headscale nodes list
|
||||||
|
ID | Name | Namespace | IP address
|
||||||
|
1 | boss-computer | users | 100.64.0.1
|
||||||
|
2 | admin1-computer | users | 100.64.0.2
|
||||||
|
3 | dev1-computer | users | 100.64.0.3
|
||||||
|
4 | dev1-phone | users | 100.64.0.4
|
||||||
|
5 | dev2-computer | users | 100.64.0.5
|
||||||
|
6 | intern1-computer | users | 100.64.0.6
|
||||||
|
7 | database | prod | 100.64.0.7
|
||||||
|
8 | app-server1 | prod | 100.64.0.8
|
||||||
|
9 | database | dev | 100.64.0.9
|
||||||
|
10 | app-server1 | dev | 100.64.0.10
|
||||||
|
11 | internal | internal | 100.64.0.11
|
||||||
|
```
|
||||||
|
|
||||||
|
In order to only allow the communications related to our description above we
|
||||||
|
need to add the following ACLs
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"hosts": {
|
||||||
|
"boss-computer": "100.64.0.1",
|
||||||
|
"admin1-computer": "100.64.0.2",
|
||||||
|
"dev1-computer": "100.64.0.3",
|
||||||
|
"dev1-phone": "100.64.0.4",
|
||||||
|
"dev2-computer": "100.64.0.5",
|
||||||
|
"intern1-computer": "100.64.0.6",
|
||||||
|
"prod-app-server1": "100.64.0.8"
|
||||||
|
},
|
||||||
|
"groups": {
|
||||||
|
"group:dev": ["dev1-computer", "dev1-phone", "dev2-computer"],
|
||||||
|
"group:admin": ["admin1-computer"],
|
||||||
|
"group:boss": ["boss-computer"],
|
||||||
|
"group:intern": ["intern1-computer"]
|
||||||
|
},
|
||||||
|
"acls": [
|
||||||
|
// boss have access to all servers but no users hosts
|
||||||
|
{
|
||||||
|
"action": "accept",
|
||||||
|
"users": ["group:boss"],
|
||||||
|
"ports": ["prod:*", "dev:*", "internal:*"]
|
||||||
|
},
|
||||||
|
|
||||||
|
// admin have access to adminstration port (lets only consider port 22 here)
|
||||||
|
{
|
||||||
|
"action": "accept",
|
||||||
|
"users": ["group:admin"],
|
||||||
|
"ports": ["prod:22", "dev:22", "internal:22"]
|
||||||
|
},
|
||||||
|
|
||||||
|
// dev can do anything on dev servers and check access on prod servers
|
||||||
|
{
|
||||||
|
"action": "accept",
|
||||||
|
"users": ["group:dev"],
|
||||||
|
"ports": ["dev:*", "prod-app-server1:80,443"]
|
||||||
|
},
|
||||||
|
|
||||||
|
// interns only have access to port 80 and 443 on dev servers (lame internship)
|
||||||
|
{ "action": "accept", "users": ["group:intern"], "ports": ["dev:80,443"] },
|
||||||
|
|
||||||
|
// users can access their own devices
|
||||||
|
{
|
||||||
|
"action": "accept",
|
||||||
|
"users": ["dev1-computer"],
|
||||||
|
"ports": ["dev1-phone:*"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "accept",
|
||||||
|
"users": ["dev1-phone"],
|
||||||
|
"ports": ["dev1-computer:*"]
|
||||||
|
},
|
||||||
|
|
||||||
|
// internal namespace communications should still be allowed within the namespace
|
||||||
|
{ "action": "accept", "users": ["dev"], "ports": ["dev:*"] },
|
||||||
|
{ "action": "accept", "users": ["prod"], "ports": ["prod:*"] },
|
||||||
|
{ "action": "accept", "users": ["internal"], "ports": ["internal:*"] }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Since communications between namespace isn't possible we also have to share the
|
||||||
|
devices between the namespaces.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
|
||||||
|
// add boss host to prod, dev and internal network
|
||||||
|
headscale nodes share -i 1 -n prod
|
||||||
|
headscale nodes share -i 1 -n dev
|
||||||
|
headscale nodes share -i 1 -n internal
|
||||||
|
|
||||||
|
// add admin computer to prod, dev and internal network
|
||||||
|
headscale nodes share -i 2 -n prod
|
||||||
|
headscale nodes share -i 2 -n dev
|
||||||
|
headscale nodes share -i 2 -n internal
|
||||||
|
|
||||||
|
// add all dev to prod and dev network
|
||||||
|
headscale nodes share -i 3 -n dev
|
||||||
|
headscale nodes share -i 4 -n dev
|
||||||
|
headscale nodes share -i 3 -n prod
|
||||||
|
headscale nodes share -i 4 -n prod
|
||||||
|
headscale nodes share -i 5 -n dev
|
||||||
|
headscale nodes share -i 5 -n prod
|
||||||
|
|
||||||
|
headscale nodes share -i 6 -n dev
|
||||||
|
```
|
||||||
|
|
||||||
|
This fake network have not been tested but it should work. Operating it could
|
||||||
|
be quite tedious if the company grows. Each time a new user join we have to add
|
||||||
|
it to a group, and share it to the correct namespaces. If the user want
|
||||||
|
multiple devices we have to allow communication to each of them one by one. If
|
||||||
|
business conduct a change in the organisations we may have to rewrite all acls
|
||||||
|
and reorganise all namespaces.
|
||||||
|
|
||||||
|
If we add servers in production we should also update the ACLs to allow dev
|
||||||
|
access to certain category of them (only app servers for example).
|
||||||
|
|
||||||
|
### example based on the proposition in this document
|
||||||
|
|
||||||
|
Let's create the namespaces
|
||||||
|
|
||||||
|
```bash
|
||||||
|
headscale namespaces create boss
|
||||||
|
headscale namespaces create admin1
|
||||||
|
headscale namespaces create dev1
|
||||||
|
headscale namespaces create dev2
|
||||||
|
headscale namespaces create intern1
|
||||||
|
```
|
||||||
|
|
||||||
|
We don't need to create namespaces for the servers because the servers will be
|
||||||
|
tagged. When registering the servers we will need to add the flag
|
||||||
|
`--advertised-tags=tag:<tag1>,tag:<tag2>`, and the user (namespace) that is
|
||||||
|
registering the server should be allowed to do it. Since anyone can add tags to
|
||||||
|
a server they can register, the check of the tags is done on headscale server
|
||||||
|
and only valid tags are applied. A tag is valid if the namespace that is
|
||||||
|
registering it is allowed to do it.
|
||||||
|
|
||||||
|
Here are the ACL's to implement the same permissions as above:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
// groups are simpler and only list the namespaces name
|
||||||
|
"groups": {
|
||||||
|
"group:boss": ["boss"],
|
||||||
|
"group:dev": ["dev1", "dev2"],
|
||||||
|
"group:admin": ["admin1"],
|
||||||
|
"group:intern": ["intern1"]
|
||||||
|
},
|
||||||
|
"tagOwners": {
|
||||||
|
// the administrators can add servers in production
|
||||||
|
"tag:prod-databases": ["group:admin"],
|
||||||
|
"tag:prod-app-servers": ["group:admin"],
|
||||||
|
|
||||||
|
// the boss can tag any server as internal
|
||||||
|
"tag:internal": ["group:boss"],
|
||||||
|
|
||||||
|
// dev can add servers for dev purposes as well as admins
|
||||||
|
"tag:dev-databases": ["group:admin", "group:dev"],
|
||||||
|
"tag:dev-app-servers": ["group:admin", "group:dev"]
|
||||||
|
|
||||||
|
// interns cannot add servers
|
||||||
|
},
|
||||||
|
"acls": [
|
||||||
|
// boss have access to all servers
|
||||||
|
{
|
||||||
|
"action": "accept",
|
||||||
|
"users": ["group:boss"],
|
||||||
|
"ports": [
|
||||||
|
"tag:prod-databases:*",
|
||||||
|
"tag:prod-app-servers:*",
|
||||||
|
"tag:internal:*",
|
||||||
|
"tag:dev-databases:*",
|
||||||
|
"tag:dev-app-servers:*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// admin have only access to administrative ports of the servers
|
||||||
|
{
|
||||||
|
"action": "accept",
|
||||||
|
"users": ["group:admin"],
|
||||||
|
"ports": [
|
||||||
|
"tag:prod-databases:22",
|
||||||
|
"tag:prod-app-servers:22",
|
||||||
|
"tag:internal:22",
|
||||||
|
"tag:dev-databases:22",
|
||||||
|
"tag:dev-app-servers:22"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"action": "accept",
|
||||||
|
"users": ["group:dev"],
|
||||||
|
"ports": [
|
||||||
|
"tag:dev-databases:*",
|
||||||
|
"tag:dev-app-servers:*",
|
||||||
|
"tag:prod-app-servers:80,443"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// servers should be able to talk to database. Database should not be able to initiate connections to server
|
||||||
|
{
|
||||||
|
"action": "accept",
|
||||||
|
"users": ["tag:dev-app-servers"],
|
||||||
|
"ports": ["tag:dev-databases:5432"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "accept",
|
||||||
|
"users": ["tag:prod-app-servers"],
|
||||||
|
"ports": ["tag:prod-databases:5432"]
|
||||||
|
},
|
||||||
|
|
||||||
|
// interns have access to dev-app-servers only in reading mode
|
||||||
|
{
|
||||||
|
"action": "accept",
|
||||||
|
"users": ["group:intern"],
|
||||||
|
"ports": ["tag:dev-app-servers:80,443"]
|
||||||
|
},
|
||||||
|
|
||||||
|
// we still have to allow internal namespaces communications since nothing guarantees that each user have their own namespaces. This could be talked over.
|
||||||
|
{ "action": "accept", "users": ["boss"], "ports": ["boss:*"] },
|
||||||
|
{ "action": "accept", "users": ["dev1"], "ports": ["dev1:*"] },
|
||||||
|
{ "action": "accept", "users": ["dev2"], "ports": ["dev2:*"] },
|
||||||
|
{ "action": "accept", "users": ["admin1"], "ports": ["admin1:*"] },
|
||||||
|
{ "action": "accept", "users": ["intern1"], "ports": ["intern1:*"] }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
With this implementation, the sharing step is not necessary. Maintenance cost
|
||||||
|
of the ACL file is lower and less tedious (no need to map hostname and IP's
|
||||||
|
into it).
|
@@ -55,6 +55,7 @@ docker run \
|
|||||||
--rm \
|
--rm \
|
||||||
--volume $(pwd)/config:/etc/headscale/ \
|
--volume $(pwd)/config:/etc/headscale/ \
|
||||||
--publish 127.0.0.1:8080:8080 \
|
--publish 127.0.0.1:8080:8080 \
|
||||||
|
--publish 127.0.0.1:9090:9090 \
|
||||||
headscale/headscale:<VERSION> \
|
headscale/headscale:<VERSION> \
|
||||||
headscale serve
|
headscale serve
|
||||||
|
|
||||||
@@ -80,7 +81,7 @@ docker ps
|
|||||||
Verify `headscale` is available:
|
Verify `headscale` is available:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl http://127.0.0.1:8080/metrics
|
curl http://127.0.0.1:9090/metrics
|
||||||
```
|
```
|
||||||
|
|
||||||
6. Create a namespace ([tailnet](https://tailscale.com/kb/1136/tailnet/)):
|
6. Create a namespace ([tailnet](https://tailscale.com/kb/1136/tailnet/)):
|
||||||
|
@@ -67,7 +67,7 @@ To run `headscale` in the background, please follow the steps in the [SystemD se
|
|||||||
Verify `headscale` is available:
|
Verify `headscale` is available:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl http://127.0.0.1:8080/metrics
|
curl http://127.0.0.1:9090/metrics
|
||||||
```
|
```
|
||||||
|
|
||||||
8. Create a namespace ([tailnet](https://tailscale.com/kb/1136/tailnet/)):
|
8. Create a namespace ([tailnet](https://tailscale.com/kb/1136/tailnet/)):
|
||||||
|
4
go.mod
4
go.mod
@@ -1,9 +1,10 @@
|
|||||||
module github.com/juanfont/headscale
|
module github.com/juanfont/headscale
|
||||||
|
|
||||||
go 1.17
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.2
|
github.com/AlecAivazis/survey/v2 v2.3.2
|
||||||
|
github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029
|
||||||
github.com/coreos/go-oidc/v3 v3.1.0
|
github.com/coreos/go-oidc/v3 v3.1.0
|
||||||
github.com/efekarakus/termcolor v1.0.1
|
github.com/efekarakus/termcolor v1.0.1
|
||||||
github.com/fatih/set v0.2.1
|
github.com/fatih/set v0.2.1
|
||||||
@@ -49,7 +50,6 @@ require (
|
|||||||
github.com/akutz/memconn v0.1.0 // indirect
|
github.com/akutz/memconn v0.1.0 // indirect
|
||||||
github.com/atomicgo/cursor v0.0.1 // indirect
|
github.com/atomicgo/cursor v0.0.1 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029 // indirect
|
|
||||||
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
|
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
github.com/containerd/continuity v0.2.2 // indirect
|
github.com/containerd/continuity v0.2.2 // indirect
|
||||||
|
@@ -24,6 +24,5 @@ derp:
|
|||||||
region_id: 999
|
region_id: 999
|
||||||
region_code: "headscale"
|
region_code: "headscale"
|
||||||
region_name: "Headscale Embedded DERP"
|
region_name: "Headscale Embedded DERP"
|
||||||
stun:
|
|
||||||
enabled: true
|
stun_listen_addr: "0.0.0.0:3478"
|
||||||
listen_addr: "0.0.0.0:3478"
|
|
||||||
|
Reference in New Issue
Block a user