mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-02 03:27:54 +00:00
Allow plugins to submit audit requests/responses via extended SystemView (#6777)
Move audit.LogInput to sdk/logical. Allow the Data values in audited logical.Request and Response to implement OptMarshaler, in which case we delegate hashing/serializing responsibility to them. Add new ClientCertificateSerialNumber audit request field. SystemView can now be cast to ExtendedSystemView to expose the Auditor interface, which allows submitting requests and responses to the audit broker.
This commit is contained in:
130
audit/format.go
130
audit/format.go
@@ -2,6 +2,8 @@ package audit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
@@ -30,7 +32,7 @@ type AuditFormatter struct {
|
||||
|
||||
var _ Formatter = (*AuditFormatter)(nil)
|
||||
|
||||
func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config FormatterConfig, in *LogInput) error {
|
||||
func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config FormatterConfig, in *logical.LogInput) error {
|
||||
if in == nil || in.Request == nil {
|
||||
return fmt.Errorf("request to request-audit a nil request")
|
||||
}
|
||||
@@ -51,15 +53,19 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config
|
||||
// Set these to the input values at first
|
||||
auth := in.Auth
|
||||
req := in.Request
|
||||
var connState *tls.ConnectionState
|
||||
|
||||
if in.Request.Connection != nil && in.Request.Connection.ConnState != nil {
|
||||
connState = in.Request.Connection.ConnState
|
||||
}
|
||||
|
||||
if !config.Raw {
|
||||
// Before we copy the structure we must nil out some data
|
||||
// otherwise we will cause reflection to panic and die
|
||||
if in.Request.Connection != nil && in.Request.Connection.ConnState != nil {
|
||||
origState := in.Request.Connection.ConnState
|
||||
if connState != nil {
|
||||
in.Request.Connection.ConnState = nil
|
||||
defer func() {
|
||||
in.Request.Connection.ConnState = origState
|
||||
in.Request.Connection.ConnState = connState
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -77,6 +83,17 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config
|
||||
return err
|
||||
}
|
||||
req = cp.(*logical.Request)
|
||||
for k, v := range req.Data {
|
||||
if o, ok := v.(logical.OptMarshaler); ok {
|
||||
marshaled, err := o.MarshalJSONWithOptions(&logical.MarshalOptions{
|
||||
ValueHasher: salt.GetIdentifiedHMAC,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Data[k] = json.RawMessage(marshaled)
|
||||
}
|
||||
}
|
||||
|
||||
// Hash any sensitive information
|
||||
if auth != nil {
|
||||
@@ -120,8 +137,12 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config
|
||||
return err
|
||||
}
|
||||
|
||||
reqType := in.Type
|
||||
if reqType == "" {
|
||||
reqType = "request"
|
||||
}
|
||||
reqEntry := &AuditRequestEntry{
|
||||
Type: "request",
|
||||
Type: reqType,
|
||||
Error: errString,
|
||||
|
||||
Auth: AuditAuth{
|
||||
@@ -147,12 +168,13 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config
|
||||
ID: ns.ID,
|
||||
Path: ns.Path,
|
||||
},
|
||||
Path: req.Path,
|
||||
Data: req.Data,
|
||||
PolicyOverride: req.PolicyOverride,
|
||||
RemoteAddr: getRemoteAddr(req),
|
||||
ReplicationCluster: req.ReplicationCluster,
|
||||
Headers: req.Headers,
|
||||
Path: req.Path,
|
||||
Data: req.Data,
|
||||
PolicyOverride: req.PolicyOverride,
|
||||
RemoteAddr: getRemoteAddr(req),
|
||||
ReplicationCluster: req.ReplicationCluster,
|
||||
Headers: req.Headers,
|
||||
ClientCertificateSerialNumber: getClientCertificateSerialNumber(connState),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -167,7 +189,7 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config
|
||||
return f.AuditFormatWriter.WriteRequest(w, reqEntry)
|
||||
}
|
||||
|
||||
func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config FormatterConfig, in *LogInput) error {
|
||||
func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config FormatterConfig, in *logical.LogInput) error {
|
||||
if in == nil || in.Request == nil {
|
||||
return fmt.Errorf("request to response-audit a nil request")
|
||||
}
|
||||
@@ -189,15 +211,19 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
|
||||
auth := in.Auth
|
||||
req := in.Request
|
||||
resp := in.Response
|
||||
var connState *tls.ConnectionState
|
||||
|
||||
if in.Request.Connection != nil && in.Request.Connection.ConnState != nil {
|
||||
connState = in.Request.Connection.ConnState
|
||||
}
|
||||
|
||||
if !config.Raw {
|
||||
// Before we copy the structure we must nil out some data
|
||||
// otherwise we will cause reflection to panic and die
|
||||
if in.Request.Connection != nil && in.Request.Connection.ConnState != nil {
|
||||
origState := in.Request.Connection.ConnState
|
||||
if connState != nil {
|
||||
in.Request.Connection.ConnState = nil
|
||||
defer func() {
|
||||
in.Request.Connection.ConnState = origState
|
||||
in.Request.Connection.ConnState = connState
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -215,6 +241,17 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
|
||||
return err
|
||||
}
|
||||
req = cp.(*logical.Request)
|
||||
for k, v := range req.Data {
|
||||
if o, ok := v.(logical.OptMarshaler); ok {
|
||||
marshaled, err := o.MarshalJSONWithOptions(&logical.MarshalOptions{
|
||||
ValueHasher: salt.GetIdentifiedHMAC,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Data[k] = json.RawMessage(marshaled)
|
||||
}
|
||||
}
|
||||
|
||||
if in.Response != nil {
|
||||
cp, err := copystructure.Copy(in.Response)
|
||||
@@ -222,6 +259,17 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
|
||||
return err
|
||||
}
|
||||
resp = cp.(*logical.Response)
|
||||
for k, v := range resp.Data {
|
||||
if o, ok := v.(logical.OptMarshaler); ok {
|
||||
marshaled, err := o.MarshalJSONWithOptions(&logical.MarshalOptions{
|
||||
ValueHasher: salt.GetIdentifiedHMAC,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Data[k] = json.RawMessage(marshaled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hash any sensitive information
|
||||
@@ -334,8 +382,12 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
|
||||
}
|
||||
}
|
||||
|
||||
respType := in.Type
|
||||
if respType == "" {
|
||||
respType = "response"
|
||||
}
|
||||
respEntry := &AuditResponseEntry{
|
||||
Type: "response",
|
||||
Type: respType,
|
||||
Error: errString,
|
||||
Auth: AuditAuth{
|
||||
ClientToken: auth.ClientToken,
|
||||
@@ -360,12 +412,13 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
|
||||
ID: ns.ID,
|
||||
Path: ns.Path,
|
||||
},
|
||||
Path: req.Path,
|
||||
Data: req.Data,
|
||||
PolicyOverride: req.PolicyOverride,
|
||||
RemoteAddr: getRemoteAddr(req),
|
||||
ReplicationCluster: req.ReplicationCluster,
|
||||
Headers: req.Headers,
|
||||
Path: req.Path,
|
||||
Data: req.Data,
|
||||
PolicyOverride: req.PolicyOverride,
|
||||
RemoteAddr: getRemoteAddr(req),
|
||||
ClientCertificateSerialNumber: getClientCertificateSerialNumber(connState),
|
||||
ReplicationCluster: req.ReplicationCluster,
|
||||
Headers: req.Headers,
|
||||
},
|
||||
|
||||
Response: AuditResponse{
|
||||
@@ -410,18 +463,19 @@ type AuditResponseEntry struct {
|
||||
}
|
||||
|
||||
type AuditRequest struct {
|
||||
ID string `json:"id"`
|
||||
ReplicationCluster string `json:"replication_cluster,omitempty"`
|
||||
Operation logical.Operation `json:"operation"`
|
||||
ClientToken string `json:"client_token"`
|
||||
ClientTokenAccessor string `json:"client_token_accessor"`
|
||||
Namespace AuditNamespace `json:"namespace"`
|
||||
Path string `json:"path"`
|
||||
Data map[string]interface{} `json:"data"`
|
||||
PolicyOverride bool `json:"policy_override"`
|
||||
RemoteAddr string `json:"remote_address"`
|
||||
WrapTTL int `json:"wrap_ttl"`
|
||||
Headers map[string][]string `json:"headers"`
|
||||
ID string `json:"id"`
|
||||
ReplicationCluster string `json:"replication_cluster,omitempty"`
|
||||
Operation logical.Operation `json:"operation"`
|
||||
ClientToken string `json:"client_token"`
|
||||
ClientTokenAccessor string `json:"client_token_accessor"`
|
||||
Namespace AuditNamespace `json:"namespace"`
|
||||
Path string `json:"path"`
|
||||
Data map[string]interface{} `json:"data"`
|
||||
PolicyOverride bool `json:"policy_override"`
|
||||
RemoteAddr string `json:"remote_address"`
|
||||
WrapTTL int `json:"wrap_ttl"`
|
||||
Headers map[string][]string `json:"headers"`
|
||||
ClientCertificateSerialNumber string `json:"client_certificate_serial_number,omitempty"`
|
||||
}
|
||||
|
||||
type AuditResponse struct {
|
||||
@@ -475,6 +529,14 @@ func getRemoteAddr(req *logical.Request) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func getClientCertificateSerialNumber(connState *tls.ConnectionState) string {
|
||||
if connState == nil || len(connState.VerifiedChains) == 0 || len(connState.VerifiedChains[0]) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return connState.VerifiedChains[0][0].SerialNumber.String()
|
||||
}
|
||||
|
||||
// parseVaultTokenFromJWT returns a string iff the token was a JWT and we could
|
||||
// extract the original token ID from inside
|
||||
func parseVaultTokenFromJWT(token string) *string {
|
||||
|
||||
Reference in New Issue
Block a user