feat(cert): dial the TLS terminator directly so cert-expiry works from the container
checkCertExpiry now accepts a `dial` address and sets ServerName to the hostname — it connects to the terminator's IP while SNI/cert-read use the hostname. The scheduler container has no mesh interface and the host resolver doesn't know the split-horizon zone, so *.hubris.network can't be dialed by name from there; dialing Caddy's lab IP (reachable on the LAN) makes the probe work. The builder passes through a cert entity's `dial` attribute. Re-seed the 20 *.hubris.network certificate entities with dial=192.168.8.175 (Caddy) and uses-certificate edges; cert-expiry monitoring now has real data.
This commit is contained in:
@@ -249,17 +249,26 @@ func buildKind(kind string, t Target, attrs map[string]any, host, user string, p
|
||||
}}, ""
|
||||
|
||||
case KindCertExpiry:
|
||||
// The host to TLS-dial for the cert. Prefer an explicit `hostname`
|
||||
// attribute, then `cn`, then a dotted name (a certificate's name is
|
||||
// its CN/SAN). Hourly: expiry changes once a day, but a renewal or a
|
||||
// mis-issued cert is worth noticing within the hour.
|
||||
// The host whose cert to read (SNI / cert CN). Prefer an explicit
|
||||
// `hostname` attribute, then `cn`, then a dotted name. Hourly: expiry
|
||||
// changes once a day, but a renewal or mis-issued cert is worth
|
||||
// noticing within the hour.
|
||||
host := certHost(t, attrs)
|
||||
if host == "" {
|
||||
return nil, "no hostname / cn / dotted name to dial for the cert"
|
||||
}
|
||||
// `dial` is the TLS terminator's address to connect to (Caddy's lab
|
||||
// IP), used when the hostname doesn't resolve/reach from the scheduler.
|
||||
// Without it the probe can't reach *.hubris.network from a container
|
||||
// with no mesh / split-horizon DNS.
|
||||
dial, _ := attrs["dial"].(string)
|
||||
config := map[string]any{"host": host, "warn_days": 30, "crit_days": 7}
|
||||
if dial != "" {
|
||||
config["dial"] = dial
|
||||
}
|
||||
return []checkDef{{
|
||||
kind: "cert-expiry",
|
||||
config: map[string]any{"host": host, "warn_days": 30, "crit_days": 7},
|
||||
config: config,
|
||||
interval: 3600,
|
||||
}}, ""
|
||||
}
|
||||
|
||||
@@ -536,6 +536,12 @@ func checkDisk(ctx context.Context, cd sqlcgen.ListEnabledCheckDefsRow) checkRes
|
||||
func checkCertExpiry(ctx context.Context, cd sqlcgen.ListEnabledCheckDefsRow) checkResult {
|
||||
cfg := struct {
|
||||
Host string `json:"host"`
|
||||
// Dial is an optional explicit dial address (the TLS terminator's IP)
|
||||
// for when the hostname doesn't resolve/reach from the scheduler — the
|
||||
// container has no mesh interface and the host resolver doesn't know
|
||||
// the split-horizon zone, so *.hubris.network dials Caddy's lab IP
|
||||
// directly while SNI/cert-read still uses Host.
|
||||
Dial string `json:"dial"`
|
||||
Port int `json:"port"`
|
||||
WarnDays int `json:"warn_days"`
|
||||
CritDays int `json:"crit_days"`
|
||||
@@ -556,9 +562,13 @@ func checkCertExpiry(ctx context.Context, cd sqlcgen.ListEnabledCheckDefsRow) ch
|
||||
timeout = 30 * time.Second
|
||||
}
|
||||
|
||||
addr := net.JoinHostPort(cfg.Host, fmt.Sprintf("%d", cfg.Port))
|
||||
dialHost := cfg.Host
|
||||
if cfg.Dial != "" {
|
||||
dialHost = cfg.Dial
|
||||
}
|
||||
addr := net.JoinHostPort(dialHost, fmt.Sprintf("%d", cfg.Port))
|
||||
|
||||
d := tls.Dialer{Config: &tls.Config{InsecureSkipVerify: true}}
|
||||
d := tls.Dialer{Config: &tls.Config{InsecureSkipVerify: true, ServerName: cfg.Host}}
|
||||
conn, err := d.DialContext(ctx, "tcp", addr)
|
||||
if err != nil {
|
||||
return checkResult{
|
||||
|
||||
Reference in New Issue
Block a user