Skip to content

Commit 0978ffb

Browse files
authored
Merge pull request #727 from jmpsec/audit-log-admin
Audit log for `osctrl-admin`
2 parents 8c84654 + 4d91c45 commit 0978ffb

File tree

18 files changed

+861
-152
lines changed

18 files changed

+861
-152
lines changed

cmd/admin/handlers/get.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"io"
55
"net/http"
66
"os"
7+
"strings"
78

89
"github.com/jmpsec/osctrl/cmd/admin/sessions"
910
"github.com/jmpsec/osctrl/pkg/carves"
@@ -150,4 +151,6 @@ func (h *HandlersAdmin) CarvesDownloadHandler(w http.ResponseWriter, r *http.Req
150151
fileReader, _ = os.Open(archived.File)
151152
_, _ = io.Copy(w, fileReader)
152153
}
154+
// Audit log visit
155+
h.AuditLog.Visit(ctx[sessions.CtxUser], r.URL.Path, strings.Split(r.RemoteAddr, ":")[0], env.ID)
153156
}

cmd/admin/handlers/handlers.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package handlers
22

33
import (
44
"github.com/jmpsec/osctrl/cmd/admin/sessions"
5+
"github.com/jmpsec/osctrl/pkg/auditlog"
56
"github.com/jmpsec/osctrl/pkg/backend"
67
"github.com/jmpsec/osctrl/pkg/cache"
78
"github.com/jmpsec/osctrl/pkg/carves"
@@ -43,6 +44,7 @@ type HandlersAdmin struct {
4344
OptimizedUI bool
4445
OsqueryTables []types.OsqueryTable
4546
AdminConfig *config.JSONConfigurationService
47+
AuditLog *auditlog.AuditLogManager
4648
DBLogger *logging.LoggerDB
4749
DebugHTTP *zerolog.Logger
4850
DebugHTTPConfig *config.DebugHTTPConfiguration
@@ -161,6 +163,12 @@ func WithAdminConfig(config *config.JSONConfigurationService) HandlersOption {
161163
}
162164
}
163165

166+
func WithAuditLog(auditLog *auditlog.AuditLogManager) HandlersOption {
167+
return func(h *HandlersAdmin) {
168+
h.AuditLog = auditLog
169+
}
170+
}
171+
164172
func WithDBLogger(dbfile string, config *backend.JSONConfigurationDB) HandlersOption {
165173
return func(h *HandlersAdmin) {
166174
if dbfile == "" {

cmd/admin/handlers/json-audit.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package handlers
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
"github.com/jmpsec/osctrl/cmd/admin/sessions"
8+
"github.com/jmpsec/osctrl/pkg/auditlog"
9+
"github.com/jmpsec/osctrl/pkg/environments"
10+
"github.com/jmpsec/osctrl/pkg/users"
11+
"github.com/jmpsec/osctrl/pkg/utils"
12+
"github.com/rs/zerolog/log"
13+
)
14+
15+
// AuditLogJSON to be used to populate JSON data for audit logs
16+
type AuditLogJSON struct {
17+
Service string `json:"service"`
18+
Username string `json:"username"`
19+
Line string `json:"line"`
20+
SourceIP string `json:"sourceip"`
21+
LogType string `json:"logtype"`
22+
Severity string `json:"severity"`
23+
Env string `json:"environment"`
24+
When CreationTimes `json:"when"`
25+
}
26+
27+
// ReturnedAudit to return a JSON with audit logs
28+
type ReturnedAudit struct {
29+
Data []AuditLogJSON `json:"data"`
30+
}
31+
32+
// JSONAuditLogHandler for audit logs in JSON
33+
func (h *HandlersAdmin) JSONAuditLogHandler(w http.ResponseWriter, r *http.Request) {
34+
if h.DebugHTTPConfig.Enabled {
35+
utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody)
36+
}
37+
// Get context data
38+
ctx := r.Context().Value(sessions.ContextKey(sessions.CtxSession)).(sessions.ContextValue)
39+
// Check permissions
40+
if !h.Users.CheckPermissions(ctx[sessions.CtxUser], users.AdminLevel, users.NoEnvironment) {
41+
adminErrorResponse(w, fmt.Sprintf("%s has insufficient permissions", ctx[sessions.CtxUser]), http.StatusForbidden, nil)
42+
return
43+
}
44+
// Get all environments
45+
envs, err := h.Envs.All()
46+
if err != nil {
47+
log.Err(err).Msg("error getting environments")
48+
return
49+
}
50+
// Get audit logs
51+
auditLogs, err := h.AuditLog.GetAll()
52+
if err != nil {
53+
log.Err(err).Msg("error getting audit logs")
54+
return
55+
}
56+
// Prepare data to be returned
57+
var auditLogsJSON []AuditLogJSON
58+
for _, logEntry := range auditLogs {
59+
auditLogsJSON = append(auditLogsJSON, AuditLogJSON{
60+
Service: logEntry.Service,
61+
Username: logEntry.Username,
62+
Line: logEntry.Line,
63+
SourceIP: logEntry.SourceIP,
64+
LogType: auditlog.LogTypeToString(logEntry.LogType),
65+
Severity: auditlog.SeverityToString(logEntry.Severity),
66+
Env: environments.EnvironmentFinderID(logEntry.EnvironmentID, envs, false),
67+
When: CreationTimes{
68+
Display: utils.PastFutureTimes(logEntry.CreatedAt),
69+
// Use Unix timestamp in seconds
70+
Timestamp: utils.TimeTimestamp(logEntry.CreatedAt),
71+
},
72+
})
73+
}
74+
returned := ReturnedAudit{
75+
Data: auditLogsJSON,
76+
}
77+
// Serve JSON
78+
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, returned)
79+
}

cmd/admin/handlers/post.go

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"net/http"
1111

1212
"github.com/jmpsec/osctrl/cmd/admin/sessions"
13+
"github.com/jmpsec/osctrl/pkg/auditlog"
1314
"github.com/jmpsec/osctrl/pkg/handlers"
1415
"github.com/jmpsec/osctrl/pkg/nodes"
1516
"github.com/jmpsec/osctrl/pkg/queries"
@@ -45,7 +46,7 @@ func (h *HandlersAdmin) LoginPOSTHandler(w http.ResponseWriter, r *http.Request)
4546
return
4647
}
4748
// Serialize and send response
48-
log.Debug().Msg("Login response sent")
49+
h.AuditLog.NewLogin(user.Username, strings.Split(r.RemoteAddr, ":")[0])
4950
adminOKResponse(w, "/dashboard")
5051
}
5152

@@ -74,7 +75,7 @@ func (h *HandlersAdmin) LogoutPOSTHandler(w http.ResponseWriter, r *http.Request
7475
return
7576
}
7677
// Serialize and send response
77-
log.Debug().Msg("Logout response sent")
78+
h.AuditLog.NewLogout(ctx[sessions.CtxUser], strings.Split(r.RemoteAddr, ":")[0])
7879
adminOKResponse(w, "OK")
7980
}
8081

@@ -172,7 +173,7 @@ func (h *HandlersAdmin) QueryRunPOSTHandler(w http.ResponseWriter, r *http.Reque
172173
}
173174
}
174175
// Serialize and send response
175-
log.Debug().Msg("Query run response sent")
176+
h.AuditLog.NewQuery(ctx[sessions.CtxUser], q.Query, strings.Split(r.RemoteAddr, ":")[0], env.ID)
176177
adminOKResponse(w, "OK")
177178
}
178179

@@ -262,7 +263,7 @@ func (h *HandlersAdmin) CarvesRunPOSTHandler(w http.ResponseWriter, r *http.Requ
262263
return
263264
}
264265
// Serialize and send response
265-
log.Debug().Msg("Carve run response sent")
266+
h.AuditLog.NewCarve(ctx[sessions.CtxUser], c.Path, strings.Split(r.RemoteAddr, ":")[0], env.ID)
266267
adminOKResponse(w, "OK")
267268
}
268269

@@ -345,8 +346,7 @@ func (h *HandlersAdmin) QueryActionsPOSTHandler(w http.ResponseWriter, r *http.R
345346
}
346347
adminOKResponse(w, "queries delete successfully")
347348
}
348-
// Serialize and send response
349-
log.Debug().Msg("Query run response sent")
349+
h.AuditLog.QueryAction(ctx[sessions.CtxUser], q.Action, strings.Split(r.RemoteAddr, ":")[0], env.ID)
350350
}
351351

352352
// CarvesActionsPOSTHandler - Handler for POST requests to carves
@@ -402,8 +402,7 @@ func (h *HandlersAdmin) CarvesActionsPOSTHandler(w http.ResponseWriter, r *http.
402402
log.Debug().Msg("testing action")
403403
adminOKResponse(w, "test successful")
404404
}
405-
// Serialize and send response
406-
log.Debug().Msg("Carves action response sent")
405+
h.AuditLog.CarveAction(ctx[sessions.CtxUser], q.Action, strings.Split(r.RemoteAddr, ":")[0], env.ID)
407406
}
408407

409408
// ConfPOSTHandler for POST requests for saving configuration
@@ -466,8 +465,8 @@ func (h *HandlersAdmin) ConfPOSTHandler(w http.ResponseWriter, r *http.Request)
466465
adminErrorResponse(w, "error saving configuration parts", http.StatusInternalServerError, err)
467466
return
468467
}
468+
h.AuditLog.ConfAction(ctx[sessions.CtxUser], "update configuration", strings.Split(r.RemoteAddr, ":")[0], env.ID)
469469
// Send response
470-
log.Debug().Msg("Configuration response sent")
471470
adminOKResponse(w, "configuration saved successfully")
472471
return
473472
}
@@ -489,8 +488,8 @@ func (h *HandlersAdmin) ConfPOSTHandler(w http.ResponseWriter, r *http.Request)
489488
adminErrorResponse(w, "error updating configuration", http.StatusInternalServerError, err)
490489
return
491490
}
491+
h.AuditLog.ConfAction(ctx[sessions.CtxUser], "update options", strings.Split(r.RemoteAddr, ":")[0], env.ID)
492492
// Send response
493-
log.Debug().Msg("Options response sent")
494493
adminOKResponse(w, "options saved successfully")
495494
return
496495
}
@@ -512,8 +511,8 @@ func (h *HandlersAdmin) ConfPOSTHandler(w http.ResponseWriter, r *http.Request)
512511
adminErrorResponse(w, "error updating configuration", http.StatusInternalServerError, err)
513512
return
514513
}
514+
h.AuditLog.ConfAction(ctx[sessions.CtxUser], "update schedule", strings.Split(r.RemoteAddr, ":")[0], env.ID)
515515
// Send response
516-
log.Debug().Msg("Schedule response sent")
517516
adminOKResponse(w, "schedule saved successfully")
518517
return
519518
}
@@ -535,8 +534,8 @@ func (h *HandlersAdmin) ConfPOSTHandler(w http.ResponseWriter, r *http.Request)
535534
adminErrorResponse(w, "error updating configuration", http.StatusInternalServerError, err)
536535
return
537536
}
537+
h.AuditLog.ConfAction(ctx[sessions.CtxUser], "update packs", strings.Split(r.RemoteAddr, ":")[0], env.ID)
538538
// Send response
539-
log.Debug().Msg("Packs response sent")
540539
adminOKResponse(w, "packs saved successfully")
541540
return
542541
}
@@ -558,8 +557,8 @@ func (h *HandlersAdmin) ConfPOSTHandler(w http.ResponseWriter, r *http.Request)
558557
adminErrorResponse(w, "error updating configuration", http.StatusInternalServerError, err)
559558
return
560559
}
560+
h.AuditLog.ConfAction(ctx[sessions.CtxUser], "update decorators", strings.Split(r.RemoteAddr, ":")[0], env.ID)
561561
// Send response
562-
log.Debug().Msg("Decorators response sent")
563562
adminOKResponse(w, "decorators saved successfully")
564563
return
565564
}
@@ -581,8 +580,8 @@ func (h *HandlersAdmin) ConfPOSTHandler(w http.ResponseWriter, r *http.Request)
581580
adminErrorResponse(w, "error updating configuration", http.StatusInternalServerError, err)
582581
return
583582
}
583+
h.AuditLog.ConfAction(ctx[sessions.CtxUser], "update ATC", strings.Split(r.RemoteAddr, ":")[0], env.ID)
584584
// Send response
585-
log.Debug().Msg("ATC response sent")
586585
adminOKResponse(w, "ATC saved successfully")
587586
return
588587
}
@@ -644,8 +643,8 @@ func (h *HandlersAdmin) IntervalsPOSTHandler(w http.ResponseWriter, r *http.Requ
644643
adminErrorResponse(w, "error updating flags", http.StatusInternalServerError, err)
645644
return
646645
}
646+
h.AuditLog.ConfAction(ctx[sessions.CtxUser], "update intervals", strings.Split(r.RemoteAddr, ":")[0], env.ID)
647647
// Serialize and send response
648-
log.Debug().Msg("Intervals response sent")
649648
adminOKResponse(w, "intervals saved successfully")
650649
}
651650

@@ -741,8 +740,7 @@ func (h *HandlersAdmin) ExpirationPOSTHandler(w http.ResponseWriter, r *http.Req
741740
adminOKResponse(w, "link set to not expire successfully")
742741
}
743742
}
744-
// Serialize and send response
745-
log.Debug().Msg("Expiration response sent")
743+
h.AuditLog.ConfAction(ctx[sessions.CtxUser], fmt.Sprintf("%s:%s", e.Type, e.Action), strings.Split(r.RemoteAddr, ":")[0], env.ID)
746744
}
747745

748746
// NodeActionsPOSTHandler for POST requests for multi node action
@@ -787,8 +785,7 @@ func (h *HandlersAdmin) NodeActionsPOSTHandler(w http.ResponseWriter, r *http.Re
787785
return
788786
}
789787
}
790-
// Serialize and send response
791-
log.Debug().Msg("Multi-node action response sent")
788+
h.AuditLog.NodeAction(ctx[sessions.CtxUser], m.Action, strings.Split(r.RemoteAddr, ":")[0], auditlog.NoEnvironment)
792789
}
793790

794791
// EnvsPOSTHandler for POST request for /environments
@@ -878,8 +875,7 @@ func (h *HandlersAdmin) EnvsPOSTHandler(w http.ResponseWriter, r *http.Request)
878875
}
879876
adminOKResponse(w, "debug changed successfully")
880877
}
881-
// Serialize and send response
882-
log.Debug().Msg("Environments response sent")
878+
h.AuditLog.EnvAction(ctx[sessions.CtxUser], fmt.Sprintf("%s - %s", c.Action, c.Name), strings.Split(r.RemoteAddr, ":")[0], auditlog.NoEnvironment)
883879
}
884880

885881
// SettingsPOSTHandler for POST request for /settings
@@ -963,8 +959,7 @@ func (h *HandlersAdmin) SettingsPOSTHandler(w http.ResponseWriter, r *http.Reque
963959
}
964960
adminOKResponse(w, "setting deleted successfully")
965961
}
966-
// Serialize and send response
967-
log.Debug().Msg("Settings response sent")
962+
h.AuditLog.SettingsAction(ctx[sessions.CtxUser], fmt.Sprintf("%s - %s", s.Action, s.Name), strings.Split(r.RemoteAddr, ":")[0])
968963
}
969964

970965
// UsersPOSTHandler for POST request for /users
@@ -1112,8 +1107,7 @@ func (h *HandlersAdmin) UsersPOSTHandler(w http.ResponseWriter, r *http.Request)
11121107
adminOKResponse(w, "service changed successfully")
11131108
}
11141109
}
1115-
// Serialize and send response
1116-
log.Debug().Msg("Users response sent")
1110+
h.AuditLog.UserAction(ctx[sessions.CtxUser], fmt.Sprintf("%s - %s", u.Action, u.Username), strings.Split(r.RemoteAddr, ":")[0])
11171111
}
11181112

11191113
// TagsPOSTHandler for POST request for /tags
@@ -1205,8 +1199,7 @@ func (h *HandlersAdmin) TagsPOSTHandler(w http.ResponseWriter, r *http.Request)
12051199
}
12061200
adminOKResponse(w, "tag removed successfully")
12071201
}
1208-
// Serialize and send response
1209-
log.Debug().Msg("Tags response sent")
1202+
h.AuditLog.TagAction(ctx[sessions.CtxUser], fmt.Sprintf("%s - %s", t.Action, t.Name), strings.Split(r.RemoteAddr, ":")[0], env.ID)
12101203
}
12111204

12121205
// TagNodesPOSTHandler for POST request for /tags/nodes
@@ -1263,8 +1256,9 @@ func (h *HandlersAdmin) TagNodesPOSTHandler(w http.ResponseWriter, r *http.Reque
12631256
return
12641257
}
12651258
}
1259+
aMsg := fmt.Sprintf("tags processed: add %d, remove %d", len(t.TagsAdd), len(t.TagsRemove))
1260+
h.AuditLog.TagAction(ctx[sessions.CtxUser], aMsg, strings.Split(r.RemoteAddr, ":")[0], toBeProcessed[0].EnvironmentID)
12661261
// Serialize and send response
1267-
log.Debug().Msg("Tags response sent")
12681262
adminOKResponse(w, "tags processed successfully")
12691263
}
12701264

@@ -1322,8 +1316,8 @@ func (h *HandlersAdmin) PermissionsPOSTHandler(w http.ResponseWriter, r *http.Re
13221316
return
13231317
}
13241318
}
1319+
h.AuditLog.UserAction(ctx[sessions.CtxUser], fmt.Sprintf("permissions - %s", usernameVar), strings.Split(r.RemoteAddr, ":")[0])
13251320
// Serialize and send response
1326-
log.Debug().Msg("Users response sent")
13271321
adminOKResponse(w, "permissions updated successfully")
13281322
}
13291323

@@ -1423,8 +1417,8 @@ func (h *HandlersAdmin) EnrollPOSTHandler(w http.ResponseWriter, r *http.Request
14231417
}
14241418
}
14251419
}
1420+
h.AuditLog.EnvAction(ctx[sessions.CtxUser], fmt.Sprintf("%s - %s", e.Action, env.Name), strings.Split(r.RemoteAddr, ":")[0], env.ID)
14261421
// Serialize and send response
1427-
log.Debug().Msg("Configuration response sent")
14281422
adminOKResponse(w, "enroll data saved")
14291423
}
14301424

@@ -1491,8 +1485,7 @@ func (h *HandlersAdmin) EditProfilePOSTHandler(w http.ResponseWriter, r *http.Re
14911485
}
14921486
adminOKResponse(w, "profiled updated successfully")
14931487
}
1494-
// Serialize and send response
1495-
log.Debug().Msg("Edit profile response sent")
1488+
h.AuditLog.UserAction(ctx[sessions.CtxUser], fmt.Sprintf("%s - %s", u.Action, u.Username), strings.Split(r.RemoteAddr, ":")[0])
14961489
}
14971490

14981491
// SavedQueriesPOSTHandler for POST requests to save queries
@@ -1520,6 +1513,4 @@ func (h *HandlersAdmin) SavedQueriesPOSTHandler(w http.ResponseWriter, r *http.R
15201513
case "edit":
15211514
adminOKResponse(w, "query saved successfully")
15221515
}
1523-
// Serialize and send response
1524-
log.Debug().Msg("Saved query response sent")
15251516
}

0 commit comments

Comments
 (0)