Add audit logging for all mutating gRPC operations
Log Info-level audit events on success for: - system: Init, Unseal, Seal - auth: Login, Logout - engine: Mount, Unmount - policy: CreatePolicy, DeletePolicy - ca: ImportRoot, CreateIssuer, DeleteIssuer, IssueCert, RenewCert Each log line includes relevant identifiers (mount, issuer, serial, CN, SANs, username) so that certificate issuance and other privileged operations are traceable in the server logs. Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
@@ -1 +1 @@
|
|||||||
[{"lang":"en","usageCount":32}]
|
[{"lang":"en","usageCount":33}]
|
||||||
@@ -27,6 +27,7 @@ func (as *authServer) Login(_ context.Context, req *pb.LoginRequest) (*pb.LoginR
|
|||||||
if t, err := time.Parse(time.RFC3339, expiresAtStr); err == nil {
|
if t, err := time.Parse(time.RFC3339, expiresAtStr); err == nil {
|
||||||
expiresAt = timestamppb.New(t)
|
expiresAt = timestamppb.New(t)
|
||||||
}
|
}
|
||||||
|
as.s.logger.Info("audit: login", "username", req.Username)
|
||||||
return &pb.LoginResponse{Token: token, ExpiresAt: expiresAt}, nil
|
return &pb.LoginResponse{Token: token, ExpiresAt: expiresAt}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,6 +40,7 @@ func (as *authServer) Logout(ctx context.Context, _ *pb.LogoutRequest) (*pb.Logo
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
_ = as.s.auth.Logout(client)
|
_ = as.s.auth.Logout(client)
|
||||||
}
|
}
|
||||||
|
as.s.logger.Info("audit: logout", "username", callerUsername(ctx))
|
||||||
return &pb.LogoutResponse{}, nil
|
return &pb.LogoutResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,14 @@ func (cs *caServer) caHandleRequest(ctx context.Context, mount, operation string
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func callerUsername(ctx context.Context) string {
|
||||||
|
ti := tokenInfoFromContext(ctx)
|
||||||
|
if ti == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return ti.Username
|
||||||
|
}
|
||||||
|
|
||||||
func (cs *caServer) callerInfo(ctx context.Context) *engine.CallerInfo {
|
func (cs *caServer) callerInfo(ctx context.Context) *engine.CallerInfo {
|
||||||
ti := tokenInfoFromContext(ctx)
|
ti := tokenInfoFromContext(ctx)
|
||||||
if ti == nil {
|
if ti == nil {
|
||||||
@@ -82,6 +90,7 @@ func (cs *caServer) ImportRoot(ctx context.Context, req *pb.ImportRootRequest) (
|
|||||||
expiresAt = timestamppb.New(t)
|
expiresAt = timestamppb.New(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cs.s.logger.Info("audit: root CA imported", "mount", req.Mount, "cn", cn, "username", callerUsername(ctx))
|
||||||
return &pb.ImportRootResponse{CommonName: cn, ExpiresAt: expiresAt}, nil
|
return &pb.ImportRootResponse{CommonName: cn, ExpiresAt: expiresAt}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,6 +137,7 @@ func (cs *caServer) CreateIssuer(ctx context.Context, req *pb.CreateIssuerReques
|
|||||||
}
|
}
|
||||||
name, _ := resp.Data["name"].(string)
|
name, _ := resp.Data["name"].(string)
|
||||||
certPEM, _ := resp.Data["cert_pem"].(string)
|
certPEM, _ := resp.Data["cert_pem"].(string)
|
||||||
|
cs.s.logger.Info("audit: issuer created", "mount", req.Mount, "issuer", name, "username", callerUsername(ctx))
|
||||||
return &pb.CreateIssuerResponse{Name: name, CertPem: []byte(certPEM)}, nil
|
return &pb.CreateIssuerResponse{Name: name, CertPem: []byte(certPEM)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,6 +153,7 @@ func (cs *caServer) DeleteIssuer(ctx context.Context, req *pb.DeleteIssuerReques
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
cs.s.logger.Info("audit: issuer deleted", "mount", req.Mount, "issuer", req.Name, "username", callerUsername(ctx))
|
||||||
return &pb.DeleteIssuerResponse{}, nil
|
return &pb.DeleteIssuerResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,6 +281,7 @@ func (cs *caServer) IssueCert(ctx context.Context, req *pb.IssueCertRequest) (*p
|
|||||||
expiresAt = timestamppb.New(t)
|
expiresAt = timestamppb.New(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cs.s.logger.Info("audit: certificate issued", "mount", req.Mount, "issuer", issuedBy, "serial", serial, "cn", cn, "sans", sans, "username", callerUsername(ctx))
|
||||||
return &pb.IssueCertResponse{
|
return &pb.IssueCertResponse{
|
||||||
Serial: serial,
|
Serial: serial,
|
||||||
CommonName: cn,
|
CommonName: cn,
|
||||||
@@ -346,6 +358,7 @@ func (cs *caServer) RenewCert(ctx context.Context, req *pb.RenewCertRequest) (*p
|
|||||||
expiresAt = timestamppb.New(t)
|
expiresAt = timestamppb.New(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cs.s.logger.Info("audit: certificate renewed", "mount", req.Mount, "old_serial", req.Serial, "new_serial", serial, "cn", cn, "issued_by", issuedBy, "username", callerUsername(ctx))
|
||||||
return &pb.RenewCertResponse{
|
return &pb.RenewCertResponse{
|
||||||
Serial: serial,
|
Serial: serial,
|
||||||
CommonName: cn,
|
CommonName: cn,
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ func (es *engineServer) Mount(ctx context.Context, req *pb.MountRequest) (*pb.Mo
|
|||||||
return nil, status.Error(codes.Internal, err.Error())
|
return nil, status.Error(codes.Internal, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ti := tokenInfoFromContext(ctx)
|
||||||
|
username := ""
|
||||||
|
if ti != nil {
|
||||||
|
username = ti.Username
|
||||||
|
}
|
||||||
|
es.s.logger.Info("audit: engine mounted", "name", req.Name, "type", req.Type, "username", username)
|
||||||
return &pb.MountResponse{}, nil
|
return &pb.MountResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +59,12 @@ func (es *engineServer) Unmount(ctx context.Context, req *pb.UnmountRequest) (*p
|
|||||||
}
|
}
|
||||||
return nil, status.Error(codes.Internal, err.Error())
|
return nil, status.Error(codes.Internal, err.Error())
|
||||||
}
|
}
|
||||||
|
ti := tokenInfoFromContext(ctx)
|
||||||
|
username := ""
|
||||||
|
if ti != nil {
|
||||||
|
username = ti.Username
|
||||||
|
}
|
||||||
|
es.s.logger.Info("audit: engine unmounted", "name", req.Name, "username", username)
|
||||||
return &pb.UnmountResponse{}, nil
|
return &pb.UnmountResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ func (ps *policyServer) CreatePolicy(ctx context.Context, req *pb.CreatePolicyRe
|
|||||||
ps.s.logger.Error("grpc: create policy", "error", err)
|
ps.s.logger.Error("grpc: create policy", "error", err)
|
||||||
return nil, status.Error(codes.Internal, "internal error")
|
return nil, status.Error(codes.Internal, "internal error")
|
||||||
}
|
}
|
||||||
|
ps.s.logger.Info("audit: policy created", "id", rule.ID, "username", callerUsername(ctx))
|
||||||
return &pb.CreatePolicyResponse{Rule: ruleToPB(rule)}, nil
|
return &pb.CreatePolicyResponse{Rule: ruleToPB(rule)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +59,7 @@ func (ps *policyServer) DeletePolicy(ctx context.Context, req *pb.DeletePolicyRe
|
|||||||
if err := ps.s.policy.DeleteRule(ctx, req.Id); err != nil {
|
if err := ps.s.policy.DeleteRule(ctx, req.Id); err != nil {
|
||||||
return nil, status.Error(codes.NotFound, "not found")
|
return nil, status.Error(codes.NotFound, "not found")
|
||||||
}
|
}
|
||||||
|
ps.s.logger.Info("audit: policy deleted", "id", req.Id, "username", callerUsername(ctx))
|
||||||
return &pb.DeletePolicyResponse{}, nil
|
return &pb.DeletePolicyResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ func (ss *systemServer) Init(ctx context.Context, req *pb.InitRequest) (*pb.Init
|
|||||||
ss.s.logger.Error("grpc: init failed", "error", err)
|
ss.s.logger.Error("grpc: init failed", "error", err)
|
||||||
return nil, status.Error(codes.Internal, "initialization failed")
|
return nil, status.Error(codes.Internal, "initialization failed")
|
||||||
}
|
}
|
||||||
|
ss.s.logger.Info("audit: vault initialized")
|
||||||
return &pb.InitResponse{State: ss.s.sealMgr.State().String()}, nil
|
return &pb.InitResponse{State: ss.s.sealMgr.State().String()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +62,7 @@ func (ss *systemServer) Unseal(ctx context.Context, req *pb.UnsealRequest) (*pb.
|
|||||||
return nil, status.Error(codes.Internal, "engine unseal failed")
|
return nil, status.Error(codes.Internal, "engine unseal failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ss.s.logger.Info("audit: vault unsealed")
|
||||||
return &pb.UnsealResponse{State: ss.s.sealMgr.State().String()}, nil
|
return &pb.UnsealResponse{State: ss.s.sealMgr.State().String()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,5 +75,6 @@ func (ss *systemServer) Seal(_ context.Context, _ *pb.SealRequest) (*pb.SealResp
|
|||||||
return nil, status.Error(codes.Internal, "seal failed")
|
return nil, status.Error(codes.Internal, "seal failed")
|
||||||
}
|
}
|
||||||
ss.s.auth.ClearCache()
|
ss.s.auth.ClearCache()
|
||||||
|
ss.s.logger.Info("audit: vault sealed")
|
||||||
return &pb.SealResponse{State: ss.s.sealMgr.State().String()}, nil
|
return &pb.SealResponse{State: ss.s.sealMgr.State().String()}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -284,6 +284,8 @@ type CertSummary struct {
|
|||||||
Issuer string
|
Issuer string
|
||||||
CommonName string
|
CommonName string
|
||||||
Profile string
|
Profile string
|
||||||
|
IssuedBy string
|
||||||
|
IssuedAt string
|
||||||
ExpiresAt string
|
ExpiresAt string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,6 +302,10 @@ func (c *VaultClient) ListCerts(ctx context.Context, token, mount string) ([]Cer
|
|||||||
Issuer: s.Issuer,
|
Issuer: s.Issuer,
|
||||||
CommonName: s.CommonName,
|
CommonName: s.CommonName,
|
||||||
Profile: s.Profile,
|
Profile: s.Profile,
|
||||||
|
IssuedBy: s.IssuedBy,
|
||||||
|
}
|
||||||
|
if s.IssuedAt != nil {
|
||||||
|
cs.IssuedAt = s.IssuedAt.AsTime().Format("2006-01-02T15:04:05Z")
|
||||||
}
|
}
|
||||||
if s.ExpiresAt != nil {
|
if s.ExpiresAt != nil {
|
||||||
cs.ExpiresAt = s.ExpiresAt.AsTime().Format("2006-01-02T15:04:05Z")
|
cs.ExpiresAt = s.ExpiresAt.AsTime().Format("2006-01-02T15:04:05Z")
|
||||||
|
|||||||
@@ -45,12 +45,12 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{{range .Certs}}
|
{{range .Certs}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{index . "cn"}}</td>
|
<td>{{.CommonName}}</td>
|
||||||
<td>{{index . "profile"}}</td>
|
<td>{{.Profile}}</td>
|
||||||
<td><code>{{index . "serial"}}</code></td>
|
<td><code>{{.Serial}}</code></td>
|
||||||
<td>{{index . "issued_by"}}</td>
|
<td>{{.IssuedBy}}</td>
|
||||||
<td>{{index . "issued_at"}}</td>
|
<td>{{.IssuedAt}}</td>
|
||||||
<td>{{index . "expires_at"}}</td>
|
<td>{{.ExpiresAt}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
Reference in New Issue
Block a user