From 6e06c7a3a8a7e994d6793bf677f3673d488338da Mon Sep 17 00:00:00 2001 From: Amy Gale Ruth Bowersox Date: Sat, 21 Feb 2026 22:05:33 -0700 Subject: [PATCH] audit message when server starts and shuts down --- database/audit.go | 2 ++ database/auditref.yaml | 4 ++++ database/user.go | 11 +++++++++++ main.go | 17 +++++++++++++++++ util/util.go | 12 ++++++++++++ 5 files changed, 46 insertions(+) diff --git a/database/audit.go b/database/audit.go index f6e17fe..aa2d4f4 100644 --- a/database/audit.go +++ b/database/audit.go @@ -76,6 +76,8 @@ type AuditRecord struct { // at all times! const ( AuditPublishToFrontPage = 1 + AuditStartup = 2 + AuditShutdown = 3 AuditLoginOK = 101 AuditLoginFail = 102 AuditAccountCreated = 103 diff --git a/database/auditref.yaml b/database/auditref.yaml index b58721e..a53c532 100644 --- a/database/auditref.yaml +++ b/database/auditref.yaml @@ -12,6 +12,10 @@ auditReference: - code: 1 text: "Publish Message to Front Page" + - code: 2 + text: "Server Startup" + - code: 3 + text: "Server Shutdown" - code: 101 text: "Login OK" - code: 102 diff --git a/database/user.go b/database/user.go index a566246..1b16055 100644 --- a/database/user.go +++ b/database/user.go @@ -561,6 +561,17 @@ func AmGetAnonUser(ctx context.Context) (*User, error) { return rc, err } +// AmGetBOFH returns the user account of the global system administrator. +func AmGetBOFH(ctx context.Context) (*User, error) { + row := amdb.QueryRowContext(ctx, "SELECT uid FROM users WHERE base_lvl = ?", AmRole("Global.BOFH").Level()) + var uid int32 + err := row.Scan(&uid) + if err != nil { + return nil, err + } + return AmGetUser(ctx, uid) +} + // hashPassword hashes the password value. func hashPassword(password string) string { if len(password) == 0 { diff --git a/main.go b/main.go index be0d731..29a4421 100644 --- a/main.go +++ b/main.go @@ -190,6 +190,16 @@ func main() { closer = ui.SetupUILayer() defer closer() + // Determine my IP address and the admin user. + myIP, err := util.MyIPAddress() + if err != nil { + panic(err) + } + bofh, err := database.AmGetBOFH(context.Background()) + if err != nil { + panic(err) + } + // Set up to trap SIGINT/SIGTERM and shut down gracefully ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() @@ -204,6 +214,10 @@ func main() { // Set up Echo. e := setupEcho() + // Audit the startup + database.AmStoreAudit(database.AmNewAudit(database.AuditStartup, bofh.Uid, myIP.String(), + fmt.Sprintf("version=%s", config.AMSTERDAM_VERSION))) + // Start server go func() { if err := e.Start(":1323"); err != nil && err != http.ErrServerClosed { @@ -218,4 +232,7 @@ func main() { if err := e.Shutdown(ctx); err != nil { e.Logger.Fatal(err) } + + // Audit the shutdown + database.AmStoreAudit(database.AmNewAudit(database.AuditShutdown, bofh.Uid, myIP.String())) } diff --git a/util/util.go b/util/util.go index a5d6f3f..097bd02 100644 --- a/util/util.go +++ b/util/util.go @@ -11,6 +11,7 @@ package util import ( + "net" "regexp" "strings" "time" @@ -168,3 +169,14 @@ func Map[A, B any](in []A, fn func(A) B) []B { } return rc } + +// MyIPAddress returns the local IP address of this machine. +func MyIPAddress() (net.IP, error) { + conn, err := net.Dial("udp", "8.8.8.8:80") + if err != nil { + return nil, err + } + defer conn.Close() + localAddr := conn.LocalAddr().(*net.UDPAddr) + return localAddr.IP, nil +}