A production-oriented, security-focused backend for infrastructure automation.
This application exposes typed infrastructure actions via FastAPI, with all endpoints dynamically generated from YAML configuration. Commands are executed safely through Make targets using asyncio.subprocess — never shell=True.
Angular Frontend
->
FastAPI Backend
->
Infrastructure Execution Layer
->
Shell Commands / Make / Ansible / Docker / Helm / VirtualBox (future)
- YAML-Driven API: All endpoints, request models, and OpenAPI documentation are generated from
configs/infrastructure.yaml. Adding a new infrastructure action requires only a YAML change and a Makefile target. - Safe Execution: All commands are executed via
asyncio.create_subprocess_execwith argument arrays. No shell string concatenation. - Strong Typing: Pydantic v2 models with
Literalenums enforce allowed values at the API layer. - Audit Foundation: Every execution is logged with structured JSON, actor, arguments, and result.
- Auth-Ready: User, Role, and Permission models exist; endpoints accept a
current_userdependency that can be swapped for JWT/OAuth2 later. - Async Job Tracking: Long-running commands return a
job_idimmediately; results are queried viaGET /api/v1/jobs/{job_id}.
app/
main.py # FastAPI app, middleware, exception handlers
core/
config.py # Pydantic-settings configuration
logging.py # Structured JSON logging with request correlation
security.py # Input validation and safe execution helpers
dependencies.py # FastAPI dependency providers
api/
dynamic_routes.py # YAML-driven route generation (core)
health.py # Liveness / readiness probes
jobs.py # Async job status endpoint
domain/
models/
command.py # CommandDefinition, ExecutionResult
job.py # Job, JobStatus
infrastructure/
command_registry.py # YAML loader, validator, command index
command_executor.py # (future) orchestration layer
process_runner.py # Safe asyncio subprocess wrapper
runner_factory.py # Extensible runner factory (MakeRunner, future runners)
schemas/
common.py # APIResponse envelope, HealthStatus
audit/
logger.py # Structured audit logging
auth/
models.py # User, Role, Permission enums
dependencies.py # current_user stub, permission dependency factory
permissions.py # Permission mapping helpers
jobs/
manager.py # In-memory job manager with abstraction layer
configs/
infrastructure.yaml # Source of truth for API endpoints
Makefile # Make targets executed by MakeRunner
tests/
unit/
test_process_runner.py
test_command_registry.py
integration/
test_api.py
# 1. Install dependencies
pip install -r requirements.txt
# 2. Run the server
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
# 3. Open Swagger UI
open http://localhost:8000/docs# Build and run
docker-compose up --build
# Or build image only
docker build -t infrastructure-api .
docker run -p 8000:8000 infrastructure-apiConfiguration is loaded from environment variables and .env files via pydantic-settings.
| Variable | Default | Description |
|---|---|---|
APP_NAME |
infrastructure-api |
Application name |
ENVIRONMENT |
development |
Runtime environment |
LOG_LEVEL |
INFO |
Logging level |
COMMAND_TIMEOUT |
300 |
Default command timeout (seconds) |
ALLOWED_ORIGINS |
http://localhost:4200 |
CORS origins |
CONFIG_PATH |
configs/infrastructure.yaml |
Infrastructure YAML path |
MAKEFILE_PATH |
configs/Makefile |
Makefile path |
Create a .env file:
cp .env.example .envThe configs/infrastructure.yaml defines the entire API surface.
VM_Control:
PowerControl:
start_vm:
type: POST
args:
vm_name:
- vm1
- vm2
- vm3
make: start-vm
description: Start a virtual machine by name
async_execution: falseThis generates:
- OpenAPI tag:
VM Control - Path:
POST /api/v1/vm-control/power-control/start-vm - Request body:
{ "vm_name": "vm1" }(enum documented in Swagger) - Execution:
make -f configs/Makefile start-vm vm_name=vm1
| Field | Required | Description |
|---|---|---|
type |
Yes | HTTP method (GET, POST, PUT, DELETE) |
args |
Yes | Map of argument names to allowed value lists |
make |
Yes | Make target to execute |
runner |
No | Runner type (make only for now) |
timeout |
No | Override command timeout |
description |
No | Endpoint description |
async_execution |
No | Return job_id immediately if true |
audit_enabled |
No | Enable audit logging |
curl -X POST http://localhost:8000/api/v1/vm-control/power-control/start-vm \
-H "Content-Type: application/json" \
-d '{"vm_name": "vm1"}'curl -X POST http://localhost:8000/api/v1/vm-control/power-control/stop-vm \
-H "Content-Type: application/json" \
-d '{"vm_name": "vm2"}'curl -X POST http://localhost:8000/api/v1/docker-control/container-lifecycle/compose-up \
-H "Content-Type: application/json" \
-d '{"project": "webapp"}'Returns:
{
"success": true,
"data": {
"job_id": "a1b2c3d4...",
"status": "pending"
},
"error": null,
"request_id": "..."
}curl http://localhost:8000/api/v1/jobs/a1b2c3d4...curl http://localhost:8000/api/v1/health/live
curl http://localhost:8000/api/v1/health/ready- No
shell=True: All commands useasyncio.create_subprocess_execwith argument arrays. - Argument Allowlists: Every request argument is validated against YAML-defined allowed values.
- Safe Identifiers: Argument names and values are validated with restrictive regex patterns before subprocess execution.
- No Arbitrary Execution: The API exposes only pre-defined infrastructure actions; there is no generic command execution endpoint.
- Audit Logging: Every command execution is logged with actor, arguments, result, and request ID.
The architecture supports the following without rewriting API generation:
| Feature | How |
|---|---|
| JWT / OAuth2 | Replace _resolve_current_user in app/auth/dependencies.py |
| RBAC | Use require_permission() dependency on routes; permissions already mapped per command |
| SSH Runner | Implement SSHRunner(BaseRunner) and register with RunnerFactory.register(RunnerTypeEnum.SSH, SSHRunner) |
| Kubernetes | Add kubernetes runner type and K8sRunner |
| Scheduled Jobs | Extend JobManager with APScheduler or Celery backend |
| WebSocket Logs | Stream ProcessRunner stdout/stderr over WebSocket |
| Audit Persistence | Replace log_audit_event with database/SIEM writer |
| Multi-user | Add database-backed user store; User model is ready |
# Run all tests
pytest
# Run with coverage
pytest --cov=app --cov-report=term-missingMIT