Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ type BFLADetector interface {
ApproveTrace(path, method string, clientRef *k8straceannotator.K8sObjectRef, apiID uint, user *DetectedUser)
DenyTrace(path, method string, clientRef *k8straceannotator.K8sObjectRef, apiID uint, user *DetectedUser)

ResetLearning(apiID uint, numberOfTraces int)
StartLearning(apiID uint, numberOfTraces int)
StopLearning(apiID uint)
ResetLearning(apiID uint, numberOfTraces int) error
StartLearning(apiID uint, numberOfTraces int) error
StopLearning(apiID uint) error

ProvideAuthzModel(apiID uint, am AuthorizationModel)
}
Expand All @@ -101,18 +101,36 @@ type apiInfoProvider interface {

type Command interface{ isCommand() }

type CommandWithError interface {
Command

Close()
SendError(err error)
RcvError() error
}

type ErrorChan chan error

func NewErrorChan() ErrorChan { return make(chan error, 1) }
func (e ErrorChan) SendError(err error) { e <- err }
func (e ErrorChan) Close() { close(e) }
func (e ErrorChan) RcvError() error { return <-e }

type StopLearningCommand struct {
apiID uint
ErrorChan
}

type StartLearningCommand struct {
apiID uint
numberOfTraces int
ErrorChan
}

type ResetLearningCommand struct {
apiID uint
numberOfTraces int
ErrorChan
}

type MarkLegitimateCommand struct {
Expand Down Expand Up @@ -156,7 +174,7 @@ type EventAlerter interface {

type learnAndDetectBFLA struct {
tracesCh chan *CompositeTrace
commandsCh chan Command
commandsCh CommandsChan
errCh chan error
apiInfoProvider apiInfoProvider

Expand All @@ -171,6 +189,18 @@ type learnAndDetectBFLA struct {
mu *sync.RWMutex
}

type CommandsChan chan Command

func (c CommandsChan) Send(cmd Command) {
c <- cmd
}

func (c CommandsChan) SendAndReplyErr(cmd CommandWithError) error {
defer cmd.Close()
c <- cmd
return cmd.RcvError()
}

type CompositeTrace struct {
*core.Event

Expand All @@ -189,9 +219,13 @@ func (l *learnAndDetectBFLA) run(ctx context.Context) {

for {
select {
case feedback, ok := <-l.commandsCh:
case command, ok := <-l.commandsCh:
if ok {
if err := l.commandsRunner(ctx, feedback); err != nil {
err := l.commandsRunner(ctx, command)
if cmdErr, ok := command.(CommandWithError); ok {
cmdErr.SendError(err)
}
if err != nil {
l.errCh <- err
}
continue
Expand Down Expand Up @@ -411,6 +445,8 @@ func (l *learnAndDetectBFLA) traceRunner(ctx context.Context, trace *CompositeTr
if err := l.eventAlerter.SetEventAlert(ctx, ModuleName, trace.APIEvent.ID, severity); err != nil {
return fmt.Errorf("unable to set alert annotation: %w", err)
}

l.logError(l.notifyController(ctx, trace.APIEvent.APIInfoID))
aud.WarningStatus = ResolveBFLAStatusInt(int(trace.APIEvent.StatusCode))
}
aud.StatusCode = trace.APIEvent.StatusCode
Expand Down Expand Up @@ -552,6 +588,8 @@ func (l *learnAndDetectBFLA) updateAuthorizationModel(tags []*models.SpecTag, pa
}

func (l *learnAndDetectBFLA) IsLearning(apiID uint) bool {
l.mu.Lock()
defer l.mu.Unlock()
_, ok := l.mustLearn(apiID)
return ok
}
Expand Down Expand Up @@ -601,58 +639,59 @@ func (l *learnAndDetectBFLA) SendTrace(trace *CompositeTrace) {
}

func (l *learnAndDetectBFLA) ApproveTrace(path, method string, clientRef *k8straceannotator.K8sObjectRef, apiID uint, user *DetectedUser) {
l.commandsCh <- &MarkLegitimateCommand{
l.commandsCh.Send(&MarkLegitimateCommand{
detectedUser: user,
path: path,
method: method,
clientRef: clientRef,
apiID: apiID,
}
})
}

func (l *learnAndDetectBFLA) DenyTrace(path, method string, clientRef *k8straceannotator.K8sObjectRef, apiID uint, user *DetectedUser) {
l.commandsCh <- &MarkIllegitimateCommand{
l.commandsCh.Send(&MarkIllegitimateCommand{
detectedUser: user,
path: path,
method: method,
clientRef: clientRef,
apiID: apiID,
}
})
}

func (l *learnAndDetectBFLA) ResetLearning(apiID uint, numberOfTraces int) {
func (l *learnAndDetectBFLA) ResetLearning(apiID uint, numberOfTraces int) error {
if numberOfTraces < -1 {
log.Errorf("value %v not allowed", numberOfTraces)
return
return fmt.Errorf("value %v not allowed", numberOfTraces)
}
l.commandsCh <- &ResetLearningCommand{
return l.commandsCh.SendAndReplyErr(&ResetLearningCommand{
apiID: apiID,
numberOfTraces: numberOfTraces,
}
ErrorChan: NewErrorChan(),
})
}

func (l *learnAndDetectBFLA) StopLearning(apiID uint) {
l.commandsCh <- &StopLearningCommand{
apiID: apiID,
}
func (l *learnAndDetectBFLA) StopLearning(apiID uint) error {
return l.commandsCh.SendAndReplyErr(&StopLearningCommand{
apiID: apiID,
ErrorChan: NewErrorChan(),
})
}

func (l *learnAndDetectBFLA) StartLearning(apiID uint, numberOfTraces int) {
func (l *learnAndDetectBFLA) StartLearning(apiID uint, numberOfTraces int) error {
if numberOfTraces < -1 {
log.Errorf("value %v not allowed", numberOfTraces)
return
return fmt.Errorf("value %v not allowed", numberOfTraces)
}
l.commandsCh <- &StartLearningCommand{
return l.commandsCh.SendAndReplyErr(&StartLearningCommand{
apiID: apiID,
numberOfTraces: numberOfTraces,
}
ErrorChan: NewErrorChan(),
})
}

func (l *learnAndDetectBFLA) ProvideAuthzModel(apiID uint, am AuthorizationModel) {
l.commandsCh <- &ProvideAuthzModelCommand{
l.commandsCh.Send(&ProvideAuthzModelCommand{
apiID: apiID,
authzModel: am,
}
})
}

func (l *learnAndDetectBFLA) ctrlNotifier(ctx context.Context) {
Expand Down
102 changes: 44 additions & 58 deletions backend/pkg/modules/internal/bfla/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,114 +430,105 @@ func ToRestapiAuthorizationModel(am *bfladetector.AuthorizationModel) *restapi.A

// nolint:stylecheck,revive
func (h httpHandler) PutAuthorizationModelApiIDApprove(w http.ResponseWriter, r *http.Request, apiID oapicommon.ApiID, params restapi.PutAuthorizationModelApiIDApproveParams) {
done := make(chan struct{})
ctx := r.Context()
clientRef := &k8straceannotator.K8sObjectRef{Uid: params.K8sClientUid} // TODO this looks wrong.
go func() {
log.Infof("approve operation on api=%d path=%s method=%s ", apiID, params.Path, params.Method)
h.bflaDetector.ApproveTrace(params.Path, strings.ToUpper(params.Method), clientRef, uint(apiID), nil)
done <- struct{}{}
}()

select {
case <-ctx.Done():
err := ctx.Err()
log.Error(err)
httpResponse(w, http.StatusInternalServerError, &oapicommon.ApiResponse{Message: err.Error()})
case <-done:
default:
log.Infof("approve operation on api=%d path=%s method=%s ", apiID, params.Path, params.Method)
h.bflaDetector.ApproveTrace(params.Path, strings.ToUpper(params.Method), clientRef, uint(apiID), nil)
log.Infof("approve applied successfully on api=%d path=%s method=%s ", apiID, params.Path, params.Method)
httpResponse(w, http.StatusOK, &oapicommon.ApiResponse{Message: "Requested approve operation on api event"})
}
}

// nolint:stylecheck,revive
func (h httpHandler) PutAuthorizationModelApiIDDeny(w http.ResponseWriter, r *http.Request, apiID oapicommon.ApiID, params restapi.PutAuthorizationModelApiIDDenyParams) {
done := make(chan struct{})
ctx := r.Context()
clientRef := &k8straceannotator.K8sObjectRef{Uid: params.K8sClientUid} // TODO this looks wrong.
go func() {
log.Infof("deny operation on api=%d path=%s method=%s ", apiID, params.Path, params.Method)
h.bflaDetector.DenyTrace(params.Path, strings.ToUpper(params.Method), clientRef, uint(apiID), nil)
done <- struct{}{}
}()

select {
case <-ctx.Done():
err := ctx.Err()
log.Error(err)
httpResponse(w, http.StatusInternalServerError, &oapicommon.ApiResponse{Message: err.Error()})
case <-done:
default:
log.Infof("deny operation on api=%d path=%s method=%s ", apiID, params.Path, params.Method)
h.bflaDetector.DenyTrace(params.Path, strings.ToUpper(params.Method), clientRef, uint(apiID), nil)
log.Infof("deny applied successfully on api=%d path=%s method=%s ", apiID, params.Path, params.Method)
httpResponse(w, http.StatusOK, &oapicommon.ApiResponse{Message: "Reqested deny operation on api event"})
}
}

// nolint:stylecheck,revive
func (h httpHandler) PutAuthorizationModelApiIDLearningReset(w http.ResponseWriter, r *http.Request, apiID oapicommon.ApiID, params restapi.PutAuthorizationModelApiIDLearningResetParams) {
done := make(chan struct{})
ctx := r.Context()
go func() {
log.Infof("reset learning api=%d", apiID)
if params.NrTraces == nil {
h.bflaDetector.ResetLearning(uint(apiID), -1)
} else {
h.bflaDetector.ResetLearning(uint(apiID), *params.NrTraces)
}
done <- struct{}{}
}()

select {
case <-ctx.Done():
err := ctx.Err()
log.Error(err)
httpResponse(w, http.StatusInternalServerError, &oapicommon.ApiResponse{Message: err.Error()})
case <-done:
default:
log.Infof("reset learning api=%d", apiID)
if params.NrTraces == nil {
if err := h.bflaDetector.ResetLearning(uint(apiID), -1); err != nil {
httpResponse(w, http.StatusBadRequest, &oapicommon.ApiResponse{Message: err.Error()})
return
}
} else {
if err := h.bflaDetector.ResetLearning(uint(apiID), *params.NrTraces); err != nil {
httpResponse(w, http.StatusBadRequest, &oapicommon.ApiResponse{Message: err.Error()})
return
}
}
log.Infof("reset learning applied successfully on api=%d", apiID)
httpResponse(w, http.StatusOK, &oapicommon.ApiResponse{Message: "Reqested reset learning operation on api event"})
}
}

// nolint:stylecheck,revive
func (h httpHandler) PutAuthorizationModelApiIDLearningStart(w http.ResponseWriter, r *http.Request, apiID oapicommon.ApiID, params restapi.PutAuthorizationModelApiIDLearningStartParams) {
done := make(chan struct{})
ctx := r.Context()
go func() {
log.Infof("start learning api=%d", apiID)
if params.NrTraces == nil {
h.bflaDetector.StartLearning(uint(apiID), -1)
} else {
h.bflaDetector.StartLearning(uint(apiID), *params.NrTraces)
}
done <- struct{}{}
}()

select {
case <-ctx.Done():
err := ctx.Err()
log.Error(err)
httpResponse(w, http.StatusInternalServerError, &oapicommon.ApiResponse{Message: err.Error()})
case <-done:
default:
log.Infof("start learning api=%d", apiID)
if params.NrTraces == nil {
if err := h.bflaDetector.StartLearning(uint(apiID), -1); err != nil {
httpResponse(w, http.StatusBadRequest, &oapicommon.ApiResponse{Message: err.Error()})
return
}
} else {
if err := h.bflaDetector.StartLearning(uint(apiID), *params.NrTraces); err != nil {
httpResponse(w, http.StatusBadRequest, &oapicommon.ApiResponse{Message: err.Error()})
return
}
}
log.Infof("start learning applied successfully on api=%d", apiID)
httpResponse(w, http.StatusOK, &oapicommon.ApiResponse{Message: "Reqested start learning operation on api event"})
}
}

// nolint:stylecheck,revive
func (h httpHandler) PutAuthorizationModelApiIDLearningStop(w http.ResponseWriter, r *http.Request, apiID oapicommon.ApiID) {
done := make(chan struct{})
ctx := r.Context()
go func() {
log.Infof("stop learning api=%d", apiID)
h.bflaDetector.StopLearning(uint(apiID))
done <- struct{}{}
}()

select {
case <-ctx.Done():
err := ctx.Err()
log.Error(err)
httpResponse(w, http.StatusInternalServerError, &oapicommon.ApiResponse{Message: err.Error()})
case <-done:
default:
log.Infof("stop learning api=%d", apiID)
if err := h.bflaDetector.StopLearning(uint(apiID)); err != nil {
httpResponse(w, http.StatusBadRequest, &oapicommon.ApiResponse{Message: err.Error()})
return
}
log.Infof("stop learning applied successfully on api=%d", apiID)
httpResponse(w, http.StatusOK, &oapicommon.ApiResponse{Message: "Reqested stop learning operation on api event"})
}
Expand Down Expand Up @@ -574,9 +565,13 @@ func (h httpHandler) PutEventIdOperation(w http.ResponseWriter, r *http.Request,
})
return
}
done := make(chan struct{})
ctx := r.Context()
go func() {
select {
case <-ctx.Done():
err := ctx.Err()
log.Error(err)
httpResponse(w, http.StatusInternalServerError, &oapicommon.ApiResponse{Message: err.Error()})
default:
log.Infof("apply %s operation on trace=%d", operation, eventID)
tags, err := bfladetector.ParseSpecInfo(apiInfo)
if err != nil {
Expand All @@ -593,15 +588,6 @@ func (h httpHandler) PutEventIdOperation(w http.ResponseWriter, r *http.Request,
case restapi.DenyUser:
h.bflaDetector.DenyTrace(resolvedPath, string(apiEvent.Method), src, apiEvent.APIInfoID, user)
}
done <- struct{}{}
}()

select {
case <-ctx.Done():
err := ctx.Err()
log.Error(err)
httpResponse(w, http.StatusInternalServerError, &oapicommon.ApiResponse{Message: err.Error()})
case <-done:
log.Infof("%s operation applied successfully on trace=%d", operation, eventID)
httpResponse(w, http.StatusOK, &oapicommon.ApiResponse{Message: fmt.Sprintf("Reqested %s operation on api event", operation)})
}
Expand Down