refactor!: remove legacy adapters, policy engine and API-key auth
Removes the FastAPI server, hand-rolled MCP protocol, tool registry and tools, YAML policy, resource-type authz, API-key and OAuth proxy modules, automation, observability, cache, the old Gitea client and stdio adapter, the key-management scripts and their tests (ADR 0006).
This commit is contained in:
1 parent
c0b22c531e
commit
4ac9626eb5
41 files changed
-13441
No files matched your search
@@ -1,140 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check API key age and alert if rotation needed."""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Check API key age from keys/ metadata directory."""
|
||||
print("=" * 70)
|
||||
print("AegisGitea MCP - API Key Age Check")
|
||||
print("=" * 70)
|
||||
print()
|
||||
|
||||
# Check keys directory
|
||||
keys_dir = Path(__file__).parent.parent / "keys"
|
||||
|
||||
if not keys_dir.exists():
|
||||
print("⚠️ No keys/ directory found")
|
||||
print()
|
||||
print(" Key metadata is not being tracked.")
|
||||
print(" Run scripts/generate_api_key.py and save metadata to track key age.")
|
||||
print()
|
||||
sys.exit(0)
|
||||
|
||||
# Find all key metadata files
|
||||
metadata_files = list(keys_dir.glob("key-*.txt"))
|
||||
|
||||
if not metadata_files:
|
||||
print("⚠️ No key metadata files found in keys/")
|
||||
print()
|
||||
print(" Run scripts/generate_api_key.py and save metadata to track key age.")
|
||||
print()
|
||||
sys.exit(0)
|
||||
|
||||
print(f"Found {len(metadata_files)} key metadata file(s)")
|
||||
print()
|
||||
print("-" * 70)
|
||||
print()
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
warnings = []
|
||||
critical = []
|
||||
|
||||
for metadata_file in sorted(metadata_files):
|
||||
with open(metadata_file, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
|
||||
# Extract metadata
|
||||
key_id_match = re.search(r"Key ID:\s+([^\n]+)", content)
|
||||
created_match = re.search(r"Created:\s+([^\n]+)", content)
|
||||
expires_match = re.search(r"Expires:\s+([^\n]+)", content)
|
||||
|
||||
if not all([key_id_match, created_match, expires_match]):
|
||||
print(f"⚠️ Could not parse: {metadata_file.name}")
|
||||
continue
|
||||
|
||||
key_id = key_id_match.group(1).strip()
|
||||
created_str = created_match.group(1).strip()
|
||||
expires_str = expires_match.group(1).strip()
|
||||
|
||||
# Parse dates
|
||||
try:
|
||||
created_at = datetime.fromisoformat(created_str.replace("Z", "+00:00"))
|
||||
expires_at = datetime.fromisoformat(expires_str.replace("Z", "+00:00"))
|
||||
except ValueError as e:
|
||||
print(f"⚠️ Could not parse dates in: {metadata_file.name}")
|
||||
print(f" Error: {e}")
|
||||
continue
|
||||
|
||||
# Calculate age and time to expiration
|
||||
age_days = (now - created_at).days
|
||||
days_to_expiration = (expires_at - now).days
|
||||
|
||||
# Status
|
||||
if days_to_expiration < 0:
|
||||
status = "❌ EXPIRED"
|
||||
critical.append(key_id)
|
||||
elif days_to_expiration <= 7:
|
||||
status = "⚠️ CRITICAL"
|
||||
critical.append(key_id)
|
||||
elif days_to_expiration <= 14:
|
||||
status = "⚠️ WARNING"
|
||||
warnings.append(key_id)
|
||||
else:
|
||||
status = "✓ OK"
|
||||
|
||||
print(f"Key: {key_id}")
|
||||
print(f" Created: {created_at.strftime('%Y-%m-%d')} ({age_days} days ago)")
|
||||
print(f" Expires: {expires_at.strftime('%Y-%m-%d')} (in {days_to_expiration} days)")
|
||||
print(f" Status: {status}")
|
||||
print()
|
||||
|
||||
print("-" * 70)
|
||||
print()
|
||||
|
||||
# Summary
|
||||
if critical:
|
||||
print("❌ CRITICAL: {} key(s) require immediate rotation!".format(len(critical)))
|
||||
print()
|
||||
print(" Keys:")
|
||||
for key_id in critical:
|
||||
print(f" • {key_id}")
|
||||
print()
|
||||
print(" Action: Run scripts/rotate_api_key.py NOW")
|
||||
print()
|
||||
sys.exit(2)
|
||||
elif warnings:
|
||||
print("⚠️ WARNING: {} key(s) will expire soon".format(len(warnings)))
|
||||
print()
|
||||
print(" Keys:")
|
||||
for key_id in warnings:
|
||||
print(f" • {key_id}")
|
||||
print()
|
||||
print(" Action: Schedule key rotation in the next few days")
|
||||
print(" Run: scripts/rotate_api_key.py")
|
||||
print()
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("✓ All keys are valid and within rotation schedule")
|
||||
print()
|
||||
print(" Next check: Run this script again in 1 week")
|
||||
print()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("\n\nOperation cancelled.")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error: {e}", file=sys.stderr)
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
@@ -1,113 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate a cryptographically secure API key for AegisGitea MCP."""
|
||||
|
||||
import sys
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
|
||||
# Add src to path for imports
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
from aegis_gitea_mcp.auth import generate_api_key
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Generate and display a new API key."""
|
||||
print("=" * 70)
|
||||
print("AegisGitea MCP - API Key Generator")
|
||||
print("=" * 70)
|
||||
print()
|
||||
|
||||
# Get optional description
|
||||
description = input("Enter description for this key (e.g., 'Claude Code'): ").strip()
|
||||
if not description:
|
||||
description = "Generated key"
|
||||
|
||||
# Generate key
|
||||
api_key = generate_api_key(length=64)
|
||||
|
||||
# Calculate expiration date (90 days from now)
|
||||
created_at = datetime.now(timezone.utc)
|
||||
expires_at = created_at + timedelta(days=90)
|
||||
|
||||
print()
|
||||
print("✓ API Key Generated Successfully!")
|
||||
print()
|
||||
print("-" * 70)
|
||||
print(f"Description: {description}")
|
||||
print(f"Created: {created_at.strftime('%Y-%m-%d %H:%M:%S UTC')}")
|
||||
print(f"Expires: {expires_at.strftime('%Y-%m-%d %H:%M:%S UTC')} (90 days)")
|
||||
print("-" * 70)
|
||||
print()
|
||||
print("API KEY:")
|
||||
print(f" {api_key}")
|
||||
print()
|
||||
print("-" * 70)
|
||||
print()
|
||||
print("📋 Next Steps:")
|
||||
print()
|
||||
print("1. Add this key to your .env file:")
|
||||
print()
|
||||
print(f" MCP_API_KEYS={api_key}")
|
||||
print()
|
||||
print(" (If you have multiple keys, separate them with commas)")
|
||||
print()
|
||||
print("2. Restart the MCP server:")
|
||||
print()
|
||||
print(" docker-compose restart aegis-mcp")
|
||||
print()
|
||||
print("3. Configure your MCP client:")
|
||||
print()
|
||||
print(" - Add the server in your MCP client settings")
|
||||
print(" - Add custom header:")
|
||||
print(f" Authorization: Bearer {api_key}")
|
||||
print()
|
||||
print("4. Test the connection:")
|
||||
print()
|
||||
print(" Ask the client: 'List my Gitea repositories'")
|
||||
print()
|
||||
print("-" * 70)
|
||||
print()
|
||||
print("⚠️ IMPORTANT:")
|
||||
print()
|
||||
print(" • Store this key securely - it won't be shown again")
|
||||
print(" • This key should be rotated in 90 days")
|
||||
print(" • Set a reminder to rotate before expiration")
|
||||
print(" • Never commit this key to version control")
|
||||
print()
|
||||
print("=" * 70)
|
||||
|
||||
# Offer to save metadata to file
|
||||
save = input("\nSave key metadata to keys/ directory? [y/N]: ").strip().lower()
|
||||
if save == "y":
|
||||
keys_dir = Path(__file__).parent.parent / "keys"
|
||||
keys_dir.mkdir(exist_ok=True)
|
||||
|
||||
# Create metadata file
|
||||
key_id = api_key[:12]
|
||||
metadata_file = keys_dir / f"key-{key_id}-{created_at.strftime('%Y%m%d')}.txt"
|
||||
|
||||
with open(metadata_file, "w", encoding="utf-8") as f:
|
||||
f.write(f"API Key Metadata\n")
|
||||
f.write(f"================\n\n")
|
||||
f.write(f"Key ID: {key_id}...\n")
|
||||
f.write(f"Description: {description}\n")
|
||||
f.write(f"Created: {created_at.isoformat()}\n")
|
||||
f.write(f"Expires: {expires_at.isoformat()}\n")
|
||||
f.write(f"\n")
|
||||
f.write(f"NOTE: The actual API key is NOT stored in this file for security.\n")
|
||||
f.write(f" Only metadata is saved for reference.\n")
|
||||
|
||||
print(f"\n✓ Metadata saved to: {metadata_file}")
|
||||
print(f"\n (The actual key is NOT saved - only you have it)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("\n\nOperation cancelled.")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
@@ -1,171 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rotate API key for AegisGitea MCP."""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
|
||||
# Add src to path for imports
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
from aegis_gitea_mcp.auth import generate_api_key
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Rotate API key in .env file."""
|
||||
print("=" * 70)
|
||||
print("AegisGitea MCP - API Key Rotation")
|
||||
print("=" * 70)
|
||||
print()
|
||||
|
||||
# Find .env file
|
||||
env_file = Path(__file__).parent.parent / ".env"
|
||||
|
||||
if not env_file.exists():
|
||||
print("❌ Error: .env file not found")
|
||||
print(f" Expected location: {env_file}")
|
||||
print()
|
||||
print(" Please create .env from .env.example first")
|
||||
sys.exit(1)
|
||||
|
||||
# Read current .env
|
||||
with open(env_file, "r", encoding="utf-8") as f:
|
||||
env_content = f.read()
|
||||
|
||||
# Check if MCP_API_KEYS exists
|
||||
if "MCP_API_KEYS" not in env_content:
|
||||
print("❌ Error: MCP_API_KEYS not found in .env file")
|
||||
print()
|
||||
print(" Please add MCP_API_KEYS to your .env file first")
|
||||
sys.exit(1)
|
||||
|
||||
# Extract current key
|
||||
match = re.search(r"MCP_API_KEYS=([^\n]+)", env_content)
|
||||
if not match:
|
||||
print("❌ Error: Could not parse MCP_API_KEYS from .env")
|
||||
sys.exit(1)
|
||||
|
||||
current_keys = match.group(1).strip()
|
||||
key_list = [k.strip() for k in current_keys.split(",") if k.strip()]
|
||||
|
||||
print(f"Found {len(key_list)} existing key(s)")
|
||||
print()
|
||||
|
||||
# Show current keys (first 8 chars only)
|
||||
for i, key in enumerate(key_list, 1):
|
||||
key_hint = f"{key[:8]}...{key[-4:]}" if len(key) >= 12 else "invalid"
|
||||
print(f" {i}. {key_hint}")
|
||||
|
||||
print()
|
||||
print("-" * 70)
|
||||
print()
|
||||
|
||||
# Generate new key
|
||||
new_key = generate_api_key(length=64)
|
||||
created_at = datetime.now(timezone.utc)
|
||||
expires_at = created_at + timedelta(days=90)
|
||||
|
||||
print("✓ New API key generated!")
|
||||
print()
|
||||
print(f"Created: {created_at.strftime('%Y-%m-%d %H:%M:%S UTC')}")
|
||||
print(f"Expires: {expires_at.strftime('%Y-%m-%d %H:%M:%S UTC')} (90 days)")
|
||||
print()
|
||||
print("-" * 70)
|
||||
print()
|
||||
|
||||
# Ask what to do with old keys
|
||||
print("Rotation Strategy:")
|
||||
print()
|
||||
print(" 1. Replace all keys with new key (recommended)")
|
||||
print(" 2. Add new key, keep old keys (grace period)")
|
||||
print(" 3. Cancel")
|
||||
print()
|
||||
|
||||
choice = input("Choose option [1/2/3]: ").strip()
|
||||
|
||||
if choice == "3":
|
||||
print("\nRotation cancelled.")
|
||||
sys.exit(0)
|
||||
elif choice == "2":
|
||||
# Add new key to list
|
||||
key_list.append(new_key)
|
||||
new_keys_str = ",".join(key_list)
|
||||
print("\n✓ New key will be added (total: {} keys)".format(len(key_list)))
|
||||
print("\n⚠️ IMPORTANT: Remove old keys manually after updating the client config")
|
||||
elif choice == "1":
|
||||
# Replace with only new key
|
||||
new_keys_str = new_key
|
||||
print("\n✓ All old keys will be replaced with new key")
|
||||
else:
|
||||
print("\n❌ Invalid choice. Operation cancelled.")
|
||||
sys.exit(1)
|
||||
|
||||
print()
|
||||
print("-" * 70)
|
||||
print()
|
||||
|
||||
# Update .env file
|
||||
new_env_content = re.sub(r"MCP_API_KEYS=([^\n]+)", f"MCP_API_KEYS={new_keys_str}", env_content)
|
||||
|
||||
# Backup old .env
|
||||
backup_file = env_file.with_suffix(f".env.backup-{datetime.now().strftime('%Y%m%d-%H%M%S')}")
|
||||
with open(backup_file, "w", encoding="utf-8") as f:
|
||||
f.write(env_content)
|
||||
|
||||
print(f"✓ Backed up old .env to: {backup_file.name}")
|
||||
|
||||
# Write new .env
|
||||
with open(env_file, "w", encoding="utf-8") as f:
|
||||
f.write(new_env_content)
|
||||
|
||||
print(f"✓ Updated .env file with new key(s)")
|
||||
print()
|
||||
print("-" * 70)
|
||||
print()
|
||||
print("📋 Next Steps:")
|
||||
print()
|
||||
print("1. Restart the MCP server:")
|
||||
print()
|
||||
print(" docker-compose restart aegis-mcp")
|
||||
print()
|
||||
print("2. Update your MCP client configuration:")
|
||||
print()
|
||||
print(" - Update the MCP server entry in your client")
|
||||
print(" - Update Authorization header:")
|
||||
print(f" Authorization: Bearer {new_key}")
|
||||
print()
|
||||
print("3. Test the connection:")
|
||||
print()
|
||||
print(" Ask the client: 'List my Gitea repositories'")
|
||||
print()
|
||||
print("4. If using grace period (option 2):")
|
||||
print()
|
||||
print(" - After confirming the client works with the new key")
|
||||
print(" - Manually remove old keys from .env")
|
||||
print(" - Restart server again")
|
||||
print()
|
||||
print("-" * 70)
|
||||
print()
|
||||
print("⚠️ IMPORTANT:")
|
||||
print()
|
||||
print(f" • New API Key: {new_key}")
|
||||
print(" • Store this securely - it won't be shown again")
|
||||
print(" • Set a reminder to rotate in 90 days")
|
||||
print(" • Old .env backed up to:", backup_file.name)
|
||||
print()
|
||||
print("=" * 70)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("\n\nOperation cancelled.")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error: {e}", file=sys.stderr)
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
Reference in new issue
Block a user