Set timeout, add lastupdate field

This commit makes two reasonably major changes:

Set a default timeout for the go HTTP server (which gin uses), which
allows us to actually have broken long poll sessions fail so we can have
the client re-establish them.
The current 10s number is chosen randomly and we need more testing to
ensure that the feature work as intended.

The second is adding a last updated field to keep track of the last time
we had an update that needs to be propagated to all of our
clients/nodes. This will be used to keep track of our machines and if
they are up to date or need us to push an update.
This commit is contained in:
Kristoffer Dalby 2021-08-18 23:21:11 +01:00
parent 2f883410d2
commit 57b79aa852

52
app.go
View File

@ -58,7 +58,10 @@ type Headscale struct {
aclPolicy *ACLPolicy aclPolicy *ACLPolicy
aclRules *[]tailcfg.FilterRule aclRules *[]tailcfg.FilterRule
clientsPolling sync.Map clientsUpdateChannels sync.Map
lastStateChangeMutex sync.RWMutex
lastStateChange time.Time
} }
// NewHeadscale returns the Headscale app // NewHeadscale returns the Headscale app
@ -85,12 +88,13 @@ func NewHeadscale(cfg Config) (*Headscale, error) {
} }
h := Headscale{ h := Headscale{
cfg: cfg, cfg: cfg,
dbType: cfg.DBtype, dbType: cfg.DBtype,
dbString: dbString, dbString: dbString,
privateKey: privKey, privateKey: privKey,
publicKey: &pubKey, publicKey: &pubKey,
aclRules: &tailcfg.FilterAllowAll, // default allowall aclRules: &tailcfg.FilterAllowAll, // default allowall
lastStateChange: time.Now().UTC(),
} }
err = h.initDB() err = h.initDB()
@ -168,6 +172,13 @@ func (h *Headscale) Serve() error {
go h.watchForKVUpdates(5000) go h.watchForKVUpdates(5000)
go h.expireEphemeralNodes(5000) go h.expireEphemeralNodes(5000)
s := &http.Server{
Addr: h.cfg.Addr,
Handler: r,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
if h.cfg.TLSLetsEncryptHostname != "" { if h.cfg.TLSLetsEncryptHostname != "" {
if !strings.HasPrefix(h.cfg.ServerURL, "https://") { if !strings.HasPrefix(h.cfg.ServerURL, "https://") {
log.Warn().Msg("Listening with TLS but ServerURL does not start with https://") log.Warn().Msg("Listening with TLS but ServerURL does not start with https://")
@ -179,9 +190,11 @@ func (h *Headscale) Serve() error {
Cache: autocert.DirCache(h.cfg.TLSLetsEncryptCacheDir), Cache: autocert.DirCache(h.cfg.TLSLetsEncryptCacheDir),
} }
s := &http.Server{ s := &http.Server{
Addr: h.cfg.Addr, Addr: h.cfg.Addr,
TLSConfig: m.TLSConfig(), TLSConfig: m.TLSConfig(),
Handler: r, Handler: r,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
} }
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)
@ -206,12 +219,27 @@ func (h *Headscale) Serve() error {
if !strings.HasPrefix(h.cfg.ServerURL, "http://") { if !strings.HasPrefix(h.cfg.ServerURL, "http://") {
log.Warn().Msg("Listening without TLS but ServerURL does not start with http://") log.Warn().Msg("Listening without TLS but ServerURL does not start with http://")
} }
err = r.Run(h.cfg.Addr) err = s.ListenAndServe()
} else { } else {
if !strings.HasPrefix(h.cfg.ServerURL, "https://") { if !strings.HasPrefix(h.cfg.ServerURL, "https://") {
log.Warn().Msg("Listening with TLS but ServerURL does not start with https://") log.Warn().Msg("Listening with TLS but ServerURL does not start with https://")
} }
err = r.RunTLS(h.cfg.Addr, h.cfg.TLSCertPath, h.cfg.TLSKeyPath) err = s.ListenAndServeTLS(h.cfg.TLSCertPath, h.cfg.TLSKeyPath)
} }
return err return err
} }
func (h *Headscale) setLastStateChangeToNow() {
h.lastStateChangeMutex.Lock()
now := time.Now().UTC()
h.lastStateChange = now
h.lastStateChangeMutex.Unlock()
}
func (h *Headscale) getLastStateChange() time.Time {
h.lastStateChangeMutex.RLock()
defer h.lastStateChangeMutex.RUnlock()
return h.lastStateChange
}