Files
AegisGitea-MCP/docs/security.md

4.4 KiB

Security

Authentication

AegisGitea MCP uses bearer token authentication. Clients must include a valid API key with every tool call.

How It Works

  1. The client sends Authorization: Bearer <key> with its request.
  2. The server extracts the token and validates it against the configured MCP_API_KEYS.
  3. Comparison is done in constant time to prevent timing attacks.
  4. If validation fails, the failure is counted against the client's IP address.

Generating API Keys

Use the provided script to generate cryptographically secure 64-character hex keys:

make generate-key
# or: python scripts/generate_api_key.py

Keys must be at least 32 characters long. The script also saves metadata (creation date, expiration) to a keys/ directory.

Multiple Keys (Grace Period During Rotation)

You can configure multiple keys separated by commas. This allows you to add a new key and remove the old one without downtime:

MCP_API_KEYS=newkey...,oldkey...

Remove the old key from the list after all clients have been updated.


Key Rotation

Rotate keys regularly (recommended: every 90 days).

make rotate-key
# or: python scripts/rotate_api_key.py

The rotation script:

  1. Reads the current key from .env
  2. Generates a new key
  3. Offers to replace the key immediately or add it alongside the old key (grace period)
  4. Creates a backup of your .env before modifying it

Checking Key Age

make check-key-age
# or: python scripts/check_key_age.py

Exit codes: 0 = OK, 1 = expiring within 7 days (warning), 2 = already expired (critical).


Rate Limiting

Failed authentication attempts are tracked per client IP address.

Setting Default Description
MAX_AUTH_FAILURES 5 Maximum failures before the IP is blocked
AUTH_FAILURE_WINDOW 300 Rolling window in seconds

Once an IP exceeds the threshold, all further requests from that IP return HTTP 429 until the window resets. This is enforced entirely in memory — a server restart resets the counters.


Audit Logging

All security-relevant events are written to a structured JSON log file.

Log Location

Default: /var/log/aegis-mcp/audit.log
Configurable via AUDIT_LOG_PATH.

The directory is created automatically on startup.

What Is Logged

Event Description
Tool invocation Every call to a tool: tool name, arguments, result status, correlation ID
Access denied Failed authentication attempts: IP address, reason
Security event Rate limit triggers, invalid key formats, startup authentication status

Log Format

Each entry is a JSON object on a single line:

{
  "timestamp": "2026-02-13T10:00:00Z",
  "event": "tool_invocation",
  "correlation_id": "a1b2c3d4-...",
  "tool": "get_file_contents",
  "owner": "myorg",
  "repo": "backend",
  "path": "src/main.py",
  "result": "success",
  "client_ip": "10.0.0.1"
}

Using Logs for Monitoring

Because entries are newline-delimited JSON, they are easy to parse:

# Show all failed tool calls
grep '"result": "error"' /var/log/aegis-mcp/audit.log | jq .

# Show all access-denied events
grep '"event": "access_denied"' /var/log/aegis-mcp/audit.log | jq .

Access Control Model

AegisGitea MCP does not implement its own repository access control. Access to repositories is determined entirely by the Gitea bot user's permissions:

  • If the bot user has no access to a repository, it will not appear in list_repositories and get_repository_info will return an error.
  • Grant the bot user the minimum set of repository permissions needed.

Principle of least privilege: create a dedicated bot user and grant it read-only access only to the repositories that the AI needs to see.


Network Security Recommendations

  • Run the MCP server behind a reverse proxy (e.g. Traefik or nginx) with TLS.
  • Do not expose the server directly on a public port without TLS.
  • Restrict inbound connections to known AI client IP ranges where possible.
  • The /mcp/tools endpoint is intentionally public (required for ChatGPT plugin discovery). If this is undesirable, restrict it at the network/proxy level.

Container Security

The provided Docker image runs with:

  • A non-root user (aegis, UID 1000)
  • no-new-privileges security option
  • CPU and memory resource limits (1 CPU, 512 MB)

See Deployment for details.