1
0
mirror of https://github.com/juanfont/headscale.git synced 2025-08-01 13:46:49 +02:00

Merge branch 'main' into auth-subnet

This commit is contained in:
Juan Font 2021-10-04 00:05:03 +02:00 committed by GitHub
commit 42b5185669
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 59 additions and 22 deletions

33
app.go
View File

@ -12,6 +12,7 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"golang.org/x/crypto/acme"
"golang.org/x/crypto/acme/autocert" "golang.org/x/crypto/acme/autocert"
"gorm.io/gorm" "gorm.io/gorm"
"inet.af/netaddr" "inet.af/netaddr"
@ -44,6 +45,9 @@ type Config struct {
TLSCertPath string TLSCertPath string
TLSKeyPath string TLSKeyPath string
ACMEURL string
ACMEEmail string
DNSConfig *tailcfg.DNSConfig DNSConfig *tailcfg.DNSConfig
} }
@ -172,16 +176,18 @@ func (h *Headscale) Serve() error {
r.GET("/apple/:platform", h.ApplePlatformConfig) r.GET("/apple/:platform", h.ApplePlatformConfig)
var err error var err error
timeout := 30 * time.Second
go h.watchForKVUpdates(5000) go h.watchForKVUpdates(5000)
go h.expireEphemeralNodes(5000) go h.expireEphemeralNodes(5000)
s := &http.Server{ s := &http.Server{
Addr: h.cfg.Addr, Addr: h.cfg.Addr,
Handler: r, Handler: r,
ReadTimeout: timeout, ReadTimeout: 30 * time.Second,
WriteTimeout: timeout, // Go does not handle timeouts in HTTP very well, and there is
// no good way to handle streaming timeouts, therefore we need to
// keep this at unlimited and be careful to clean up connections
// https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/#aboutstreaming
WriteTimeout: 0,
} }
if h.cfg.TLSLetsEncryptHostname != "" { if h.cfg.TLSLetsEncryptHostname != "" {
@ -193,14 +199,14 @@ func (h *Headscale) Serve() error {
Prompt: autocert.AcceptTOS, Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist(h.cfg.TLSLetsEncryptHostname), HostPolicy: autocert.HostWhitelist(h.cfg.TLSLetsEncryptHostname),
Cache: autocert.DirCache(h.cfg.TLSLetsEncryptCacheDir), Cache: autocert.DirCache(h.cfg.TLSLetsEncryptCacheDir),
Client: &acme.Client{
DirectoryURL: h.cfg.ACMEURL,
},
Email: h.cfg.ACMEEmail,
} }
s := &http.Server{
Addr: h.cfg.Addr, s.TLSConfig = m.TLSConfig()
TLSConfig: m.TLSConfig(),
Handler: r,
ReadTimeout: timeout,
WriteTimeout: timeout,
}
if h.cfg.TLSLetsEncryptChallengeType == "TLS-ALPN-01" { if h.cfg.TLSLetsEncryptChallengeType == "TLS-ALPN-01" {
// Configuration via autocert with TLS-ALPN-01 (https://tools.ietf.org/html/rfc8737) // Configuration via autocert with TLS-ALPN-01 (https://tools.ietf.org/html/rfc8737)
// The RFC requires that the validation is done on port 443; in other words, headscale // The RFC requires that the validation is done on port 443; in other words, headscale
@ -211,7 +217,6 @@ func (h *Headscale) Serve() error {
// port 80 for the certificate validation in addition to the headscale // port 80 for the certificate validation in addition to the headscale
// service, which can be configured to run on any other port. // service, which can be configured to run on any other port.
go func() { go func() {
log.Fatal(). log.Fatal().
Err(http.ListenAndServe(h.cfg.TLSLetsEncryptListen, m.HTTPHandler(http.HandlerFunc(h.redirect)))). Err(http.ListenAndServe(h.cfg.TLSLetsEncryptListen, m.HTTPHandler(http.HandlerFunc(h.redirect)))).
Msg("failed to set up a HTTP server") Msg("failed to set up a HTTP server")

View File

@ -169,6 +169,9 @@ func getHeadscaleApp() (*headscale.Headscale, error) {
TLSCertPath: absPath(viper.GetString("tls_cert_path")), TLSCertPath: absPath(viper.GetString("tls_cert_path")),
TLSKeyPath: absPath(viper.GetString("tls_key_path")), TLSKeyPath: absPath(viper.GetString("tls_key_path")),
ACMEEmail: viper.GetString("acme_email"),
ACMEURL: viper.GetString("acme_url"),
DNSConfig: GetDNSConfig(), DNSConfig: GetDNSConfig(),
} }

View File

@ -10,6 +10,8 @@
"db_name": "headscale", "db_name": "headscale",
"db_user": "foo", "db_user": "foo",
"db_pass": "bar", "db_pass": "bar",
"acme_url": "https://acme-v02.api.letsencrypt.org/directory",
"acme_email": "",
"tls_letsencrypt_hostname": "", "tls_letsencrypt_hostname": "",
"tls_letsencrypt_listen": ":http", "tls_letsencrypt_listen": ":http",
"tls_letsencrypt_cache_dir": ".cache", "tls_letsencrypt_cache_dir": ".cache",

View File

@ -6,6 +6,8 @@
"ephemeral_node_inactivity_timeout": "30m", "ephemeral_node_inactivity_timeout": "30m",
"db_type": "sqlite3", "db_type": "sqlite3",
"db_path": "db.sqlite", "db_path": "db.sqlite",
"acme_url": "https://acme-v02.api.letsencrypt.org/directory",
"acme_email": "",
"tls_letsencrypt_hostname": "", "tls_letsencrypt_hostname": "",
"tls_letsencrypt_listen": ":http", "tls_letsencrypt_listen": ":http",
"tls_letsencrypt_cache_dir": ".cache", "tls_letsencrypt_cache_dir": ".cache",

View File

@ -433,7 +433,7 @@ func (s *IntegrationTestSuite) TestPingAllPeers() {
command := []string{ command := []string{
"tailscale", "ping", "tailscale", "ping",
"--timeout=1s", "--timeout=1s",
"--c=20", "--c=10",
"--until-direct=true", "--until-direct=true",
ip.String(), ip.String(),
} }

View File

@ -308,7 +308,8 @@ func (h *Headscale) notifyChangesToPeers(m *Machine) {
Str("func", "notifyChangesToPeers"). Str("func", "notifyChangesToPeers").
Str("machine", m.Name). Str("machine", m.Name).
Str("peer", p.Name). Str("peer", p.Name).
Msgf("Peer %s does not appear to be polling", p.Name) Msgf("Peer %s does not have an open update client, skipping.", p.Name)
continue
} }
log.Trace(). log.Trace().
Str("func", "notifyChangesToPeers"). Str("func", "notifyChangesToPeers").
@ -379,11 +380,12 @@ func (h *Headscale) sendRequestOnUpdateChannel(m *tailcfg.Node) error {
Msgf("Notified machine %s", m.Name) Msgf("Notified machine %s", m.Name)
} }
} else { } else {
err := errors.New("machine does not have an open update channel")
log.Info(). log.Info().
Str("func", "requestUpdate"). Str("func", "requestUpdate").
Str("machine", m.Name). Str("machine", m.Name).
Msgf("Machine %s does not appear to be polling", m.Name) Msgf("Machine %s does not have an open update channel", m.Name)
return errors.New("machine does not seem to be polling") return err
} }
return nil return nil
} }

31
poll.go
View File

@ -230,6 +230,7 @@ func (h *Headscale) PollNetMapStream(
Str("channel", "pollData"). Str("channel", "pollData").
Err(err). Err(err).
Msg("Cannot write data") Msg("Cannot write data")
return false
} }
log.Trace(). log.Trace().
Str("handler", "PollNetMapStream"). Str("handler", "PollNetMapStream").
@ -237,7 +238,7 @@ func (h *Headscale) PollNetMapStream(
Str("channel", "pollData"). Str("channel", "pollData").
Int("bytes", len(data)). Int("bytes", len(data)).
Msg("Data from pollData channel written successfully") Msg("Data from pollData channel written successfully")
// TODO: Abstract away all the database calls, this can cause race conditions // TODO(kradalby): Abstract away all the database calls, this can cause race conditions
// when an outdated machine object is kept alive, e.g. db is update from // when an outdated machine object is kept alive, e.g. db is update from
// command line, but then overwritten. // command line, but then overwritten.
err = h.UpdateMachine(&m) err = h.UpdateMachine(&m)
@ -258,7 +259,7 @@ func (h *Headscale) PollNetMapStream(
Str("machine", m.Name). Str("machine", m.Name).
Str("channel", "pollData"). Str("channel", "pollData").
Int("bytes", len(data)). Int("bytes", len(data)).
Msg("Machine updated successfully after sending pollData") Msg("Machine entry in database updated successfully after sending pollData")
return true return true
case data := <-keepAliveChan: case data := <-keepAliveChan:
@ -276,6 +277,7 @@ func (h *Headscale) PollNetMapStream(
Str("channel", "keepAlive"). Str("channel", "keepAlive").
Err(err). Err(err).
Msg("Cannot write keep alive message") Msg("Cannot write keep alive message")
return false
} }
log.Trace(). log.Trace().
Str("handler", "PollNetMapStream"). Str("handler", "PollNetMapStream").
@ -283,7 +285,7 @@ func (h *Headscale) PollNetMapStream(
Str("channel", "keepAlive"). Str("channel", "keepAlive").
Int("bytes", len(data)). Int("bytes", len(data)).
Msg("Keep alive sent successfully") Msg("Keep alive sent successfully")
// TODO: Abstract away all the database calls, this can cause race conditions // TODO(kradalby): Abstract away all the database calls, this can cause race conditions
// when an outdated machine object is kept alive, e.g. db is update from // when an outdated machine object is kept alive, e.g. db is update from
// command line, but then overwritten. // command line, but then overwritten.
err = h.UpdateMachine(&m) err = h.UpdateMachine(&m)
@ -336,6 +338,7 @@ func (h *Headscale) PollNetMapStream(
Str("channel", "update"). Str("channel", "update").
Err(err). Err(err).
Msg("Could not write the map response") Msg("Could not write the map response")
return false
} }
log.Trace(). log.Trace().
Str("handler", "PollNetMapStream"). Str("handler", "PollNetMapStream").
@ -347,7 +350,7 @@ func (h *Headscale) PollNetMapStream(
// we sometimes end in a state were the update // we sometimes end in a state were the update
// is not picked up by a client and we use this // is not picked up by a client and we use this
// to determine if we should "force" an update. // to determine if we should "force" an update.
// TODO: Abstract away all the database calls, this can cause race conditions // TODO(kradalby): Abstract away all the database calls, this can cause race conditions
// when an outdated machine object is kept alive, e.g. db is update from // when an outdated machine object is kept alive, e.g. db is update from
// command line, but then overwritten. // command line, but then overwritten.
err = h.UpdateMachine(&m) err = h.UpdateMachine(&m)
@ -393,13 +396,33 @@ func (h *Headscale) PollNetMapStream(
m.LastSeen = &now m.LastSeen = &now
h.db.Save(&m) h.db.Save(&m)
log.Trace().
Str("handler", "PollNetMapStream").
Str("machine", m.Name).
Str("channel", "Done").
Msg("Cancelling keepAlive channel")
cancelKeepAlive <- struct{}{} cancelKeepAlive <- struct{}{}
log.Trace().
Str("handler", "PollNetMapStream").
Str("machine", m.Name).
Str("channel", "Done").
Msg("Closing update channel")
h.closeUpdateChannel(&m) h.closeUpdateChannel(&m)
close(pollDataChan) close(pollDataChan)
log.Trace().
Str("handler", "PollNetMapStream").
Str("machine", m.Name).
Str("channel", "Done").
Msg("Closing pollData channel")
close(keepAliveChan) close(keepAliveChan)
log.Trace().
Str("handler", "PollNetMapStream").
Str("machine", m.Name).
Str("channel", "Done").
Msg("Closing keepAliveChan channel")
return false return false
} }