From eaa302758baec558098d608a758e9326ff79cb68 Mon Sep 17 00:00:00 2001 From: Danilo Mendes Rocha Date: Mon, 7 Jul 2025 16:32:16 -0300 Subject: [PATCH] Enhance audit logging by including request details and IP addresses in audit entries --- oauthproxy.go | 6 ++-- pkg/pics/audit/audit_client.go | 51 +++++++++++++++++++++++++++------- pkg/pics/audit/audit_event.go | 13 +++++---- 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/oauthproxy.go b/oauthproxy.go index d45e638483..feb5391a85 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -827,14 +827,14 @@ func (p *OAuthProxy) backendLogout(rw http.ResponseWriter, req *http.Request, si if resp.StatusCode() != 200 { logger.Errorf("error while calling backend logout url, returned error code %v", resp.StatusCode()) } - p.picsAuditClient.CreateSuccessfulLogoutAuditEntry(session, req.RequestURI, req.Header.Get("edisp-org-id")) + p.picsAuditClient.CreateSuccessfulLogoutAuditEntry(session, req.RequestURI, req.Header.Get("edisp-org-id"), req) } else { if providerData.BackendRevokeAccessTokenURL != "" { err := PicsRevokeAccessToken(providerData.BackendRevokeAccessTokenURL, session.AccessToken, providerData.ClientID, providerData.ClientSecret) if err != nil { logger.Errorf("error while calling backend revoke access token: %v", err) } else { - p.picsAuditClient.CreateSuccessfulRevokeAccessTokenAuditEntry(session, req.RequestURI, req.Header.Get("edisp-org-id")) + p.picsAuditClient.CreateSuccessfulRevokeAccessTokenAuditEntry(session, req.RequestURI, req.Header.Get("edisp-org-id"), req) } } @@ -1010,7 +1010,7 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) { p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error()) return } - p.picsAuditClient.CreateSuccessfulLoginAuditEntry(session, appRedirect, req.Header.Get("edisp-org-id")) + p.picsAuditClient.CreateSuccessfulLoginAuditEntry(session, appRedirect, req.Header.Get("edisp-org-id"), req) http.Redirect(rw, req, appRedirect, http.StatusFound) } else { logger.PrintAuthf(session.Email, req, logger.AuthFailure, "Invalid authentication via OAuth2: unauthorized") diff --git a/pkg/pics/audit/audit_client.go b/pkg/pics/audit/audit_client.go index 3cc2c9d59a..e612f94a7b 100644 --- a/pkg/pics/audit/audit_client.go +++ b/pkg/pics/audit/audit_client.go @@ -5,6 +5,9 @@ import ( "errors" "fmt" "log" + "net" + "net/http" + "os" "strings" "time" @@ -52,34 +55,60 @@ func NewAuditClient(opts *ClientOpts) (*Client, error) { return &Client{enabled: opts.Enabled, apiSignature: apiSignature, client: client, opts: opts}, nil } -func (c *Client) CreateSuccessfulLoginAuditEntry(ss *sessions.SessionState, appURL string, tenantID string) { +func (c *Client) CreateSuccessfulLoginAuditEntry(ss *sessions.SessionState, appURL string, tenantID string, req *http.Request) { coding := Coding{ System: "http://hl7.org/fhir/ValueSet/audit-event-type", Version: "1", Code: "110114", Display: "User Authentication"} - c.createAuditEntry(ss, appURL, tenantID, "0", "Success", &coding) + c.createAuditEntry(ss, appURL, tenantID, "0", "Success", &coding, req) } -func (c *Client) CreateFailedLoginAuditEntry(ss *sessions.SessionState, appURL string, tenantID string, errorDesc string) { +func (c *Client) CreateFailedLoginAuditEntry(ss *sessions.SessionState, appURL string, tenantID string, errorDesc string, req *http.Request) { coding := Coding{ System: "http://hl7.org/fhir/ValueSet/audit-event-type", Version: "1", Code: "110114", Display: "User Authentication"} - c.createAuditEntry(ss, appURL, tenantID, "1", errorDesc, &coding) + c.createAuditEntry(ss, appURL, tenantID, "1", errorDesc, &coding, req) } -func (c *Client) CreateSuccessfulLogoutAuditEntry(ss *sessions.SessionState, appURL string, tenantID string) { +func (c *Client) CreateSuccessfulLogoutAuditEntry(ss *sessions.SessionState, appURL string, tenantID string, req *http.Request) { coding := Coding{ System: "http://hl7.org/fhir/ValueSet/audit-event-type", Version: "1", Code: "110123", Display: "User Logout All Sessions"} - c.createAuditEntry(ss, appURL, tenantID, "0", "Success", &coding) + c.createAuditEntry(ss, appURL, tenantID, "0", "Success", &coding, req) } -func (c *Client) CreateSuccessfulRevokeAccessTokenAuditEntry(ss *sessions.SessionState, appURL string, tenantID string) { +func (c *Client) CreateSuccessfulRevokeAccessTokenAuditEntry(ss *sessions.SessionState, appURL string, tenantID string, req *http.Request) { coding := Coding{ System: "http://hl7.org/fhir/ValueSet/audit-event-type", Version: "1", Code: "110123", Display: "User revoked access token"} - c.createAuditEntry(ss, appURL, tenantID, "0", "Success", &coding) + c.createAuditEntry(ss, appURL, tenantID, "0", "Success", &coding, req) } -func (c *Client) createAuditEntry(ss *sessions.SessionState, appURL string, tenantID string, outcomeCode string, outcomeDesc string, coding *Coding) { +func (c *Client) createAuditEntry(ss *sessions.SessionState, appURL string, tenantID string, outcomeCode string, outcomeDesc string, coding *Coding, req *http.Request) { if !c.enabled { return } + + // Service/component identifier + serviceIDs := []string{"oauth2proxy"} + var targetIP string + if hn, err := os.Hostname(); err == nil { + serviceIDs = append(serviceIDs, hn) + addrs, err := net.LookupHost(hn) + if err == nil && len(addrs) > 0 { + targetIP = addrs[0] + } else { + targetIP = hn + } + } else { + targetIP = "unknown" + } + + // Extract source IP address + sourceIP := "" + if req != nil { + sourceIP = req.RemoteAddr + if xff := req.Header.Get("X-Forwarded-For"); xff != "" { + ips := strings.Split(xff, ",") + sourceIP = strings.TrimSpace(ips[0]) + } + } + auditObject := RootEvent{ ResourceType: "AuditEvent", Event: &Event{ @@ -88,7 +117,6 @@ func (c *Client) createAuditEntry(ss *sessions.SessionState, appURL string, tena DateTime: time.Now().UTC().Format(time.RFC3339), Outcome: outcomeCode, OutcomeDesc: outcomeDesc}, - Participant: []*Participant{ {AltID: ss.User, UserID: UserID{Value: ss.Email}, Name: ss.PreferredUsername, Requestor: true}}, Source: Source{ @@ -133,6 +161,9 @@ func (c *Client) createAuditEntry(ss *sessions.SessionState, appURL string, tena }, }, }, + ServiceIDs: serviceIDs, + SourceIPAddress: sourceIP, + TargetIPAddress: targetIP, } auditMessage, err := json.Marshal(auditObject) diff --git a/pkg/pics/audit/audit_event.go b/pkg/pics/audit/audit_event.go index e4f11baf42..6d0a147484 100644 --- a/pkg/pics/audit/audit_event.go +++ b/pkg/pics/audit/audit_event.go @@ -110,9 +110,12 @@ type Event struct { } type RootEvent struct { - ResourceType string `json:"resourceType,omitempty"` - Event *Event `json:"event,omitempty"` - Participant []*Participant `json:"participant,omitempty"` - Source Source `json:"source,omitempty"` - Object []*Object `json:"object,omitempty"` + ResourceType string `json:"resourceType,omitempty"` + Event *Event `json:"event,omitempty"` + Participant []*Participant `json:"participant,omitempty"` + Source Source `json:"source,omitempty"` + Object []*Object `json:"object,omitempty"` + ServiceIDs []string `json:"service_ids,omitempty"` + SourceIPAddress string `json:"source_ip_address,omitempty"` + TargetIPAddress string `json:"target_ip_address,omitempty"` }