Back to .md Directory

Security Audit Report

Documents implemented security controls, identifies gaps, and provides remediation code for a Go microservices deployment on Kubernetes.

May 2, 2026
0 downloads
1 views
ai rag
View source

What this file does

Documents implemented security controls, identifies gaps, and provides remediation code for a Go microservices deployment on Kubernetes.

When to use it

  • Preparing a production security review for a Kubernetes-based microservices system
  • Planning HSM integration for cryptographic signing operations
  • Setting up per-user rate limiting and audit logging in Go services
  • Creating a security checklist and incident response plan for a fintech or Web3 project

Assumes this stack

GoKubernetesHashiCorp VaultPKCS#11Istio/LinkerdSupabase

Security Audit Report

Executive Summary

This document outlines the security measures implemented in Protocol Banks Go microservices and identifies remaining items that need attention before production deployment.


1. Implemented Security Measures

1.1 Network Security

ControlStatusImplementation
TLS EncryptionDoneAll external traffic via Ingress with cert-manager
Network PoliciesDoneDefault deny, explicit allow rules per service
Rate LimitingDoneNginx ingress rate limiting (100 req/s)
DDoS ProtectionPartialNeed Cloudflare/AWS Shield integration

1.2 Authentication & Authorization

ControlStatusImplementation
API AuthenticationDoneJWT tokens with user ID claims
Service-to-Service AuthDonemTLS between services (via Istio/Linkerd)
RBACDoneKubernetes RBAC, Supabase RLS
Webhook Signature VerificationDoneHMAC-SHA256 validation

1.3 Data Security

ControlStatusImplementation
Encryption at RestDoneSupabase/RDS encryption
Encryption in TransitDoneTLS 1.3
Secret ManagementPartialUsing K8s secrets, need Vault
Private Key ProtectionPartialNeed HSM integration

1.4 Application Security

ControlStatusImplementation
Input ValidationDoneAddress/amount validation in Go
SQL Injection PreventionDoneParameterized queries
Replay Attack PreventionDoneIdempotency keys, timestamp validation
Nonce ManagementDoneRedis atomic operations with locks

2. Security Findings & Remediation

2.1 CRITICAL - Private Key Storage

Current State: Private keys stored in K8s secrets Risk: Secrets visible to cluster admins, not encrypted in etcd by default Remediation:

# Use External Secrets Operator with Vault
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: payout-signer-key
spec:
  refreshInterval: 1h
  secretStoreRef:
    kind: ClusterSecretStore
    name: vault-backend
  target:
    name: payout-signer-key
  data:
    - secretKey: private_key
      remoteRef:
        key: secret/data/protocolbanks/signer
        property: private_key

Action Items:

  1. Deploy HashiCorp Vault
  2. Configure External Secrets Operator
  3. Migrate all secrets to Vault
  4. Enable Vault transit encryption for signing operations

2.2 HIGH - Insufficient Audit Logging

Current State: Basic logging implemented Risk: Cannot trace unauthorized access or fraud Remediation:

// Add to services/shared/audit/audit.go
package audit

import (
    "context"
    "encoding/json"
    "time"
)

type AuditEvent struct {
    Timestamp   time.Time              `json:"timestamp"`
    EventType   string                 `json:"event_type"`
    UserID      string                 `json:"user_id"`
    Action      string                 `json:"action"`
    Resource    string                 `json:"resource"`
    ResourceID  string                 `json:"resource_id"`
    IPAddress   string                 `json:"ip_address"`
    UserAgent   string                 `json:"user_agent"`
    Result      string                 `json:"result"`
    Details     map[string]interface{} `json:"details,omitempty"`
}

func LogAuditEvent(ctx context.Context, event AuditEvent) error {
    event.Timestamp = time.Now().UTC()
    
    // Send to audit log storage (CloudWatch, Splunk, etc.)
    data, _ := json.Marshal(event)
    
    // Immutable audit log - write to append-only storage
    return writeToAuditLog(ctx, data)
}

2.3 HIGH - Missing HSM Integration

Current State: Software-based signing Risk: Private keys exposed in memory Remediation:

// Add to services/payout-engine/internal/signer/hsm.go
package signer

import (
    "crypto/ecdsa"
    "github.com/miekg/pkcs11"
)

type HSMSigner struct {
    ctx       *pkcs11.Ctx
    session   pkcs11.SessionHandle
    keyHandle pkcs11.ObjectHandle
}

func NewHSMSigner(libraryPath, pin string) (*HSMSigner, error) {
    ctx := pkcs11.New(libraryPath)
    if err := ctx.Initialize(); err != nil {
        return nil, err
    }
    
    slots, _ := ctx.GetSlotList(true)
    session, _ := ctx.OpenSession(slots[0], pkcs11.CKF_SERIAL_SESSION)
    ctx.Login(session, pkcs11.CKU_USER, pin)
    
    // Find the signing key
    template := []*pkcs11.Attribute{
        pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY),
        pkcs11.NewAttribute(pkcs11.CKA_LABEL, "payout-signer"),
    }
    ctx.FindObjectsInit(session, template)
    handles, _, _ := ctx.FindObjects(session, 1)
    ctx.FindObjectsFinal(session)
    
    return &HSMSigner{
        ctx:       ctx,
        session:   session,
        keyHandle: handles[0],
    }, nil
}

func (s *HSMSigner) Sign(hash []byte) ([]byte, error) {
    mechanism := []*pkcs11.Mechanism{
        pkcs11.NewMechanism(pkcs11.CKM_ECDSA, nil),
    }
    
    s.ctx.SignInit(s.session, mechanism, s.keyHandle)
    return s.ctx.Sign(s.session, hash)
}

2.4 MEDIUM - Rate Limiting Enhancement

Current State: Global rate limiting at ingress Risk: Single user can exhaust rate limit for all Remediation:

// Add per-user rate limiting
package middleware

import (
    "net/http"
    "sync"
    "time"
    
    "golang.org/x/time/rate"
)

type UserRateLimiter struct {
    limiters map[string]*rate.Limiter
    mu       sync.RWMutex
    rate     rate.Limit
    burst    int
}

func NewUserRateLimiter(r rate.Limit, b int) *UserRateLimiter {
    return &UserRateLimiter{
        limiters: make(map[string]*rate.Limiter),
        rate:     r,
        burst:    b,
    }
}

func (l *UserRateLimiter) GetLimiter(userID string) *rate.Limiter {
    l.mu.RLock()
    limiter, exists := l.limiters[userID]
    l.mu.RUnlock()
    
    if exists {
        return limiter
    }
    
    l.mu.Lock()
    defer l.mu.Unlock()
    
    limiter = rate.NewLimiter(l.rate, l.burst)
    l.limiters[userID] = limiter
    
    return limiter
}

func (l *UserRateLimiter) RateLimitMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        userID := r.Header.Get("X-User-ID")
        if userID == "" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        
        limiter := l.GetLimiter(userID)
        if !limiter.Allow() {
            http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
            return
        }
        
        next.ServeHTTP(w, r)
    })
}

2.5 MEDIUM - Insufficient Error Handling

Current State: Generic error responses Risk: Information leakage, poor debugging Remediation:

// Standardized error responses
package errors

import (
    "encoding/json"
    "net/http"
)

type APIError struct {
    Code       string `json:"code"`
    Message    string `json:"message"`
    RequestID  string `json:"request_id"`
    // Internal fields (not exposed)
    InternalErr error  `json:"-"`
    StatusCode  int    `json:"-"`
}

var (
    ErrInvalidAddress    = &APIError{Code: "INVALID_ADDRESS", Message: "Invalid wallet address format", StatusCode: 400}
    ErrInsufficientFunds = &APIError{Code: "INSUFFICIENT_FUNDS", Message: "Insufficient balance for transaction", StatusCode: 400}
    ErrNonceTooLow       = &APIError{Code: "NONCE_TOO_LOW", Message: "Transaction nonce is too low", StatusCode: 409}
    ErrRateLimited       = &APIError{Code: "RATE_LIMITED", Message: "Too many requests", StatusCode: 429}
    ErrInternalError     = &APIError{Code: "INTERNAL_ERROR", Message: "An unexpected error occurred", StatusCode: 500}
)

func (e *APIError) WithRequestID(id string) *APIError {
    copy := *e
    copy.RequestID = id
    return &copy
}

func (e *APIError) Write(w http.ResponseWriter) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(e.StatusCode)
    json.NewEncoder(w).Encode(e)
}

3. Security Checklist Before Production

Infrastructure

  • Enable etcd encryption for Kubernetes secrets
  • Deploy HashiCorp Vault
  • Configure mTLS between services
  • Enable Pod Security Policies / Pod Security Admission
  • Configure Falco for runtime security monitoring
  • Set up WAF (Web Application Firewall)

Application

  • Complete HSM integration for signing
  • Implement comprehensive audit logging
  • Add per-user rate limiting
  • Enable request tracing (Jaeger/Zipkin)
  • Implement circuit breakers for external calls

Operations

  • Configure SIEM integration (Splunk/ELK)
  • Set up security alerts in PagerDuty
  • Create incident response runbooks
  • Schedule penetration testing
  • Establish bug bounty program

Compliance

  • Complete SOC 2 Type II preparation
  • Document data retention policies
  • Implement GDPR data subject requests
  • Configure backup encryption

4. Threat Model

4.1 Attack Vectors

VectorRiskMitigation
Compromised RPC endpointHIGHUse trusted RPC providers, verify responses
Replay attacksHIGHIdempotency keys, nonce management
Private key theftCRITICALHSM, Vault, minimal exposure
Webhook spoofingHIGHHMAC signature verification
SQL injectionHIGHParameterized queries, ORM
DDoSMEDIUMRate limiting, Cloudflare
Insider threatMEDIUMRBAC, audit logs, separation of duties

4.2 Data Classification

Data TypeClassificationHandling
Private keysTOP SECRETHSM only, no logging
User credentialsCONFIDENTIALEncrypted, hashed
Transaction dataCONFIDENTIALEncrypted at rest
Audit logsINTERNALAppend-only, retained 7 years
Public addressesPUBLICNo special handling

5. Incident Response

5.1 Security Incident Severity Levels

LevelDescriptionResponse TimeExample
P0Active exploit, funds at risk15 minutesPrivate key compromised
P1Potential exploit, no loss yet1 hourSuspicious transaction pattern
P2Vulnerability discovered24 hoursNew CVE affecting dependencies
P3Security improvement needed1 weekMissing security header

5.2 Contacts


Approval

RoleNameDateSignature
Security Lead
CTO
Compliance

What's inside

5 sections: implemented controls table, 4 findings with code, production checklist, threat model, incident response plan

Change this for your project

  • Replace protocolbanks.com with your own domain in contact emails
  • Replace secret/data/protocolbanks/signer with your own Vault secret path
  • Replace payout-signer with your own HSM key label
  • Replace services/shared/audit/audit.go with your own package path

Where it goes

Keep it in your repository where the agent or team that needs it will read it.

Worth borrowing

  • Classifying data by sensitivity (TOP SECRET, CONFIDENTIAL, etc.) and tying handling rules to each level
  • Structuring security findings with current state, risk, and a concrete code remediation snippet

Related Documents