mirror of
https://github.com/henrygd/beszel.git
synced 2026-09-25 19:07:47 +02:00
feat: add TLS certificate expiry check to HTTPS network monitors (#2401)
Co-authored-by: henrygd <hank@henrygd.me>
This commit is contained in:
@@ -114,6 +114,9 @@ func setMonitorResultFields(record *core.Record, result monitor.Result) {
|
||||
record.Set("resMin1h", result.MinResponse1h)
|
||||
record.Set("resMax1h", result.MaxResponse1h)
|
||||
record.Set("loss1h", result.PacketLoss1h)
|
||||
if result.Cert != nil {
|
||||
record.Set("certInfo", result.Cert)
|
||||
}
|
||||
record.Set("updated", nowString)
|
||||
}
|
||||
|
||||
|
||||
@@ -206,6 +206,7 @@ func TestCopyMonitorToNewRecordDropsResultFields(t *testing.T) {
|
||||
"resMin1h": 900,
|
||||
"resMax1h": 1600,
|
||||
"loss1h": 5,
|
||||
"certInfo": map[string]any{"expires": 1800000000000},
|
||||
"updated": "2026-04-29 12:00:00.000Z",
|
||||
})
|
||||
|
||||
@@ -216,6 +217,7 @@ func TestCopyMonitorToNewRecordDropsResultFields(t *testing.T) {
|
||||
assert.Equal(t, "http", newRecord.GetString("protocol"))
|
||||
assert.Equal(t, 443, newRecord.GetInt("port"))
|
||||
assert.True(t, newRecord.GetBool("enabled"))
|
||||
assert.Contains(t, []string{"", "null"}, newRecord.GetString("certInfo"))
|
||||
assert.Zero(t, newRecord.GetFloat("res"))
|
||||
assert.Zero(t, newRecord.GetFloat("resAvg1h"))
|
||||
assert.Zero(t, newRecord.GetFloat("resMin1h"))
|
||||
|
||||
@@ -222,3 +222,49 @@ func TestNetworkMonitorAlertsAfterCommit(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNetworkMonitorCertPersistence(t *testing.T) {
|
||||
for _, realtime := range []bool{false, true} {
|
||||
name := "sql"
|
||||
if realtime {
|
||||
name = "realtime"
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
sys, app := newTestSystemWithHub(t)
|
||||
if realtime {
|
||||
client := subscriptions.NewDefaultClient()
|
||||
client.Subscribe("network_monitors/*")
|
||||
app.SubscriptionsBroker().Register(client)
|
||||
t.Cleanup(func() { app.SubscriptionsBroker().Unregister(client.Id()) })
|
||||
}
|
||||
col, err := app.FindCachedCollectionByNameOrId("network_monitors")
|
||||
require.NoError(t, err)
|
||||
record := core.NewRecord(col)
|
||||
record.Id = "monitor1"
|
||||
record.Set("system", sys.Id)
|
||||
require.NoError(t, app.SaveNoValidate(record))
|
||||
|
||||
storedCert := func() monitor.CertInfo {
|
||||
t.Helper()
|
||||
record, err := app.FindRecordById("network_monitors", "monitor1")
|
||||
require.NoError(t, err)
|
||||
var cert monitor.CertInfo
|
||||
require.NoError(t, record.UnmarshalJSONField("certInfo", &cert))
|
||||
return cert
|
||||
}
|
||||
cert := &monitor.CertInfo{Expires: 1_800_000_000_000, Issuer: "Test CA"}
|
||||
_, err = sys.createRecords(&system.CombinedData{Monitors: map[string]monitor.Result{
|
||||
"monitor1": {LastProbeAt: 1000, Cert: cert},
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, *cert, storedCert())
|
||||
|
||||
// Results without cert info keep the stored certificate.
|
||||
_, err = sys.createRecords(&system.CombinedData{Monitors: map[string]monitor.Result{
|
||||
"monitor1": {LastProbeAt: 2000},
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, *cert, storedCert())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,6 +433,8 @@ func (sys *System) updateNetworkMonitorsRecords(app core.App, monitorResults map
|
||||
for i, f := range monitorFields {
|
||||
setClauses[i] = fmt.Sprintf("%s={:%s}", f, f)
|
||||
}
|
||||
// Results omit certInfo unless it changed, so keep the stored value.
|
||||
setClauses = append(setClauses, "certInfo=COALESCE({:certInfo}, certInfo)")
|
||||
queryString := fmt.Sprintf("UPDATE %s SET %s WHERE id={:id}", monitorCollectionName, strings.Join(setClauses, ", "))
|
||||
updateQuery = db.NewQuery(queryString)
|
||||
}
|
||||
@@ -453,11 +455,23 @@ func (sys *System) updateNetworkMonitorsRecords(app core.App, monitorResults map
|
||||
var record *core.Record
|
||||
record, err = app.FindRecordById(monitorCollectionName, id)
|
||||
if err == nil {
|
||||
if result.Cert != nil {
|
||||
monitorData["certInfo"] = result.Cert
|
||||
}
|
||||
record.Load(monitorData)
|
||||
err = app.SaveNoValidate(record)
|
||||
}
|
||||
default:
|
||||
_, err = updateQuery.Bind(dbx.Params(monitorData)).Execute()
|
||||
monitorData["certInfo"] = nil
|
||||
if result.Cert != nil {
|
||||
var cert []byte
|
||||
if cert, err = json.Marshal(result.Cert); err == nil {
|
||||
monitorData["certInfo"] = string(cert)
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
_, err = updateQuery.Bind(dbx.Params(monitorData)).Execute()
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
app.Logger().Warn("Failed to update monitor", "system", systemId, "monitor", id, "err", err)
|
||||
|
||||
Reference in New Issue
Block a user