mirror of
https://github.com/juanfont/headscale.git
synced 2026-11-04 12:39:25 +09:00
integration: test TLS certificate reload on SIGHUP
Fixes #3027 Claude-Session: https://claude.ai/code/session_01RHEZM1qJ2hgndiXio7P56h
This commit is contained in:
1 parent
b3ffec4269
commit
021f0294f8
2 files changed
+66
No files matched your search
@@ -15,6 +15,7 @@ import (
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
@@ -461,3 +462,67 @@ func parseChain(t *testing.T, chainPEM []byte) (*x509.Certificate, *x509.CertPoo
|
||||
|
||||
return certs[0], intermediates
|
||||
}
|
||||
|
||||
// TestTLSCertificateReload covers what the handshake tests in hscontrol cannot:
|
||||
// a SIGHUP reaching the certificate. No policy is configured, so it also pins
|
||||
// that the reload runs before the signal handler skips the policy.
|
||||
func TestTLSCertificateReload(t *testing.T) {
|
||||
IntegrationSkip(t)
|
||||
|
||||
const (
|
||||
certPath = "/etc/headscale/tls.cert"
|
||||
keyPath = "/etc/headscale/tls.key"
|
||||
)
|
||||
|
||||
scenario, err := NewScenario(ScenarioSpec{})
|
||||
require.NoError(t, err)
|
||||
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
headscale, err := scenario.Headscale(hsic.WithTestName("tlsreload"))
|
||||
require.NoError(t, err)
|
||||
|
||||
endpoint, err := url.Parse(headscale.GetEndpoint())
|
||||
require.NoError(t, err)
|
||||
|
||||
served := func() ([]byte, error) {
|
||||
// CreateCertificate mints a new CA per call, so nothing here chains to
|
||||
// the startup CA. This compares certificates; it trusts none of them.
|
||||
conn, err := tls.Dial("tcp", endpoint.Host, &tls.Config{InsecureSkipVerify: true}) //nolint:gosec,noctx
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
return conn.ConnectionState().PeerCertificates[0].Raw, nil
|
||||
}
|
||||
|
||||
_, certPEM, keyPEM, err := integrationutil.CreateCertificate(headscale.GetHostname())
|
||||
require.NoError(t, err)
|
||||
|
||||
block, _ := pem.Decode(certPEM)
|
||||
require.NotNil(t, block)
|
||||
|
||||
require.NoError(t, headscale.WriteFile(certPath, certPEM))
|
||||
require.NoError(t, headscale.WriteFile(keyPath, keyPEM))
|
||||
require.NoError(t, headscale.Reload())
|
||||
|
||||
assert.EventuallyWithT(t, func(c *assert.CollectT) {
|
||||
der, err := served()
|
||||
assert.NoError(c, err)
|
||||
assert.Equal(c, block.Bytes, der)
|
||||
}, 10*time.Second, 200*time.Millisecond, "a new connection should get the certificate now on disk")
|
||||
|
||||
require.NoError(t, headscale.WriteFile(keyPath, []byte("garbage")))
|
||||
require.NoError(t, headscale.Reload())
|
||||
|
||||
assert.EventuallyWithT(t, func(c *assert.CollectT) {
|
||||
stdout, stderr, err := headscale.ReadLog()
|
||||
assert.NoError(c, err)
|
||||
assert.Contains(c, stdout+stderr, "reloading TLS certificate failed")
|
||||
}, 10*time.Second, 200*time.Millisecond, "the operator should see the failed reload in the log")
|
||||
|
||||
der, err := served()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, block.Bytes, der, "a failed reload should keep the previous certificate")
|
||||
}
|
||||
@@ -17,6 +17,7 @@ type ControlServer interface {
|
||||
Shutdown() (string, string, error)
|
||||
SaveLog(path string) (string, string, error)
|
||||
ReadLog() (string, string, error)
|
||||
Reload() error
|
||||
SaveProfile(path string) error
|
||||
Execute(command []string) (string, error)
|
||||
WriteFile(path string, content []byte) error
|
||||
|
||||
Reference in new issue
Block a user