forked from siderolabs/omni
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This PR implements audit logs. To enable it you have to set the `--audit-log-dir` flag to a directory where the audit logs will be stored. The audit logs are stored in a JSON format. Example: ```json {"event_type":"update","resource_type":"PublicKeys.omni.sidero.dev","event_ts":1722537710182,"event_data":{"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36","ip_address":"<snip>","user_id":"a19a7a38-1793-4262-a9ef-97bc00c7a155","role":"Admin","email":"[email protected]","confirmation_type":"auth0","fingerprint":"15acb974f769bdccd38a4b28f282b78736b80bc7","public_key_expiration":1722565909}} ``` Keep in mind that `event_ts` are in milliseconds instead of seconds. Field `event_data` contains all relevant information about the event. To enabled it in the development environment you will have to add the `--audit-log-dir /tmp/omni-data/audit-logs` line to `docker-compose.override.yml` or run `generate-certs` again. For siderolabs#37 Signed-off-by: Dmitriy Matrenichev <[email protected]>
- Loading branch information
Showing
22 changed files
with
737 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
// Copyright (c) 2024 Sidero Labs, Inc. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the LICENSE file. | ||
|
||
package audit | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"sync" | ||
|
||
"github.com/cosi-project/runtime/pkg/resource" | ||
"github.com/siderolabs/gen/pair" | ||
) | ||
|
||
// Check is a function that checks if the event is allowed. | ||
type Check = func(ctx context.Context, eventType EventType, args ...any) bool | ||
|
||
// Gate is a gate that checks if the event is allowed. | ||
// | ||
//nolint:govet | ||
type Gate struct { | ||
mu sync.RWMutex | ||
fns [10]map[resource.Type]Check | ||
} | ||
|
||
// Check checks if the event is allowed. | ||
func (g *Gate) Check(ctx context.Context, eventType EventType, typ resource.Type, args ...any) bool { | ||
g.mu.RLock() | ||
|
||
if g.fns[0] == nil { | ||
g.mu.RUnlock() | ||
|
||
return false | ||
} | ||
|
||
var fn Check | ||
|
||
switch { | ||
case eventType&EventGet != 0: | ||
fn = g.fns[0][typ] | ||
case eventType&EventList != 0: | ||
fn = g.fns[1][typ] | ||
case eventType&EventCreate != 0: | ||
fn = g.fns[2][typ] | ||
case eventType&EventUpdate != 0: | ||
fn = g.fns[3][typ] | ||
case eventType&EventDestroy != 0: | ||
fn = g.fns[4][typ] | ||
case eventType&EventWatch != 0: | ||
fn = g.fns[5][typ] | ||
case eventType&EventWatchKind != 0: | ||
fn = g.fns[6][typ] | ||
case eventType&EventWatchKindAggregated != 0: | ||
fn = g.fns[7][typ] | ||
case eventType&EventUpdateWithConflicts != 0: | ||
fn = g.fns[8][typ] | ||
case eventType&EventWatchFor != 0: | ||
fn = g.fns[9][typ] | ||
} | ||
|
||
g.mu.RUnlock() | ||
|
||
if fn == nil { | ||
return false | ||
} | ||
|
||
return fn(ctx, eventType, args...) | ||
} | ||
|
||
// AddChecks adds checks for the event type. | ||
func (g *Gate) AddChecks(eventType EventType, pairs []pair.Pair[resource.Type, Check]) { | ||
g.mu.Lock() | ||
defer g.mu.Unlock() | ||
|
||
if g.fns[0] == nil { | ||
for i := range g.fns { | ||
g.fns[i] = map[resource.Type]Check{} | ||
} | ||
} | ||
|
||
for _, p := range pairs { | ||
g.addCheck(eventType, p) | ||
} | ||
} | ||
|
||
func (g *Gate) addCheck(eventType EventType, p pair.Pair[resource.Type, Check]) { | ||
slc := []EventType{ | ||
EventGet, | ||
EventList, | ||
EventCreate, | ||
EventUpdate, | ||
EventDestroy, | ||
EventWatch, | ||
EventWatchKind, | ||
EventWatchKindAggregated, | ||
EventUpdateWithConflicts, | ||
EventWatchFor, | ||
} | ||
|
||
for i, e := range slc { | ||
if eventType&e != 0 { | ||
if _, ok := g.fns[i][p.F1]; ok { | ||
panic("duplicate check") | ||
} | ||
|
||
g.fns[i][p.F1] = p.F2 | ||
} | ||
} | ||
} | ||
|
||
// AllowAll is a check that allows all events for certain event type. | ||
func AllowAll(context.Context, EventType, ...any) bool { | ||
return true | ||
} | ||
|
||
const ( | ||
// EventGet is the get event type. | ||
EventGet EventType = 1 << iota | ||
// EventList is the list event type. | ||
EventList | ||
// EventCreate is the create event type. | ||
EventCreate | ||
// EventUpdate is the update event type. | ||
EventUpdate | ||
// EventDestroy is the destroy event type. | ||
EventDestroy | ||
// EventWatch is the watch event type. | ||
EventWatch | ||
// EventWatchKind is the watch kind event type. | ||
EventWatchKind | ||
// EventWatchKindAggregated is the watch kind aggregated event type. | ||
EventWatchKindAggregated | ||
// EventUpdateWithConflicts is the update with conflicts event type. | ||
EventUpdateWithConflicts | ||
// EventWatchFor is the watch for event type. | ||
EventWatchFor | ||
) | ||
|
||
// EventType represents the type of event. | ||
type EventType int | ||
|
||
// MarshalJSON marshals the event type to JSON. | ||
func (e *EventType) MarshalJSON() ([]byte, error) { | ||
return []byte(`"` + e.String() + `"`), nil | ||
} | ||
|
||
// UnmarshalJSON unmarshals the event type from JSON. | ||
func (e *EventType) UnmarshalJSON(bytes []byte) error { | ||
switch string(bytes) { | ||
case `"get"`: | ||
*e = EventGet | ||
case `"list"`: | ||
*e = EventList | ||
case `"create"`: | ||
*e = EventCreate | ||
case `"update"`: | ||
*e = EventUpdate | ||
case `"destroy"`: | ||
*e = EventDestroy | ||
case `"watch"`: | ||
*e = EventWatch | ||
case `"watch_kind"`: | ||
*e = EventWatchKind | ||
case `"watch_kind_aggregated"`: | ||
*e = EventWatchKindAggregated | ||
case `"update_with_conflicts"`: | ||
*e = EventUpdateWithConflicts | ||
case `"watch_for"`: | ||
*e = EventWatchFor | ||
default: | ||
return fmt.Errorf("unknown event type: %s", bytes) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// String returns the string representation of the event type. | ||
func (e *EventType) String() string { | ||
switch *e { | ||
case EventGet: | ||
return "get" | ||
case EventList: | ||
return "list" | ||
case EventCreate: | ||
return "create" | ||
case EventUpdate: | ||
return "update" | ||
case EventDestroy: | ||
return "destroy" | ||
case EventWatch: | ||
return "watch" | ||
case EventWatchKind: | ||
return "watch_kind" | ||
case EventWatchKindAggregated: | ||
return "watch_kind_aggregated" | ||
case EventUpdateWithConflicts: | ||
return "update_with_conflicts" | ||
case EventWatchFor: | ||
return "watch_for" | ||
default: | ||
return "<unknown>" | ||
} | ||
} |
Oops, something went wrong.