Skip to content

Security: axonops/axonops-schema-registry

Security

docs/security.md

Security

Contents

Overview

Security in AxonOps Schema Registry spans authentication, authorization, transport encryption, rate limiting, and audit logging. All security features are optional and can be enabled independently. When no security configuration is present, the registry operates in open mode with all endpoints accessible without credentials.

For detailed coverage of authentication methods (Basic Auth, API keys, LDAP, OIDC, JWT), user management, and the admin CLI, see the Authentication guide. mTLS is transport-level security (not an auth method) and is documented in Configuration. This document focuses on transport security, access control policies, rate limiting, audit logging, and operational hardening.

Transport Layer Security (TLS)

Enable HTTPS by providing a certificate and private key pair. The registry supports configurable minimum TLS versions, client certificate authentication (mTLS), and automatic certificate reloading.

Configuration

security:
  tls:
    enabled: true
    cert_file: /path/to/server.crt
    key_file: /path/to/server.key
    ca_file: /path/to/ca.crt
    min_version: "TLS1.2"
    client_auth: verify
    auto_reload: true
Field Description Default
enabled Enable HTTPS false
cert_file Path to PEM-encoded server certificate (required when enabled)
key_file Path to PEM-encoded private key (required when enabled)
ca_file Path to CA certificate for verifying client certificates ""
min_version Minimum TLS version (TLS1.0, TLS1.1, TLS1.2, TLS1.3) TLS1.2
client_auth Client certificate policy (see below) none
auto_reload Reload certificates from disk without restart false

Client Certificate Modes

Mode Behavior
none No client certificate requested
request Client certificate requested but not required
require Client certificate required but not verified against CA
verify Client certificate required and verified against the CA in ca_file

Use verify for mutual TLS (mTLS) authentication. This requires a valid ca_file containing the certificate authority that signed the client certificates.

Certificate Reload

When auto_reload: true, the registry reloads the server certificate and key from disk on each new TLS handshake without requiring a process restart. This supports zero-downtime certificate rotation, which is useful with automated certificate management tools such as cert-manager or ACME clients.

TLS Termination at a Load Balancer

If TLS is terminated at a reverse proxy or load balancer (e.g., NGINX, HAProxy, AWS ALB), disable TLS on the registry itself and configure the proxy to forward requests over plain HTTP. Ensure the proxy sets X-Forwarded-For and X-Real-IP headers so that rate limiting and audit logging record the correct client IP.

Role-Based Access Control (RBAC)

The registry uses a fixed set of four built-in roles with hierarchical permissions. Roles cannot be customized, but the super_admins list grants unrestricted access to specific usernames regardless of their assigned role.

Permission Matrix

Role Schema Read Schema Write Schema Delete Config Read Config Write Mode Read Mode Write Import User Mgmt
super_admin Yes Yes Yes Yes Yes Yes Yes Yes Full
admin Yes Yes Yes Yes Yes Yes Yes Yes Read only
developer Yes Yes No Yes No Yes No No No
readonly Yes No No Yes No Yes No No No

Permissions are mapped to API endpoints as follows:

Permission Applies to
schema:read GET /subjects/*, GET /schemas/*, POST /compatibility/*
schema:write POST /subjects/*/versions
schema:delete DELETE /subjects/*
config:read GET /config, GET /config/*
config:write PUT /config, DELETE /config, PUT /config/*, DELETE /config/*
mode:read GET /mode, GET /mode/*
mode:write PUT /mode, PUT /mode/*
import:write POST /import/*
admin:read GET /admin/*
admin:write POST/PUT/DELETE /admin/*

Configuration

security:
  auth:
    rbac:
      enabled: true
      default_role: readonly
      super_admins:
        - admin
        - ops-lead
Field Description Default
enabled Enable RBAC enforcement false
default_role Role assigned when an authentication method does not provide one ""
super_admins Usernames with full access to all operations including user management []

When RBAC is disabled, all authenticated users have unrestricted access. When enabled, users listed in super_admins bypass all permission checks. The default_role is applied to users authenticated via methods that do not inherently assign a role (e.g., config-based basic auth).

Credential Storage

Passwords

User passwords are stored as bcrypt hashes with the default cost factor (10). Plaintext passwords are NEVER written to disk or database. The golang.org/x/crypto/bcrypt package is used for hashing, and the cost factor is the bcrypt.DefaultCost constant (currently 10). Bcrypt is intentionally slow, which limits brute-force attack throughput.

API Keys

API keys are stored as SHA-256 hashes by default. When the api_key.secret configuration value is set, the registry uses HMAC-SHA256 with that secret as a pepper, providing defense-in-depth: even if the database is compromised, the attacker cannot verify API keys without the secret.

security:
  auth:
    api_key:
      secret: "${API_KEY_SECRET}"

Key security properties:

  • Hashed at rest -- keys are never stored in plaintext
  • Shown once -- the raw key is returned only at creation time and CANNOT be retrieved afterward
  • Pepper protection -- when secret is configured, HMAC-SHA256 is used instead of plain SHA-256
  • Cluster consistency -- if secret is configured, all registry instances must use the same value
  • Expiration enforced -- key expiration is checked on every request
  • Key prefixes -- configurable prefix (e.g., sr_live_) helps identify keys in logs and configuration

The secret value SHOULD be at least 32 bytes of cryptographically random data. Load it from an environment variable or secrets manager rather than hardcoding it in the configuration file.

External Credential Storage with HashiCorp Vault

Users and API keys can be stored in HashiCorp Vault separately from schema data by setting storage.auth_type: vault. This allows you to use one backend (e.g., PostgreSQL) for schemas while keeping credentials in Vault's KV secrets engine.

storage:
  type: postgresql
  auth_type: vault
  vault:
    address: "https://vault.example.com:8200"
    token: "${VAULT_TOKEN}"
    mount_path: "secret"
    base_path: "schema-registry"

See the Configuration guide for the full Vault configuration reference.

Rate Limiting

The registry implements a token bucket algorithm to protect API endpoints from excessive request volume. Rate limiting is applied as HTTP middleware and operates independently of authentication.

Configuration

security:
  rate_limiting:
    enabled: true
    requests_per_second: 100
    burst_size: 200
    per_client: true
    per_endpoint: false
Field Description Default
enabled Enable rate limiting false
requests_per_second Sustained request rate (token refill rate) 0
burst_size Maximum burst capacity (token bucket size) 0
per_client Maintain separate rate limits per source IP false
per_endpoint Maintain separate rate limits per API path false

Behavior

When a request exceeds the rate limit, the registry responds with HTTP 429 Too Many Requests and includes the following headers:

Header Description
X-RateLimit-Limit Configured requests per second
X-RateLimit-Remaining Tokens remaining in the bucket
Retry-After Seconds until the client should retry (set to 1)

The X-RateLimit-Limit and X-RateLimit-Remaining headers are included on all responses, not only rate-limited ones.

Scope

Rate limiting modes are mutually exclusive in order of precedence:

  1. Per-client (per_client: true) -- each source IP gets its own token bucket. Client IP is determined from X-Forwarded-For, X-Real-IP, or RemoteAddr in that order.
  2. Per-endpoint (per_endpoint: true) -- each combination of HTTP method and path gets its own bucket (e.g., GET:/subjects is separate from POST:/subjects/test/versions).
  3. Global (both false) -- a single shared bucket applies to all requests.

Exempt Endpoints

The health check (GET /) and Prometheus metrics (GET /metrics) endpoints are served before the rate limiting middleware is applied and are never rate-limited.

Stale Client Cleanup

When using per-client rate limiting, the registry provides a CleanupStaleClients method that removes token buckets for clients that have not made a request within a configurable duration. This prevents unbounded memory growth in environments with many transient clients.

Audit Logging

The audit logger records security-relevant events to a structured JSON log. Each event captures who performed an action (actor identity, type, role, and authentication method), what was affected (target type and identifier), and whether the action succeeded or failed (outcome and reason codes). Events cover both the REST API and the MCP server.

For the complete audit logging reference — including the full event schema, all field descriptions, event types, actor types, authentication methods, reason codes, change integrity hashes, and example payloads — see the Audit Logging Guide.

Quick Configuration

security:
  audit:
    enabled: true
    log_file: /var/log/axonops-schema-registry/audit.log
    events: []          # empty = all security-relevant events (recommended)
    include_body: false  # truncated request body in audit entries

Example Event

{
  "timestamp": "2026-03-11T14:30:00Z",
  "duration_ms": 42,
  "event_type": "schema_register",
  "outcome": "success",
  "actor_id": "jane",
  "actor_type": "user",
  "role": "developer",
  "auth_method": "basic",
  "target_type": "subject",
  "target_id": "payments-value",
  "schema_id": 42,
  "version": 3,
  "schema_type": "AVRO",
  "before_hash": "sha256:8b7f...",
  "after_hash": "sha256:2d91...",
  "source_ip": "172.18.0.1",
  "user_agent": "curl/8.1",
  "method": "POST",
  "path": "/subjects/payments-value/versions",
  "status_code": 200,
  "request_id": "localhost/abc-123-def"
}

See the Audit Logging Guide for the complete field reference and more examples.

Unauthenticated Endpoints

The following endpoints are always accessible without credentials, regardless of authentication configuration:

Endpoint Purpose
GET / Health check
GET /metrics Prometheus metrics

When docs_enabled: true in the server configuration:

Endpoint Purpose
GET /docs Swagger UI
GET /openapi.yaml OpenAPI specification

These endpoints are registered outside the authentication middleware chain and are also exempt from rate limiting.

MCP Security

The MCP server has its own security controls, independent from the REST API. For full details, see the MCP Guide.

  • Bearer token authentication: Set mcp.auth_token to require Authorization: Bearer <token> on all MCP requests
  • Permission scopes: 14 scopes with 5 named presets (readonly, developer, operator, admin, full) control which tools are visible
  • Read-only mode: mcp.read_only: true hides all write/delete tools
  • Two-phase confirmations: mcp.require_confirmations: true requires dry-run preview before destructive operations
  • Origin validation: mcp.allowed_origins validates the Origin header per the MCP specification
  • Tool policy: Fine-grained allow_list/deny_list for individual tools
  • Localhost binding: The MCP server binds to 127.0.0.1 by default, not exposed to the network

Security Hardening Checklist

  1. Enable TLS with a minimum version of TLS 1.2 (min_version: "TLS1.2"), or terminate TLS at a load balancer and restrict the registry to private network traffic.
  2. Enable authentication with at least one method (basic, api_key, jwt, or oidc).
  3. Enable RBAC with a restrictive default_role (e.g., readonly) and explicitly assign higher-privilege roles only where needed.
  4. Use API keys with expiration for programmatic access. Rotate keys regularly via the admin API or CLI.
  5. Configure the API key HMAC secret (api_key.secret) on all registry instances and store it in an environment variable or secrets manager.
  6. Enable rate limiting to prevent abuse and reduce the impact of credential brute-force attempts.
  7. Enable audit logging and forward logs to a centralized system for monitoring and alerting.
  8. Use environment variables or Vault for secrets -- never hardcode passwords, API key secrets, or Vault tokens in configuration files. The registry supports ${ENV_VAR} substitution in YAML configuration.
  9. Run as a non-root user -- the Docker image runs as UID/GID 1000 (schemaregistry user) by default.
  10. Configure CORS appropriately if the registry serves browser-based clients.
  11. Restrict network access -- bind the registry to an internal interface or use firewall rules to limit access to trusted networks.
  12. Set client_auth: verify when using mTLS to ensure client certificates are validated against your CA.
  13. Review super_admins list regularly -- users in this list bypass all RBAC checks.

Related Documentation

  • Authentication -- authentication methods, user management, API key lifecycle, admin CLI
  • Configuration -- full configuration reference including all security options
  • Deployment -- production deployment guidance
  • MCP Server -- MCP server security, permission scopes, tool policies

There aren't any published security advisories