mirror of
https://github.com/juanfont/headscale.git
synced 2024-11-26 08:53:05 +00:00
Resolve merge conflict
This commit is contained in:
commit
7da3d4ba50
13 changed files with 466 additions and 212 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
/headscale
|
/headscale
|
||||||
config.json
|
config.json
|
||||||
|
config.yaml
|
||||||
*.key
|
*.key
|
||||||
/db.sqlite
|
/db.sqlite
|
||||||
*.sqlite3
|
*.sqlite3
|
||||||
|
|
|
@ -12,6 +12,11 @@ RUN test -e /go/bin/headscale
|
||||||
|
|
||||||
FROM ubuntu:20.04
|
FROM ubuntu:20.04
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y ca-certificates \
|
||||||
|
&& update-ca-certificates \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY --from=build /go/bin/headscale /usr/local/bin/headscale
|
COPY --from=build /go/bin/headscale /usr/local/bin/headscale
|
||||||
ENV TZ UTC
|
ENV TZ UTC
|
||||||
|
|
||||||
|
|
158
README.md
158
README.md
|
@ -61,4 +61,162 @@ Please have a look at the documentation under [`docs/`](docs/).
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/juanfont>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/181059?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Juan Font/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Juan Font</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/kradalby>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/98431?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Kristoffer Dalby/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Kristoffer Dalby</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/cure>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/149135?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Ward Vandewege/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Ward Vandewege</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/ohdearaugustin>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/14001491?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=ohdearaugustin/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>ohdearaugustin</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/qbit>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/68368?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Aaron Bieber/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Aaron Bieber</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/ptman>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/24669?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Paul Tötterman/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Paul Tötterman</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/cmars>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/23741?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Casey Marshall/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Casey Marshall</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<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/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Silver Bullet</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/t56k>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/12165422?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=thomas/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>thomas</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<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/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Arthur Woimbée</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/fkr>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/51063?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Felix Kronlage-Dammers/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Felix Kronlage-Dammers</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/felixonmars>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/1006477?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Felix Yan/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Felix Yan</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/shaananc>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/2287839?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Shaanan Cohney/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Shaanan Cohney</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<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/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Teteros</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/gitter-badger>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/8518239?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=The Gitter Badger/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>The Gitter Badger</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/tianon>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/161631?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Tianon Gravi/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Tianon Gravi</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/woudsma>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/6162978?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Tjerk Woudsma/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Tjerk Woudsma</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<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/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>Zakhar Bessarab</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<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/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>derelm</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/ignoramous>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/852289?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=ignoramous/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>ignoramous</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||||
|
<a href=https://github.com/xpzouying>
|
||||||
|
<img src=https://avatars.githubusercontent.com/u/3946563?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=zy/>
|
||||||
|
<br />
|
||||||
|
<sub style="font-size:14px"><b>zy</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
15
api.go
15
api.go
|
@ -82,7 +82,10 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) {
|
||||||
|
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
var m Machine
|
var m Machine
|
||||||
if result := h.db.Preload("Namespace").First(&m, "machine_key = ?", mKey.HexString()); errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
if result := h.db.Preload("Namespace").First(&m, "machine_key = ?", mKey.HexString()); errors.Is(
|
||||||
|
result.Error,
|
||||||
|
gorm.ErrRecordNotFound,
|
||||||
|
) {
|
||||||
log.Info().Str("machine", req.Hostinfo.Hostname).Msg("New machine")
|
log.Info().Str("machine", req.Hostinfo.Hostname).Msg("New machine")
|
||||||
m = Machine{
|
m = Machine{
|
||||||
Expiry: &req.Expiry,
|
Expiry: &req.Expiry,
|
||||||
|
@ -270,7 +273,7 @@ func (h *Headscale) getMapResponse(mKey wgkey.Key, req tailcfg.MapRequest, m *Ma
|
||||||
DNSConfig: dnsConfig,
|
DNSConfig: dnsConfig,
|
||||||
Domain: h.cfg.BaseDomain,
|
Domain: h.cfg.BaseDomain,
|
||||||
PacketFilter: *h.aclRules,
|
PacketFilter: *h.aclRules,
|
||||||
DERPMap: h.cfg.DerpMap,
|
DERPMap: h.DERPMap,
|
||||||
UserProfiles: profiles,
|
UserProfiles: profiles,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,7 +332,13 @@ func (h *Headscale) getMapKeepAliveResponse(mKey wgkey.Key, req tailcfg.MapReque
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Headscale) handleAuthKey(c *gin.Context, db *gorm.DB, idKey wgkey.Key, req tailcfg.RegisterRequest, m Machine) {
|
func (h *Headscale) handleAuthKey(
|
||||||
|
c *gin.Context,
|
||||||
|
db *gorm.DB,
|
||||||
|
idKey wgkey.Key,
|
||||||
|
req tailcfg.RegisterRequest,
|
||||||
|
m Machine,
|
||||||
|
) {
|
||||||
log.Debug().
|
log.Debug().
|
||||||
Str("func", "handleAuthKey").
|
Str("func", "handleAuthKey").
|
||||||
Str("machine", req.Hostinfo.Hostname).
|
Str("machine", req.Hostinfo.Hostname).
|
||||||
|
|
34
app.go
34
app.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -28,11 +29,12 @@ type Config struct {
|
||||||
ServerURL string
|
ServerURL string
|
||||||
Addr string
|
Addr string
|
||||||
PrivateKeyPath string
|
PrivateKeyPath string
|
||||||
DerpMap *tailcfg.DERPMap
|
|
||||||
EphemeralNodeInactivityTimeout time.Duration
|
EphemeralNodeInactivityTimeout time.Duration
|
||||||
IPPrefix netaddr.IPPrefix
|
IPPrefix netaddr.IPPrefix
|
||||||
BaseDomain string
|
BaseDomain string
|
||||||
|
|
||||||
|
DERP DERPConfig
|
||||||
|
|
||||||
DBtype string
|
DBtype string
|
||||||
DBpath string
|
DBpath string
|
||||||
DBhost string
|
DBhost string
|
||||||
|
@ -55,6 +57,13 @@ type Config struct {
|
||||||
DNSConfig *tailcfg.DNSConfig
|
DNSConfig *tailcfg.DNSConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DERPConfig struct {
|
||||||
|
URLs []url.URL
|
||||||
|
Paths []string
|
||||||
|
AutoUpdate bool
|
||||||
|
UpdateFrequency time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
// Headscale represents the base app of the service
|
// Headscale represents the base app of the service
|
||||||
type Headscale struct {
|
type Headscale struct {
|
||||||
cfg Config
|
cfg Config
|
||||||
|
@ -65,6 +74,8 @@ type Headscale struct {
|
||||||
publicKey *wgkey.Key
|
publicKey *wgkey.Key
|
||||||
privateKey *wgkey.Private
|
privateKey *wgkey.Private
|
||||||
|
|
||||||
|
DERPMap *tailcfg.DERPMap
|
||||||
|
|
||||||
aclPolicy *ACLPolicy
|
aclPolicy *ACLPolicy
|
||||||
aclRules *[]tailcfg.FilterRule
|
aclRules *[]tailcfg.FilterRule
|
||||||
|
|
||||||
|
@ -114,7 +125,7 @@ func NewHeadscale(cfg Config) (*Headscale, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// we might have routes already from Split DNS
|
// we might have routes already from Split DNS
|
||||||
if h.cfg.DNSConfig.Routes == nil {
|
if h.cfg.DNSConfig.Routes == nil {
|
||||||
h.cfg.DNSConfig.Routes = make(map[string][]dnstype.Resolver)
|
h.cfg.DNSConfig.Routes = make(map[string][]dnstype.Resolver)
|
||||||
}
|
}
|
||||||
for _, d := range magicDNSDomains {
|
for _, d := range magicDNSDomains {
|
||||||
|
@ -153,11 +164,15 @@ func (h *Headscale) expireEphemeralNodesWorker() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, m := range *machines {
|
for _, m := range *machines {
|
||||||
if m.AuthKey != nil && m.LastSeen != nil && m.AuthKey.Ephemeral && time.Now().After(m.LastSeen.Add(h.cfg.EphemeralNodeInactivityTimeout)) {
|
if m.AuthKey != nil && m.LastSeen != nil && m.AuthKey.Ephemeral &&
|
||||||
|
time.Now().After(m.LastSeen.Add(h.cfg.EphemeralNodeInactivityTimeout)) {
|
||||||
log.Info().Str("machine", m.Name).Msg("Ephemeral client removed from database")
|
log.Info().Str("machine", m.Name).Msg("Ephemeral client removed from database")
|
||||||
err = h.db.Unscoped().Delete(m).Error
|
err = h.db.Unscoped().Delete(m).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Str("machine", m.Name).Msg("🤮 Cannot delete ephemeral machine from the database")
|
log.Error().
|
||||||
|
Err(err).
|
||||||
|
Str("machine", m.Name).
|
||||||
|
Msg("🤮 Cannot delete ephemeral machine from the database")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,6 +213,15 @@ func (h *Headscale) Serve() error {
|
||||||
go h.watchForKVUpdates(5000)
|
go h.watchForKVUpdates(5000)
|
||||||
go h.expireEphemeralNodes(5000)
|
go h.expireEphemeralNodes(5000)
|
||||||
|
|
||||||
|
// Fetch an initial DERP Map before we start serving
|
||||||
|
h.DERPMap = GetDERPMap(h.cfg.DERP)
|
||||||
|
|
||||||
|
if h.cfg.DERP.AutoUpdate {
|
||||||
|
derpMapCancelChannel := make(chan struct{})
|
||||||
|
defer func() { derpMapCancelChannel <- struct{}{} }()
|
||||||
|
go h.scheduledDERPMapUpdateWorker(derpMapCancelChannel)
|
||||||
|
}
|
||||||
|
|
||||||
s := &http.Server{
|
s := &http.Server{
|
||||||
Addr: h.cfg.Addr,
|
Addr: h.cfg.Addr,
|
||||||
Handler: r,
|
Handler: r,
|
||||||
|
@ -273,7 +297,6 @@ func (h *Headscale) getLastStateChange(namespaces ...string) time.Time {
|
||||||
|
|
||||||
times = append(times, lastChange)
|
times = append(times, lastChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(times, func(i, j int) bool {
|
sort.Slice(times, func(i, j int) bool {
|
||||||
|
@ -284,7 +307,6 @@ func (h *Headscale) getLastStateChange(namespaces ...string) time.Time {
|
||||||
|
|
||||||
if len(times) == 0 {
|
if len(times) == 0 {
|
||||||
return time.Now().UTC()
|
return time.Now().UTC()
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return times[0]
|
return times[0]
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -13,7 +13,6 @@ import (
|
||||||
"github.com/juanfont/headscale"
|
"github.com/juanfont/headscale"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/dnstype"
|
"tailscale.com/types/dnstype"
|
||||||
|
@ -51,21 +50,26 @@ func LoadConfig(path string) error {
|
||||||
|
|
||||||
// Collect any validation errors and return them all at once
|
// Collect any validation errors and return them all at once
|
||||||
var errorText string
|
var errorText string
|
||||||
if (viper.GetString("tls_letsencrypt_hostname") != "") && ((viper.GetString("tls_cert_path") != "") || (viper.GetString("tls_key_path") != "")) {
|
if (viper.GetString("tls_letsencrypt_hostname") != "") &&
|
||||||
|
((viper.GetString("tls_cert_path") != "") || (viper.GetString("tls_key_path") != "")) {
|
||||||
errorText += "Fatal config error: set either tls_letsencrypt_hostname or tls_cert_path/tls_key_path, not both\n"
|
errorText += "Fatal config error: set either tls_letsencrypt_hostname or tls_cert_path/tls_key_path, not both\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viper.GetString("tls_letsencrypt_hostname") != "") && (viper.GetString("tls_letsencrypt_challenge_type") == "TLS-ALPN-01") && (!strings.HasSuffix(viper.GetString("listen_addr"), ":443")) {
|
if (viper.GetString("tls_letsencrypt_hostname") != "") &&
|
||||||
|
(viper.GetString("tls_letsencrypt_challenge_type") == "TLS-ALPN-01") &&
|
||||||
|
(!strings.HasSuffix(viper.GetString("listen_addr"), ":443")) {
|
||||||
// this is only a warning because there could be something sitting in front of headscale that redirects the traffic (e.g. an iptables rule)
|
// this is only a warning because there could be something sitting in front of headscale that redirects the traffic (e.g. an iptables rule)
|
||||||
log.Warn().
|
log.Warn().
|
||||||
Msg("Warning: when using tls_letsencrypt_hostname with TLS-ALPN-01 as challenge type, headscale must be reachable on port 443, i.e. listen_addr should probably end in :443")
|
Msg("Warning: when using tls_letsencrypt_hostname with TLS-ALPN-01 as challenge type, headscale must be reachable on port 443, i.e. listen_addr should probably end in :443")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viper.GetString("tls_letsencrypt_challenge_type") != "HTTP-01") && (viper.GetString("tls_letsencrypt_challenge_type") != "TLS-ALPN-01") {
|
if (viper.GetString("tls_letsencrypt_challenge_type") != "HTTP-01") &&
|
||||||
|
(viper.GetString("tls_letsencrypt_challenge_type") != "TLS-ALPN-01") {
|
||||||
errorText += "Fatal config error: the only supported values for tls_letsencrypt_challenge_type are HTTP-01 and TLS-ALPN-01\n"
|
errorText += "Fatal config error: the only supported values for tls_letsencrypt_challenge_type are HTTP-01 and TLS-ALPN-01\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(viper.GetString("server_url"), "http://") && !strings.HasPrefix(viper.GetString("server_url"), "https://") {
|
if !strings.HasPrefix(viper.GetString("server_url"), "http://") &&
|
||||||
|
!strings.HasPrefix(viper.GetString("server_url"), "https://") {
|
||||||
errorText += "Fatal config error: server_url must start with https:// or http://\n"
|
errorText += "Fatal config error: server_url must start with https:// or http://\n"
|
||||||
}
|
}
|
||||||
if errorText != "" {
|
if errorText != "" {
|
||||||
|
@ -73,7 +77,35 @@ func LoadConfig(path string) error {
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDERPConfig() headscale.DERPConfig {
|
||||||
|
urlStrs := viper.GetStringSlice("derp.urls")
|
||||||
|
|
||||||
|
urls := make([]url.URL, len(urlStrs))
|
||||||
|
for index, urlStr := range urlStrs {
|
||||||
|
urlAddr, err := url.Parse(urlStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().
|
||||||
|
Str("url", urlStr).
|
||||||
|
Err(err).
|
||||||
|
Msg("Failed to parse url, ignoring...")
|
||||||
|
}
|
||||||
|
|
||||||
|
urls[index] = *urlAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
paths := viper.GetStringSlice("derp.paths")
|
||||||
|
|
||||||
|
autoUpdate := viper.GetBool("derp.auto_update_enabled")
|
||||||
|
updateFrequency := viper.GetDuration("derp.update_frequency")
|
||||||
|
|
||||||
|
return headscale.DERPConfig{
|
||||||
|
URLs: urls,
|
||||||
|
Paths: paths,
|
||||||
|
AutoUpdate: autoUpdate,
|
||||||
|
UpdateFrequency: updateFrequency,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDNSConfig() (*tailcfg.DNSConfig, string) {
|
func GetDNSConfig() (*tailcfg.DNSConfig, string) {
|
||||||
|
@ -171,33 +203,30 @@ func absPath(path string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHeadscaleApp() (*headscale.Headscale, error) {
|
func getHeadscaleApp() (*headscale.Headscale, error) {
|
||||||
derpPath := absPath(viper.GetString("derp_map_path"))
|
|
||||||
derpMap, err := loadDerpMap(derpPath)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().
|
|
||||||
Str("path", derpPath).
|
|
||||||
Err(err).
|
|
||||||
Msg("Could not load DERP servers map file")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Minimum inactivity time out is keepalive timeout (60s) plus a few seconds
|
// Minimum inactivity time out is keepalive timeout (60s) plus a few seconds
|
||||||
// to avoid races
|
// to avoid races
|
||||||
minInactivityTimeout, _ := time.ParseDuration("65s")
|
minInactivityTimeout, _ := time.ParseDuration("65s")
|
||||||
if viper.GetDuration("ephemeral_node_inactivity_timeout") <= minInactivityTimeout {
|
if viper.GetDuration("ephemeral_node_inactivity_timeout") <= minInactivityTimeout {
|
||||||
err = fmt.Errorf("ephemeral_node_inactivity_timeout (%s) is set too low, must be more than %s\n", viper.GetString("ephemeral_node_inactivity_timeout"), minInactivityTimeout)
|
err := fmt.Errorf(
|
||||||
|
"ephemeral_node_inactivity_timeout (%s) is set too low, must be more than %s\n",
|
||||||
|
viper.GetString("ephemeral_node_inactivity_timeout"),
|
||||||
|
minInactivityTimeout,
|
||||||
|
)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsConfig, baseDomain := GetDNSConfig()
|
dnsConfig, baseDomain := GetDNSConfig()
|
||||||
|
derpConfig := GetDERPConfig()
|
||||||
|
|
||||||
cfg := headscale.Config{
|
cfg := headscale.Config{
|
||||||
ServerURL: viper.GetString("server_url"),
|
ServerURL: viper.GetString("server_url"),
|
||||||
Addr: viper.GetString("listen_addr"),
|
Addr: viper.GetString("listen_addr"),
|
||||||
PrivateKeyPath: absPath(viper.GetString("private_key_path")),
|
PrivateKeyPath: absPath(viper.GetString("private_key_path")),
|
||||||
DerpMap: derpMap,
|
|
||||||
IPPrefix: netaddr.MustParseIPPrefix(viper.GetString("ip_prefix")),
|
IPPrefix: netaddr.MustParseIPPrefix(viper.GetString("ip_prefix")),
|
||||||
BaseDomain: baseDomain,
|
BaseDomain: baseDomain,
|
||||||
|
|
||||||
|
DERP: derpConfig,
|
||||||
|
|
||||||
EphemeralNodeInactivityTimeout: viper.GetDuration("ephemeral_node_inactivity_timeout"),
|
EphemeralNodeInactivityTimeout: viper.GetDuration("ephemeral_node_inactivity_timeout"),
|
||||||
|
|
||||||
DBtype: viper.GetString("db_type"),
|
DBtype: viper.GetString("db_type"),
|
||||||
|
@ -243,21 +272,6 @@ func getHeadscaleApp() (*headscale.Headscale, error) {
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadDerpMap(path string) (*tailcfg.DERPMap, error) {
|
|
||||||
derpFile, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer derpFile.Close()
|
|
||||||
var derpMap tailcfg.DERPMap
|
|
||||||
b, err := io.ReadAll(derpFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = yaml.Unmarshal(b, &derpMap)
|
|
||||||
return &derpMap, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func JsonOutput(result interface{}, errResult error, outputFormat string) {
|
func JsonOutput(result interface{}, errResult error, outputFormat string) {
|
||||||
var j []byte
|
var j []byte
|
||||||
var err error
|
var err error
|
||||||
|
|
|
@ -27,7 +27,7 @@ func (s *Suite) SetUpSuite(c *check.C) {
|
||||||
func (s *Suite) TearDownSuite(c *check.C) {
|
func (s *Suite) TearDownSuite(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Suite) TestSqliteConfigLoading(c *check.C) {
|
func (*Suite) TestConfigLoading(c *check.C) {
|
||||||
tmpDir, err := ioutil.TempDir("", "headscale")
|
tmpDir, err := ioutil.TempDir("", "headscale")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fatal(err)
|
c.Fatal(err)
|
||||||
|
@ -52,7 +52,7 @@ func (*Suite) TestSqliteConfigLoading(c *check.C) {
|
||||||
// Test that config file was interpreted correctly
|
// Test that config file was interpreted correctly
|
||||||
c.Assert(viper.GetString("server_url"), check.Equals, "http://127.0.0.1:8080")
|
c.Assert(viper.GetString("server_url"), check.Equals, "http://127.0.0.1:8080")
|
||||||
c.Assert(viper.GetString("listen_addr"), check.Equals, "0.0.0.0:8080")
|
c.Assert(viper.GetString("listen_addr"), check.Equals, "0.0.0.0:8080")
|
||||||
c.Assert(viper.GetString("derp_map_path"), check.Equals, "derp.yaml")
|
c.Assert(viper.GetStringSlice("derp.paths")[0], check.Equals, "derp-example.yaml")
|
||||||
c.Assert(viper.GetString("db_type"), check.Equals, "sqlite3")
|
c.Assert(viper.GetString("db_type"), check.Equals, "sqlite3")
|
||||||
c.Assert(viper.GetString("db_path"), check.Equals, "db.sqlite")
|
c.Assert(viper.GetString("db_path"), check.Equals, "db.sqlite")
|
||||||
c.Assert(viper.GetString("tls_letsencrypt_hostname"), check.Equals, "")
|
c.Assert(viper.GetString("tls_letsencrypt_hostname"), check.Equals, "")
|
||||||
|
|
15
derp-example.yaml
Normal file
15
derp-example.yaml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# If you plan to somehow use headscale, please deploy your own DERP infra: https://tailscale.com/kb/1118/custom-derp-servers/
|
||||||
|
regions:
|
||||||
|
900:
|
||||||
|
regionid: 900
|
||||||
|
regioncode: custom
|
||||||
|
regionname: My Region
|
||||||
|
nodes:
|
||||||
|
- name: 1a
|
||||||
|
regionid: 1
|
||||||
|
hostname: myderp.mydomain.no
|
||||||
|
ipv4: 123.123.123.123
|
||||||
|
ipv6: "2604:a880:400:d1::828:b001"
|
||||||
|
stunport: 0
|
||||||
|
stunonly: false
|
||||||
|
derptestport: 0
|
152
derp.go
Normal file
152
derp.go
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
package headscale
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
|
"tailscale.com/tailcfg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadDERPMapFromPath(path string) (*tailcfg.DERPMap, error) {
|
||||||
|
derpFile, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer derpFile.Close()
|
||||||
|
var derpMap tailcfg.DERPMap
|
||||||
|
b, err := io.ReadAll(derpFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = yaml.Unmarshal(b, &derpMap)
|
||||||
|
return &derpMap, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadDERPMapFromURL(addr url.URL) (*tailcfg.DERPMap, error) {
|
||||||
|
client := http.Client{
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
}
|
||||||
|
resp, err := client.Get(addr.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var derpMap tailcfg.DERPMap
|
||||||
|
err = json.Unmarshal(body, &derpMap)
|
||||||
|
return &derpMap, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeDERPMaps naively merges a list of DERPMaps into a single
|
||||||
|
// DERPMap, it will _only_ look at the Regions, an integer.
|
||||||
|
// If a region exists in two of the given DERPMaps, the region
|
||||||
|
// form the _last_ DERPMap will be preserved.
|
||||||
|
// An empty DERPMap list will result in a DERPMap with no regions
|
||||||
|
func mergeDERPMaps(derpMaps []*tailcfg.DERPMap) *tailcfg.DERPMap {
|
||||||
|
result := tailcfg.DERPMap{
|
||||||
|
OmitDefaultRegions: false,
|
||||||
|
Regions: map[int]*tailcfg.DERPRegion{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, derpMap := range derpMaps {
|
||||||
|
for id, region := range derpMap.Regions {
|
||||||
|
result.Regions[id] = region
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDERPMap(cfg DERPConfig) *tailcfg.DERPMap {
|
||||||
|
derpMaps := make([]*tailcfg.DERPMap, 0)
|
||||||
|
|
||||||
|
for _, path := range cfg.Paths {
|
||||||
|
log.Debug().
|
||||||
|
Str("func", "GetDERPMap").
|
||||||
|
Str("path", path).
|
||||||
|
Msg("Loading DERPMap from path")
|
||||||
|
derpMap, err := loadDERPMapFromPath(path)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().
|
||||||
|
Str("func", "GetDERPMap").
|
||||||
|
Str("path", path).
|
||||||
|
Err(err).
|
||||||
|
Msg("Could not load DERP map from path")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
derpMaps = append(derpMaps, derpMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range cfg.URLs {
|
||||||
|
derpMap, err := loadDERPMapFromURL(addr)
|
||||||
|
log.Debug().
|
||||||
|
Str("func", "GetDERPMap").
|
||||||
|
Str("url", addr.String()).
|
||||||
|
Msg("Loading DERPMap from path")
|
||||||
|
if err != nil {
|
||||||
|
log.Error().
|
||||||
|
Str("func", "GetDERPMap").
|
||||||
|
Str("url", addr.String()).
|
||||||
|
Err(err).
|
||||||
|
Msg("Could not load DERP map from path")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
derpMaps = append(derpMaps, derpMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
derpMap := mergeDERPMaps(derpMaps)
|
||||||
|
|
||||||
|
log.Trace().Interface("derpMap", derpMap).Msg("DERPMap loaded")
|
||||||
|
|
||||||
|
if len(derpMap.Regions) == 0 {
|
||||||
|
log.Warn().
|
||||||
|
Msg("DERP map is empty, not a single DERP map datasource was loaded correctly or contained a region")
|
||||||
|
}
|
||||||
|
|
||||||
|
return derpMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Headscale) scheduledDERPMapUpdateWorker(cancelChan <-chan struct{}) {
|
||||||
|
log.Info().
|
||||||
|
Dur("frequency", h.cfg.DERP.UpdateFrequency).
|
||||||
|
Msg("Setting up a DERPMap update worker")
|
||||||
|
ticker := time.NewTicker(h.cfg.DERP.UpdateFrequency)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-cancelChan:
|
||||||
|
return
|
||||||
|
|
||||||
|
case <-ticker.C:
|
||||||
|
log.Info().Msg("Fetching DERPMap updates")
|
||||||
|
h.DERPMap = GetDERPMap(h.cfg.DERP)
|
||||||
|
|
||||||
|
namespaces, err := h.ListNamespaces()
|
||||||
|
if err != nil {
|
||||||
|
log.Error().
|
||||||
|
Err(err).
|
||||||
|
Msg("Failed to fetch namespaces")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, namespace := range *namespaces {
|
||||||
|
h.setLastStateChangeToNow(namespace.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
146
derp.yaml
146
derp.yaml
|
@ -1,146 +0,0 @@
|
||||||
# This file contains some of the official Tailscale DERP servers,
|
|
||||||
# shamelessly taken from https://github.com/tailscale/tailscale/blob/main/net/dnsfallback/dns-fallback-servers.json
|
|
||||||
#
|
|
||||||
# If you plan to somehow use headscale, please deploy your own DERP infra: https://tailscale.com/kb/1118/custom-derp-servers/
|
|
||||||
regions:
|
|
||||||
1:
|
|
||||||
regionid: 1
|
|
||||||
regioncode: nyc
|
|
||||||
regionname: New York City
|
|
||||||
nodes:
|
|
||||||
- name: 1a
|
|
||||||
regionid: 1
|
|
||||||
hostname: derp1.tailscale.com
|
|
||||||
ipv4: 159.89.225.99
|
|
||||||
ipv6: "2604:a880:400:d1::828:b001"
|
|
||||||
stunport: 0
|
|
||||||
stunonly: false
|
|
||||||
derptestport: 0
|
|
||||||
- name: 1b
|
|
||||||
regionid: 1
|
|
||||||
hostname: derp1b.tailscale.com
|
|
||||||
ipv4: 45.55.35.93
|
|
||||||
ipv6: "2604:a880:800:a1::f:2001"
|
|
||||||
stunport: 0
|
|
||||||
stunonly: false
|
|
||||||
derptestport: 0
|
|
||||||
2:
|
|
||||||
regionid: 2
|
|
||||||
regioncode: sfo
|
|
||||||
regionname: San Francisco
|
|
||||||
nodes:
|
|
||||||
- name: 2a
|
|
||||||
regionid: 2
|
|
||||||
hostname: derp2.tailscale.com
|
|
||||||
ipv4: 167.172.206.31
|
|
||||||
ipv6: "2604:a880:2:d1::c5:7001"
|
|
||||||
stunport: 0
|
|
||||||
stunonly: false
|
|
||||||
derptestport: 0
|
|
||||||
- name: 2b
|
|
||||||
regionid: 2
|
|
||||||
hostname: derp2b.tailscale.com
|
|
||||||
ipv4: 64.227.106.23
|
|
||||||
ipv6: "2604:a880:4:1d0::29:9000"
|
|
||||||
stunport: 0
|
|
||||||
stunonly: false
|
|
||||||
derptestport: 0
|
|
||||||
3:
|
|
||||||
regionid: 3
|
|
||||||
regioncode: sin
|
|
||||||
regionname: Singapore
|
|
||||||
nodes:
|
|
||||||
- name: 3a
|
|
||||||
regionid: 3
|
|
||||||
hostname: derp3.tailscale.com
|
|
||||||
ipv4: 68.183.179.66
|
|
||||||
ipv6: "2400:6180:0:d1::67d:8001"
|
|
||||||
stunport: 0
|
|
||||||
stunonly: false
|
|
||||||
derptestport: 0
|
|
||||||
4:
|
|
||||||
regionid: 4
|
|
||||||
regioncode: fra
|
|
||||||
regionname: Frankfurt
|
|
||||||
nodes:
|
|
||||||
- name: 4a
|
|
||||||
regionid: 4
|
|
||||||
hostname: derp4.tailscale.com
|
|
||||||
ipv4: 167.172.182.26
|
|
||||||
ipv6: "2a03:b0c0:3:e0::36e:900"
|
|
||||||
stunport: 0
|
|
||||||
stunonly: false
|
|
||||||
derptestport: 0
|
|
||||||
- name: 4b
|
|
||||||
regionid: 4
|
|
||||||
hostname: derp4b.tailscale.com
|
|
||||||
ipv4: 157.230.25.0
|
|
||||||
ipv6: "2a03:b0c0:3:e0::58f:3001"
|
|
||||||
stunport: 0
|
|
||||||
stunonly: false
|
|
||||||
derptestport: 0
|
|
||||||
5:
|
|
||||||
regionid: 5
|
|
||||||
regioncode: syd
|
|
||||||
regionname: Sydney
|
|
||||||
nodes:
|
|
||||||
- name: 5a
|
|
||||||
regionid: 5
|
|
||||||
hostname: derp5.tailscale.com
|
|
||||||
ipv4: 103.43.75.49
|
|
||||||
ipv6: "2001:19f0:5801:10b7:5400:2ff:feaa:284c"
|
|
||||||
stunport: 0
|
|
||||||
stunonly: false
|
|
||||||
derptestport: 0
|
|
||||||
6:
|
|
||||||
regionid: 6
|
|
||||||
regioncode: blr
|
|
||||||
regionname: Bangalore
|
|
||||||
nodes:
|
|
||||||
- name: 6a
|
|
||||||
regionid: 6
|
|
||||||
hostname: derp6.tailscale.com
|
|
||||||
ipv4: 68.183.90.120
|
|
||||||
ipv6: "2400:6180:100:d0::982:d001"
|
|
||||||
stunport: 0
|
|
||||||
stunonly: false
|
|
||||||
derptestport: 0
|
|
||||||
7:
|
|
||||||
regionid: 7
|
|
||||||
regioncode: tok
|
|
||||||
regionname: Tokyo
|
|
||||||
nodes:
|
|
||||||
- name: 7a
|
|
||||||
regionid: 7
|
|
||||||
hostname: derp7.tailscale.com
|
|
||||||
ipv4: 167.179.89.145
|
|
||||||
ipv6: "2401:c080:1000:467f:5400:2ff:feee:22aa"
|
|
||||||
stunport: 0
|
|
||||||
stunonly: false
|
|
||||||
derptestport: 0
|
|
||||||
8:
|
|
||||||
regionid: 8
|
|
||||||
regioncode: lhr
|
|
||||||
regionname: London
|
|
||||||
nodes:
|
|
||||||
- name: 8a
|
|
||||||
regionid: 8
|
|
||||||
hostname: derp8.tailscale.com
|
|
||||||
ipv4: 167.71.139.179
|
|
||||||
ipv6: "2a03:b0c0:1:e0::3cc:e001"
|
|
||||||
stunport: 0
|
|
||||||
stunonly: false
|
|
||||||
derptestport: 0
|
|
||||||
9:
|
|
||||||
regionid: 9
|
|
||||||
regioncode: sao
|
|
||||||
regionname: São Paulo
|
|
||||||
nodes:
|
|
||||||
- name: 9a
|
|
||||||
regionid: 9
|
|
||||||
hostname: derp9.tailscale.com
|
|
||||||
ipv4: 207.148.3.137
|
|
||||||
ipv6: "2001:19f0:6401:1d9c:5400:2ff:feef:bb82"
|
|
||||||
stunport: 0
|
|
||||||
stunonly: false
|
|
||||||
derptestport: 0
|
|
|
@ -230,7 +230,6 @@ func (s *IntegrationTestSuite) SetupSuite() {
|
||||||
Name: "headscale",
|
Name: "headscale",
|
||||||
Mounts: []string{
|
Mounts: []string{
|
||||||
fmt.Sprintf("%s/integration_test/etc:/etc/headscale", currentPath),
|
fmt.Sprintf("%s/integration_test/etc:/etc/headscale", currentPath),
|
||||||
fmt.Sprintf("%s/derp.yaml:/etc/headscale/derp.yaml", currentPath),
|
|
||||||
},
|
},
|
||||||
Networks: []*dockertest.Network{&network},
|
Networks: []*dockertest.Network{&network},
|
||||||
Cmd: []string{"headscale", "serve"},
|
Cmd: []string{"headscale", "serve"},
|
||||||
|
@ -289,7 +288,16 @@ func (s *IntegrationTestSuite) SetupSuite() {
|
||||||
fmt.Printf("Creating pre auth key for %s\n", namespace)
|
fmt.Printf("Creating pre auth key for %s\n", namespace)
|
||||||
authKey, err := executeCommand(
|
authKey, err := executeCommand(
|
||||||
&headscale,
|
&headscale,
|
||||||
[]string{"headscale", "--namespace", namespace, "preauthkeys", "create", "--reusable", "--expiration", "24h"},
|
[]string{
|
||||||
|
"headscale",
|
||||||
|
"--namespace",
|
||||||
|
namespace,
|
||||||
|
"preauthkeys",
|
||||||
|
"create",
|
||||||
|
"--reusable",
|
||||||
|
"--expiration",
|
||||||
|
"24h",
|
||||||
|
},
|
||||||
[]string{},
|
[]string{},
|
||||||
)
|
)
|
||||||
assert.Nil(s.T(), err)
|
assert.Nil(s.T(), err)
|
||||||
|
@ -298,7 +306,16 @@ func (s *IntegrationTestSuite) SetupSuite() {
|
||||||
|
|
||||||
fmt.Printf("Joining tailscale containers to headscale at %s\n", headscaleEndpoint)
|
fmt.Printf("Joining tailscale containers to headscale at %s\n", headscaleEndpoint)
|
||||||
for hostname, tailscale := range scales.tailscales {
|
for hostname, tailscale := range scales.tailscales {
|
||||||
command := []string{"tailscale", "up", "-login-server", headscaleEndpoint, "--authkey", strings.TrimSuffix(authKey, "\n"), "--hostname", hostname}
|
command := []string{
|
||||||
|
"tailscale",
|
||||||
|
"up",
|
||||||
|
"-login-server",
|
||||||
|
headscaleEndpoint,
|
||||||
|
"--authkey",
|
||||||
|
strings.TrimSuffix(authKey, "\n"),
|
||||||
|
"--hostname",
|
||||||
|
hostname,
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println("Join command:", command)
|
fmt.Println("Join command:", command)
|
||||||
fmt.Printf("Running join command for %s\n", hostname)
|
fmt.Printf("Running join command for %s\n", hostname)
|
||||||
|
@ -661,7 +678,13 @@ func (s *IntegrationTestSuite) TestMagicDNS() {
|
||||||
fmt.Sprintf("%s.%s.headscale.net", peername, namespace),
|
fmt.Sprintf("%s.%s.headscale.net", peername, namespace),
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Pinging using Hostname (magicdns) from %s (%s) to %s (%s)\n", hostname, ips[hostname], peername, ip)
|
fmt.Printf(
|
||||||
|
"Pinging using Hostname (magicdns) from %s (%s) to %s (%s)\n",
|
||||||
|
hostname,
|
||||||
|
ips[hostname],
|
||||||
|
peername,
|
||||||
|
ip,
|
||||||
|
)
|
||||||
result, err := executeCommand(
|
result, err := executeCommand(
|
||||||
&tailscale,
|
&tailscale,
|
||||||
command,
|
command,
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
{
|
|
||||||
"server_url": "http://headscale:8080",
|
|
||||||
"listen_addr": "0.0.0.0:8080",
|
|
||||||
"private_key_path": "private.key",
|
|
||||||
"derp_map_path": "derp.yaml",
|
|
||||||
"ephemeral_node_inactivity_timeout": "30m",
|
|
||||||
"db_type": "sqlite3",
|
|
||||||
"db_path": "/tmp/integration_test_db.sqlite3",
|
|
||||||
"acl_policy_path": "",
|
|
||||||
"log_level": "trace",
|
|
||||||
"dns_config": {
|
|
||||||
"nameservers": [
|
|
||||||
"1.1.1.1"
|
|
||||||
],
|
|
||||||
"domains": [],
|
|
||||||
"magic_dns": true,
|
|
||||||
"base_domain": "headscale.net"
|
|
||||||
}
|
|
||||||
}
|
|
20
integration_test/etc/config.yaml
Normal file
20
integration_test/etc/config.yaml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
log_level: trace
|
||||||
|
acl_policy_path: ""
|
||||||
|
db_type: sqlite3
|
||||||
|
ephemeral_node_inactivity_timeout: 30m
|
||||||
|
dns_config:
|
||||||
|
base_domain: headscale.net
|
||||||
|
magic_dns: true
|
||||||
|
domains: []
|
||||||
|
nameservers:
|
||||||
|
- 1.1.1.1
|
||||||
|
db_path: /tmp/integration_test_db.sqlite3
|
||||||
|
private_key_path: private.key
|
||||||
|
listen_addr: 0.0.0.0:8080
|
||||||
|
server_url: http://headscale:8080
|
||||||
|
|
||||||
|
derp:
|
||||||
|
urls:
|
||||||
|
- https://controlplane.tailscale.com/derpmap/default
|
||||||
|
auto_update_enabled: false
|
||||||
|
update_frequency: 1m
|
Loading…
Reference in a new issue