Skip to content

Commit 866e88f

Browse files
authored
Merge pull request #729 from jmpsec/audit-log-api
Audit logs in `osctrl-api` and `osctrl-cli`
2 parents 0978ffb + 5b31be7 commit 866e88f

34 files changed

+401
-49
lines changed

cmd/admin/handlers/json-audit.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"net/http"
66

77
"github.com/jmpsec/osctrl/cmd/admin/sessions"
8-
"github.com/jmpsec/osctrl/pkg/auditlog"
98
"github.com/jmpsec/osctrl/pkg/environments"
109
"github.com/jmpsec/osctrl/pkg/users"
1110
"github.com/jmpsec/osctrl/pkg/utils"
@@ -61,8 +60,8 @@ func (h *HandlersAdmin) JSONAuditLogHandler(w http.ResponseWriter, r *http.Reque
6160
Username: logEntry.Username,
6261
Line: logEntry.Line,
6362
SourceIP: logEntry.SourceIP,
64-
LogType: auditlog.LogTypeToString(logEntry.LogType),
65-
Severity: auditlog.SeverityToString(logEntry.Severity),
63+
LogType: h.AuditLog.LogTypeToString(logEntry.LogType),
64+
Severity: h.AuditLog.SeverityToString(logEntry.Severity),
6665
Env: environments.EnvironmentFinderID(logEntry.EnvironmentID, envs, false),
6766
When: CreationTimes{
6867
Display: utils.PastFutureTimes(logEntry.CreatedAt),

cmd/admin/main.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,6 @@ func osctrlAdminService() {
226226
if err := loadingSettings(settingsmgr, flagParams.ConfigValues); err != nil {
227227
log.Fatal().Msgf("Error loading settings - %v", err)
228228
}
229-
log.Info().Msg("Loading service metrics")
230-
231229
// Start SAML Middleware if we are using SAML
232230
if flagParams.ConfigValues.Auth == config.AuthSAML {
233231
log.Debug().Msg("SAML enabled for authentication")
@@ -248,7 +246,6 @@ func osctrlAdminService() {
248246
log.Fatal().Msgf("Can not initialize SAML Middleware %s", err)
249247
}
250248
}
251-
252249
// FIXME Redis cache - Ticker to cleanup sessions
253250
// FIXME splay this?
254251
log.Info().Msg("Initialize cleanup sessions")
@@ -263,7 +260,6 @@ func osctrlAdminService() {
263260
time.Sleep(time.Duration(_t) * time.Second)
264261
}
265262
}()
266-
267263
// Goroutine to cleanup expired queries and carves
268264
log.Info().Msg("Initialize cleanup queries/carves")
269265
go func() {
@@ -294,7 +290,6 @@ func osctrlAdminService() {
294290
time.Sleep(time.Duration(_t) * time.Second)
295291
}
296292
}()
297-
298293
var loggerDBConfig *backend.JSONConfigurationDB
299294
// Set the logger configuration file if we have a DB logger
300295
if flagParams.ConfigValues.Logger == config.LoggingDB {
@@ -303,13 +298,16 @@ func osctrlAdminService() {
303298
loggerDBConfig = &flagParams.DBConfigValues
304299
}
305300
}
306-
307301
// Initialize audit log manager
302+
if flagParams.AuditLog {
303+
log.Info().Msg("Initialize audit log (enabled)")
304+
} else {
305+
log.Info().Msg("Initialize audit log (disabled)")
306+
}
308307
auditLog, err = auditlog.CreateAuditLogManager(db.Conn, serviceName, flagParams.AuditLog)
309308
if err != nil {
310309
log.Fatal().Msgf("Error initializing audit log manager - %v", err)
311310
}
312-
313311
// Initialize Admin handlers before router
314312
log.Info().Msg("Initializing handlers")
315313
handlersAdmin = handlers.CreateHandlersAdmin(
@@ -339,12 +337,10 @@ func osctrlAdminService() {
339337
handlers.WithDBLogger(flagParams.LoggerFile, loggerDBConfig),
340338
handlers.WithDebugHTTP(&flagParams.DebugHTTPValues),
341339
)
342-
343340
// ////////////////////////// ADMIN
344341
log.Info().Msg("Initializing router")
345342
// Create router for admin
346343
adminMux := http.NewServeMux()
347-
348344
// ///////////////////////// UNAUTHENTICATED CONTENT
349345
// Admin: login only if local auth is enabled
350346
if flagParams.ConfigValues.Auth != config.AuthNone && flagParams.ConfigValues.Auth != config.AuthSAML {

cmd/api/handlers/audit.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package handlers
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"strings"
7+
8+
"github.com/jmpsec/osctrl/pkg/auditlog"
9+
"github.com/jmpsec/osctrl/pkg/users"
10+
"github.com/jmpsec/osctrl/pkg/utils"
11+
"github.com/rs/zerolog/log"
12+
)
13+
14+
// AuditLogsHandler - GET Handler for all audit logs
15+
func (h *HandlersApi) AuditLogsHandler(w http.ResponseWriter, r *http.Request) {
16+
// Debug HTTP if enabled
17+
if h.DebugHTTPConfig.Enabled {
18+
utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody)
19+
}
20+
// Get context data and check access
21+
ctx := r.Context().Value(ContextKey(contextAPI)).(ContextValue)
22+
if !h.Users.CheckPermissions(ctx[ctxUser], users.AdminLevel, users.NoEnvironment) {
23+
apiErrorResponse(w, "no access", http.StatusForbidden, fmt.Errorf("attempt to use API by user %s", ctx[ctxUser]))
24+
return
25+
}
26+
// Get audit logs
27+
auditLogs, err := h.AuditLog.GetAll()
28+
if err != nil {
29+
log.Err(err).Msg("error getting audit logs")
30+
return
31+
}
32+
// Serialize and serve JSON
33+
log.Debug().Msgf("Returned %d audit log entries", len(auditLogs))
34+
h.AuditLog.Visit(ctx[ctxUser], r.URL.Path, strings.Split(r.RemoteAddr, ":")[0], auditlog.NoEnvironment)
35+
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, auditLogs)
36+
}

cmd/api/handlers/carves.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"fmt"
66
"net/http"
7+
"strings"
78
"time"
89

910
"github.com/jmpsec/osctrl/pkg/carves"
@@ -58,6 +59,7 @@ func (h *HandlersApi) CarveShowHandler(w http.ResponseWriter, r *http.Request) {
5859
}
5960
// Serialize and serve JSON
6061
log.Debug().Msgf("Returned carve %s", name)
62+
h.AuditLog.Visit(ctx[ctxUser], r.URL.Path, strings.Split(r.RemoteAddr, ":")[0], env.ID)
6163
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, carve)
6264
}
6365

@@ -108,6 +110,7 @@ func (h *HandlersApi) CarveQueriesHandler(w http.ResponseWriter, r *http.Request
108110
}
109111
// Serialize and serve JSON
110112
log.Debug().Msgf("Returned %d carves", len(carves))
113+
h.AuditLog.Visit(ctx[ctxUser], r.URL.Path, strings.Split(r.RemoteAddr, ":")[0], env.ID)
111114
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, carves)
112115
}
113116

@@ -147,6 +150,7 @@ func (h *HandlersApi) CarveListHandler(w http.ResponseWriter, r *http.Request) {
147150
}
148151
// Serialize and serve JSON
149152
log.Debug().Msgf("Returned %d carves", len(carves))
153+
h.AuditLog.Visit(ctx[ctxUser], r.URL.Path, strings.Split(r.RemoteAddr, ":")[0], env.ID)
150154
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, carves)
151155
}
152156

@@ -239,6 +243,7 @@ func (h *HandlersApi) CarvesRunHandler(w http.ResponseWriter, r *http.Request) {
239243
}
240244
// Return query name as serialized response
241245
log.Debug().Msgf("Created query %s", newQuery.Name)
246+
h.AuditLog.NewCarve(ctx[ctxUser], newQuery.Path, strings.Split(r.RemoteAddr, ":")[0], env.ID)
242247
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, types.ApiQueriesResponse{Name: newQuery.Name})
243248
}
244249

@@ -306,5 +311,6 @@ func (h *HandlersApi) CarvesActionHandler(w http.ResponseWriter, r *http.Request
306311
}
307312
// Return message as serialized response
308313
log.Debug().Msgf("%s", msgReturn)
314+
h.AuditLog.CarveAction(ctx[ctxUser], actionVar+" carve "+nameVar, strings.Split(r.RemoteAddr, ":")[0], env.ID)
309315
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, types.ApiGenericResponse{Message: msgReturn})
310316
}

cmd/api/handlers/environments.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"encoding/json"
55
"fmt"
66
"net/http"
7+
"strings"
78

9+
"github.com/jmpsec/osctrl/pkg/auditlog"
810
"github.com/jmpsec/osctrl/pkg/environments"
911
"github.com/jmpsec/osctrl/pkg/settings"
1012
"github.com/jmpsec/osctrl/pkg/types"
@@ -52,6 +54,7 @@ func (h *HandlersApi) EnvironmentHandler(w http.ResponseWriter, r *http.Request)
5254
}
5355
// Serialize and serve JSON
5456
log.Debug().Msgf("Returned environment %s", env.Name)
57+
h.AuditLog.Visit(ctx[ctxUser], r.URL.Path, strings.Split(r.RemoteAddr, ":")[0], env.ID)
5558
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, env)
5659
}
5760

@@ -95,6 +98,7 @@ func (h *HandlersApi) EnvironmentMapHandler(w http.ResponseWriter, r *http.Reque
9598
}
9699
// Serialize and serve JSON
97100
log.Debug().Msg("Returned environments map")
101+
h.AuditLog.Visit(ctx[ctxUser], r.URL.Path, strings.Split(r.RemoteAddr, ":")[0], auditlog.NoEnvironment)
98102
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, envMap)
99103
}
100104

@@ -118,6 +122,7 @@ func (h *HandlersApi) EnvironmentsHandler(w http.ResponseWriter, r *http.Request
118122
}
119123
// Serialize and serve JSON
120124
log.Debug().Msg("Returned environments")
125+
h.AuditLog.Visit(ctx[ctxUser], r.URL.Path, strings.Split(r.RemoteAddr, ":")[0], auditlog.NoEnvironment)
121126
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, envAll)
122127
}
123128

@@ -181,6 +186,7 @@ func (h *HandlersApi) EnvEnrollHandler(w http.ResponseWriter, r *http.Request) {
181186
}
182187
// Serialize and serve JSON
183188
log.Debug().Msgf("Returned data for environment%s : %s", env.Name, returnData)
189+
h.AuditLog.Visit(ctx[ctxUser], r.URL.Path, strings.Split(r.RemoteAddr, ":")[0], env.ID)
184190
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, types.ApiDataResponse{Data: returnData})
185191
}
186192

@@ -238,6 +244,7 @@ func (h *HandlersApi) EnvRemoveHandler(w http.ResponseWriter, r *http.Request) {
238244
}
239245
// Serialize and serve JSON
240246
log.Debug().Msgf("Returned data for environment %s : %s", env.Name, returnData)
247+
h.AuditLog.Visit(ctx[ctxUser], r.URL.Path, strings.Split(r.RemoteAddr, ":")[0], env.ID)
241248
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, types.ApiDataResponse{Data: returnData})
242249
}
243250

@@ -337,6 +344,7 @@ func (h *HandlersApi) EnvEnrollActionsHandler(w http.ResponseWriter, r *http.Req
337344
}
338345
// Return query name as serialized response
339346
log.Debug().Msgf("Returned data for environment %s : %s", env.Name, msgReturn)
347+
h.AuditLog.EnvAction(ctx[ctxUser], actionVar+" enrollment for environment "+env.Name, strings.Split(r.RemoteAddr, ":")[0], env.ID)
340348
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, types.ApiGenericResponse{Message: msgReturn})
341349
}
342350

@@ -411,5 +419,6 @@ func (h *HandlersApi) EnvRemoveActionsHandler(w http.ResponseWriter, r *http.Req
411419
}
412420
// Return query name as serialized response
413421
log.Debug().Msgf("Returned data for environment %s : %s", env.Name, msgReturn)
422+
h.AuditLog.EnvAction(ctx[ctxUser], actionVar+" removal for environment "+env.Name, strings.Split(r.RemoteAddr, ":")[0], env.ID)
414423
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, types.ApiGenericResponse{Message: msgReturn})
415424
}

cmd/api/handlers/handlers.go

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

33
import (
4+
"github.com/jmpsec/osctrl/pkg/auditlog"
45
"github.com/jmpsec/osctrl/pkg/cache"
56
"github.com/jmpsec/osctrl/pkg/carves"
67
"github.com/jmpsec/osctrl/pkg/config"
@@ -31,6 +32,7 @@ type HandlersApi struct {
3132
RedisCache *cache.RedisManager
3233
ServiceVersion string
3334
ServiceName string
35+
AuditLog *auditlog.AuditLogManager
3436
ApiConfig *config.JSONConfigurationService
3537
DebugHTTP *zerolog.Logger
3638
DebugHTTPConfig *config.DebugHTTPConfiguration
@@ -104,6 +106,12 @@ func WithName(name string) HandlersOption {
104106
}
105107
}
106108

109+
func WithAuditLog(auditLog *auditlog.AuditLogManager) HandlersOption {
110+
return func(h *HandlersApi) {
111+
h.AuditLog = auditLog
112+
}
113+
}
114+
107115
func WithDebugHTTP(cfg *config.DebugHTTPConfiguration) HandlersOption {
108116
return func(h *HandlersApi) {
109117
h.DebugHTTPConfig = cfg

cmd/api/handlers/login.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"github.com/jmpsec/osctrl/pkg/types"
99
"github.com/jmpsec/osctrl/pkg/users"
1010
"github.com/jmpsec/osctrl/pkg/utils"
11-
"github.com/rs/zerolog/log"
1211
)
1312

1413
// LoginHandler - POST Handler for API login request
@@ -59,7 +58,7 @@ func (h *HandlersApi) LoginHandler(w http.ResponseWriter, r *http.Request) {
5958
}
6059
user.APIToken = token
6160
}
61+
h.AuditLog.NewLogin(l.Username, r.RemoteAddr)
6262
// Serialize and serve JSON
63-
log.Debug().Msgf("Returning token for %s", user.Username)
6463
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, types.ApiLoginResponse{Token: user.APIToken})
6564
}

cmd/api/handlers/nodes.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"fmt"
66
"net/http"
7+
"strings"
78

89
"github.com/jmpsec/osctrl/pkg/nodes"
910
"github.com/jmpsec/osctrl/pkg/types"
@@ -53,8 +54,9 @@ func (h *HandlersApi) NodeHandler(w http.ResponseWriter, r *http.Request) {
5354
}
5455
return
5556
}
56-
// Serialize and serve JSON
5757
log.Debug().Msgf("Returned node %s", nodeVar)
58+
h.AuditLog.NodeAction(ctx[ctxUser], "viewed node "+nodeVar, strings.Split(r.RemoteAddr, ":")[0], env.ID)
59+
// Serialize and serve JSON
5860
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, node)
5961
}
6062

@@ -93,7 +95,8 @@ func (h *HandlersApi) ActiveNodesHandler(w http.ResponseWriter, r *http.Request)
9395
return
9496
}
9597
// Serialize and serve JSON
96-
log.Debug().Msg("Returned nodes")
98+
log.Debug().Msg("Returned active nodes")
99+
h.AuditLog.NodeAction(ctx[ctxUser], "viewed active nodes", strings.Split(r.RemoteAddr, ":")[0], env.ID)
97100
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, nodes)
98101
}
99102

@@ -132,7 +135,8 @@ func (h *HandlersApi) InactiveNodesHandler(w http.ResponseWriter, r *http.Reques
132135
return
133136
}
134137
// Serialize and serve JSON
135-
log.Debug().Msg("Returned nodes")
138+
log.Debug().Msg("Returned inactive nodes")
139+
h.AuditLog.NodeAction(ctx[ctxUser], "viewed inactive nodes", strings.Split(r.RemoteAddr, ":")[0], env.ID)
136140
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, nodes)
137141
}
138142

@@ -171,7 +175,8 @@ func (h *HandlersApi) AllNodesHandler(w http.ResponseWriter, r *http.Request) {
171175
return
172176
}
173177
// Serialize and serve JSON
174-
log.Debug().Msg("Returned nodes")
178+
log.Debug().Msg("Returned all nodes")
179+
h.AuditLog.NodeAction(ctx[ctxUser], "viewed all nodes", strings.Split(r.RemoteAddr, ":")[0], env.ID)
175180
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, nodes)
176181
}
177182

@@ -213,8 +218,9 @@ func (h *HandlersApi) DeleteNodeHandler(w http.ResponseWriter, r *http.Request)
213218
}
214219
return
215220
}
221+
log.Debug().Msgf("Deleted node %s", n.UUID)
222+
h.AuditLog.NodeAction(ctx[ctxUser], "deleted node "+n.UUID, strings.Split(r.RemoteAddr, ":")[0], env.ID)
216223
// Serialize and serve JSON
217-
log.Debug().Msgf("Returned node %s", n.UUID)
218224
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, types.ApiGenericResponse{Message: "node deleted"})
219225
}
220226

@@ -262,8 +268,9 @@ func (h *HandlersApi) TagNodeHandler(w http.ResponseWriter, r *http.Request) {
262268
apiErrorResponse(w, "error tagging node", http.StatusInternalServerError, err)
263269
return
264270
}
265-
// Serialize and serve JSON
266271
log.Debug().Msgf("Tagged node %s with %s", n.UUID, t.Tag)
272+
h.AuditLog.NodeAction(ctx[ctxUser], "tagged node "+n.UUID+" with "+t.Tag, strings.Split(r.RemoteAddr, ":")[0], env.ID)
273+
// Serialize and serve JSON
267274
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, types.ApiGenericResponse{Message: "node tagged"})
268275
}
269276

@@ -298,6 +305,8 @@ func (h *HandlersApi) LookupNodeHandler(w http.ResponseWriter, r *http.Request)
298305
}
299306
return
300307
}
308+
log.Debug().Msgf("Looked up node %s", l.Identifier)
309+
h.AuditLog.NodeAction(ctx[ctxUser], "looked up node "+l.Identifier, strings.Split(r.RemoteAddr, ":")[0], n.ID)
301310
// Serialize and serve JSON
302311
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, n)
303312
}

cmd/api/handlers/platforms.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package handlers
33
import (
44
"fmt"
55
"net/http"
6+
"strings"
67

8+
"github.com/jmpsec/osctrl/pkg/auditlog"
79
"github.com/jmpsec/osctrl/pkg/users"
810
"github.com/jmpsec/osctrl/pkg/utils"
911
"github.com/rs/zerolog/log"
@@ -29,6 +31,7 @@ func (h *HandlersApi) PlatformsHandler(w http.ResponseWriter, r *http.Request) {
2931
}
3032
// Serialize and serve JSON
3133
log.Debug().Msg("Returned platforms")
34+
h.AuditLog.Visit(ctx[ctxUser], r.URL.Path, strings.Split(r.RemoteAddr, ":")[0], auditlog.NoEnvironment)
3235
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, platforms)
3336
}
3437

@@ -68,5 +71,6 @@ func (h *HandlersApi) PlatformsEnvHandler(w http.ResponseWriter, r *http.Request
6871
}
6972
// Serialize and serve JSON
7073
log.Debug().Msg("Returned platforms")
74+
h.AuditLog.Visit(ctx[ctxUser], r.URL.Path, strings.Split(r.RemoteAddr, ":")[0], env.ID)
7175
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, platforms)
7276
}

0 commit comments

Comments
 (0)