Watchdog: Verify Server TLS Certificate

This commit is contained in:
Maximilian Paß
2024-01-16 15:03:52 +01:00
committed by Sebastian Serth
parent b48c7fe8b6
commit 221a6ff1b2
5 changed files with 52 additions and 28 deletions

View File

@ -9,6 +9,7 @@ Requires=poseidon.socket
WorkingDirectory=${GITHUB_WORKSPACE}
ExecStart=${GITHUB_WORKSPACE}/poseidon
Environment="POSEIDON_SERVER_SYSTEMDSOCKETACTIVATION=TRUE"
Environment="GOCOVERDIR=${GITHUB_WORKSPACE}/${GOCOVERDIR}"
Restart=always
StartLimitBurst=0

View File

@ -3,6 +3,7 @@ package main
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"github.com/coreos/go-systemd/v22/activation"
@ -26,6 +27,7 @@ import (
"runtime/debug"
"runtime/pprof"
"strconv"
"strings"
"sync"
"time"
)
@ -234,18 +236,30 @@ func notifySystemd(router *mux.Router) {
log.WithError(err).Error("Systemd Watchdog not supported")
return
}
go notifyWatchdog(context.Background(), router, interval)
go systemdWatchdogLoop(context.Background(), router, interval)
}
func notifyWatchdog(ctx context.Context, router *mux.Router, interval time.Duration) {
func systemdWatchdogLoop(ctx context.Context, router *mux.Router, interval time.Duration) {
healthRoute, err := router.Get(api.HealthPath).URL()
if err != nil {
log.WithError(err).Error("Failed to parse Health route")
return
}
// We do not verify the certificate as we (intend to) perform only requests to the local server.
tlsConfig := &tls.Config{InsecureSkipVerify: true} // #nosec G402 The default min tls version is secure.
client := &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}}
healthURL := config.Config.Server.URL().String() + healthRoute.String()
healthURL = strings.ReplaceAll(healthURL, "0.0.0.0", "localhost") // Workaround for certificate subject names
client := &http.Client{}
if config.Config.Server.TLS.Active {
tlsConfig := &tls.Config{RootCAs: x509.NewCertPool()} // #nosec G402 The default MinTLSVersion is secure.
caCertBytes, err := os.ReadFile(config.Config.Server.TLS.CAFile)
if err != nil {
log.WithError(err).Warn("Cannot read tls ca file")
} else {
ok := tlsConfig.RootCAs.AppendCertsFromPEM(caCertBytes)
log.WithField("success", ok).Trace("Loaded CA certificate")
}
client.Transport = &http.Transport{TLSClientConfig: tlsConfig}
}
// notificationIntervalFactor defines how many more notifications we send than required.
const notificationIntervalFactor = 2
@ -254,15 +268,22 @@ func notifyWatchdog(ctx context.Context, router *mux.Router, interval time.Durat
case <-ctx.Done():
return
case <-time.After(interval / notificationIntervalFactor):
req, err := http.NewRequestWithContext(ctx, http.MethodGet, config.Config.Server.URL().String()+healthRoute.String(), http.NoBody)
notifySystemdWatchdog(ctx, healthURL, client)
}
}
}
func notifySystemdWatchdog(ctx context.Context, healthURL string, client *http.Client) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, healthURL, http.NoBody)
if err != nil {
continue
return
}
resp, err := client.Do(req)
if err != nil {
log.WithError(err).Debug("Failed watchdog health check")
// We do not check for resp.StatusCode == 503 as Poseidon's error recovery will try to handle such errors
// by itself. The Watchdog should just check that Poseidon handles http requests at all.
continue
return
}
_ = resp.Body.Close()
@ -276,8 +297,6 @@ func notifyWatchdog(ctx context.Context, router *mux.Router, interval time.Durat
log.Trace("Notified Systemd Watchdog")
}
}
}
}
type managerCreator func(ctx context.Context) (
runnerManager runner.Manager, environmentManager environment.ManagerHandler)
@ -375,7 +394,7 @@ func initServer(router *mux.Router) *http.Server {
func shutdownOnOSSignal(server *http.Server, ctx context.Context, stopProfiling func()) {
// wait for SIGINT
shutdownSignals := make(chan os.Signal, 1)
signal.Notify(shutdownSignals, unix.SIGINT, unix.SIGTERM)
signal.Notify(shutdownSignals, unix.SIGINT, unix.SIGTERM, unix.SIGABRT)
// wait for SIGUSR1
writeProfileSignal := make(chan os.Signal, 1)

View File

@ -15,6 +15,8 @@ server:
tls:
# If set, the API uses TLS for all incoming connections.
active: false
# The path to the certificate of the CA authority used for TLS
# cafile: ./ca.crt
# The path to the certificate file used for TLS
# certfile: ./poseidon.crt
# The path to the key file used for TLS

View File

@ -27,6 +27,7 @@ var (
Token: "",
TLS: TLS{
Active: false,
CAFile: "",
CertFile: "",
KeyFile: "",
},

View File

@ -7,6 +7,7 @@ import (
"github.com/openHPI/poseidon/tests/e2e"
"github.com/openHPI/poseidon/tests/helpers"
"github.com/shirou/gopsutil/v3/process"
"golang.org/x/sys/unix"
"net/http"
"time"
)
@ -59,7 +60,7 @@ func killPoseidon() {
continue
}
if n == "poseidon" {
err = p.Kill()
err = p.SendSignal(unix.SIGTERM)
if err != nil {
log.WithError(err).Error("Error killing Poseidon")
} else {