Files
prowler/skills/prowler-api/references/configuration.md
2026-05-27 16:42:01 +02:00

293 lines
9.5 KiB
Markdown

# Prowler API Configuration Reference
## Settings File Structure
```text
api/src/backend/config/
├── django/
│ ├── base.py # Base settings (all environments)
│ ├── devel.py # Development overrides
│ ├── production.py # Production settings
│ └── testing.py # Test settings
├── settings/
│ ├── celery.py # Celery broker/backend config
│ ├── partitions.py # Table partitioning settings
│ ├── sentry.py # Error tracking + exception filtering
│ └── social_login.py # OAuth/SAML providers
├── celery.py # Celery app instance + RLSTask
├── custom_logging.py # NDJSON/Human-readable formatters
├── env.py # django-environ setup
└── urls.py # Root URL config
```
---
## REST Framework Configuration
### Complete `REST_FRAMEWORK` Settings
```python
REST_FRAMEWORK = {
# Schema Generation (JSON:API compatible)
"DEFAULT_SCHEMA_CLASS": "drf_spectacular_jsonapi.schemas.openapi.JsonApiAutoSchema",
# Authentication (JWT + API Key)
"DEFAULT_AUTHENTICATION_CLASSES": (
"api.authentication.CombinedJWTOrAPIKeyAuthentication",
),
# Pagination
"PAGE_SIZE": 10,
"DEFAULT_PAGINATION_CLASS": "drf_spectacular_jsonapi.schemas.pagination.JsonApiPageNumberPagination",
# Custom exception handler (JSON:API format)
"EXCEPTION_HANDLER": "api.exceptions.custom_exception_handler",
# Parsers (JSON:API compatible)
"DEFAULT_PARSER_CLASSES": (
"rest_framework_json_api.parsers.JSONParser",
"rest_framework.parsers.FormParser",
"rest_framework.parsers.MultiPartParser",
),
# Custom renderer with RLS context support
"DEFAULT_RENDERER_CLASSES": ("api.renderers.APIJSONRenderer",),
# Metadata
"DEFAULT_METADATA_CLASS": "rest_framework_json_api.metadata.JSONAPIMetadata",
# Filter Backends
"DEFAULT_FILTER_BACKENDS": (
"rest_framework_json_api.filters.QueryParameterValidationFilter",
"rest_framework_json_api.filters.OrderingFilter",
"rest_framework_json_api.django_filters.backends.DjangoFilterBackend",
"rest_framework.filters.SearchFilter",
),
# JSON:API search parameter
"SEARCH_PARAM": "filter[search]",
# Test settings
"TEST_REQUEST_RENDERER_CLASSES": ("rest_framework_json_api.renderers.JSONRenderer",),
"TEST_REQUEST_DEFAULT_FORMAT": "vnd.api+json",
# Uniform exception format
"JSON_API_UNIFORM_EXCEPTIONS": True,
# Throttling
"DEFAULT_THROTTLE_CLASSES": ["rest_framework.throttling.ScopedRateThrottle"],
"DEFAULT_THROTTLE_RATES": {
"token-obtain": env("DJANGO_THROTTLE_TOKEN_OBTAIN", default=None),
"dj_rest_auth": None,
},
}
```
### Throttling Configuration
| Scope | Environment Variable | Default | Format |
|-------|---------------------|---------|--------|
| `token-obtain` | `DJANGO_THROTTLE_TOKEN_OBTAIN` | `None` (disabled) | `"X/minute"`, `"X/hour"`, `"X/day"` |
| `dj_rest_auth` | N/A | `None` (disabled) | Same |
**To enable throttling:**
```bash
DJANGO_THROTTLE_TOKEN_OBTAIN="10/minute" # Limit token endpoint to 10 requests/minute
```
---
## JWT Configuration (SIMPLE_JWT)
```python
SIMPLE_JWT = {
# Token Lifetimes
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=30), # DJANGO_ACCESS_TOKEN_LIFETIME
"REFRESH_TOKEN_LIFETIME": timedelta(minutes=1440), # DJANGO_REFRESH_TOKEN_LIFETIME (24h)
# Token Rotation
"ROTATE_REFRESH_TOKENS": True,
"BLACKLIST_AFTER_ROTATION": True,
# Cryptographic Settings
"ALGORITHM": "RS256", # Asymmetric (requires key pair)
"SIGNING_KEY": env.str("DJANGO_TOKEN_SIGNING_KEY", ""),
"VERIFYING_KEY": env.str("DJANGO_TOKEN_VERIFYING_KEY", ""),
# JWT Claims
"TOKEN_TYPE_CLAIM": "typ",
"JTI_CLAIM": "jti",
"USER_ID_FIELD": "id",
"USER_ID_CLAIM": "sub",
# Issuer/Audience
"AUDIENCE": env.str("DJANGO_JWT_AUDIENCE", "https://api.prowler.com"),
"ISSUER": env.str("DJANGO_JWT_ISSUER", "https://api.prowler.com"),
# Custom Serializers
"TOKEN_OBTAIN_SERIALIZER": "api.serializers.TokenSerializer",
"TOKEN_REFRESH_SERIALIZER": "api.serializers.TokenRefreshSerializer",
}
```
---
## Database Configuration
### 4-Database Architecture
```python
DATABASES = {
"default": {...}, # Alias to prowler_user (RLS enabled)
"prowler_user": {...}, # RLS-enabled connection
"admin": {...}, # Admin connection (bypasses RLS)
"replica": {...}, # Read replica (RLS enabled)
"admin_replica": {...}, # Admin on replica
"neo4j": {...}, # Graph database (attack paths)
}
```
### Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `POSTGRES_DB` | `prowler_db` | Database name |
| `POSTGRES_USER` | `prowler_user` | API user (RLS-constrained) |
| `POSTGRES_PASSWORD` | - | API user password |
| `POSTGRES_HOST` | `postgres-db` | Database host |
| `POSTGRES_PORT` | `5432` | Database port |
| `POSTGRES_ADMIN_USER` | `prowler` | Admin user (migrations) |
| `POSTGRES_ADMIN_PASSWORD` | - | Admin password |
| `POSTGRES_REPLICA_HOST` | - | Replica host (optional) |
| `POSTGRES_REPLICA_MAX_ATTEMPTS` | `3` | Retry attempts before fallback |
| `POSTGRES_REPLICA_RETRY_BASE_DELAY` | `0.5` | Base delay for exponential backoff |
---
## Celery Configuration
### Broker/Backend
```python
VALKEY_SCHEME = env("VALKEY_SCHEME", default="redis")
VALKEY_USERNAME = env("VALKEY_USERNAME", default="")
VALKEY_PASSWORD = env("VALKEY_PASSWORD", default="")
VALKEY_HOST = env("VALKEY_HOST", default="valkey")
VALKEY_PORT = env("VALKEY_PORT", default="6379")
VALKEY_DB = env("VALKEY_DB", default="0")
CELERY_BROKER_URL = _build_celery_broker_url(
VALKEY_SCHEME,
VALKEY_USERNAME,
VALKEY_PASSWORD,
VALKEY_HOST,
VALKEY_PORT,
VALKEY_DB,
)
CELERY_RESULT_BACKEND = "django-db" # Store results in PostgreSQL
CELERY_TASK_TRACK_STARTED = True
CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True
```
### Task Visibility
| Variable | Default | Description |
|----------|---------|-------------|
| `DJANGO_BROKER_VISIBILITY_TIMEOUT` | `86400` (24h) | Task visibility timeout |
| `DJANGO_CELERY_DEADLOCK_ATTEMPTS` | `5` | Deadlock retry attempts |
---
## Partitioning Configuration
```python
PSQLEXTRA_PARTITIONING_MANAGER = "api.partitions.manager"
FINDINGS_TABLE_PARTITION_MONTHS = env.int("FINDINGS_TABLE_PARTITION_MONTHS", 1)
FINDINGS_TABLE_PARTITION_COUNT = env.int("FINDINGS_TABLE_PARTITION_COUNT", 7)
FINDINGS_TABLE_PARTITION_MAX_AGE_MONTHS = env.int("...", None) # Optional cleanup
```
---
## Application Settings
| Variable | Default | Description |
|----------|---------|-------------|
| `DJANGO_DEBUG` | `False` | Debug mode |
| `DJANGO_ALLOWED_HOSTS` | `["localhost"]` | Allowed hosts |
| `DJANGO_CACHE_MAX_AGE` | `3600` | HTTP cache max-age |
| `DJANGO_STALE_WHILE_REVALIDATE` | `60` | Stale-while-revalidate time |
| `DJANGO_FINDINGS_MAX_DAYS_IN_RANGE` | `7` | Max days for findings date filter |
| `DJANGO_TMP_OUTPUT_DIRECTORY` | `/tmp/prowler_api_output` | Temp output directory |
| `DJANGO_FINDINGS_BATCH_SIZE` | `1000` | Batch size for findings export |
| `DJANGO_DELETION_BATCH_SIZE` | `5000` | Batch size for deletions |
| `DJANGO_LOGGING_LEVEL` | `INFO` | Log level |
| `DJANGO_LOGGING_FORMATTER` | `ndjson` | Log format (`ndjson` or `human_readable`) |
---
## Social Login (OAuth/SAML)
| Variable | Description |
|----------|-------------|
| `SOCIAL_GOOGLE_OAUTH_CLIENT_ID` | Google OAuth client ID |
| `SOCIAL_GOOGLE_OAUTH_CLIENT_SECRET` | Google OAuth secret |
| `SOCIAL_GITHUB_OAUTH_CLIENT_ID` | GitHub OAuth client ID |
| `SOCIAL_GITHUB_OAUTH_CLIENT_SECRET` | GitHub OAuth secret |
---
## Monitoring
| Variable | Description |
|----------|-------------|
| `DJANGO_SENTRY_DSN` | Sentry DSN for error tracking |
---
## Middleware Stack (Order Matters)
```python
MIDDLEWARE = [
"django_guid.middleware.guid_middleware", # 1. Transaction ID
"django.middleware.security.SecurityMiddleware", # 2. Security headers
"django.contrib.sessions.middleware.SessionMiddleware",
"corsheaders.middleware.CorsMiddleware", # 4. CORS (before Common)
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"api.middleware.APILoggingMiddleware", # 10. Custom API logging
"allauth.account.middleware.AccountMiddleware",
]
```
---
## Security Headers
| Setting | Value | Description |
|---------|-------|-------------|
| `SECURE_PROXY_SSL_HEADER` | `("HTTP_X_FORWARDED_PROTO", "https")` | Trust X-Forwarded-Proto |
| `SECURE_CONTENT_TYPE_NOSNIFF` | `True` | X-Content-Type-Options: nosniff |
| `X_FRAME_OPTIONS` | `"DENY"` | Prevent framing |
| `CSRF_COOKIE_SECURE` | `True` | HTTPS-only CSRF cookie |
| `SESSION_COOKIE_SECURE` | `True` | HTTPS-only session cookie |
---
## Password Validators
| Validator | Options |
|-----------|---------|
| `UserAttributeSimilarityValidator` | Default |
| `MinimumLengthValidator` | `min_length=12` |
| `MaximumLengthValidator` | `max_length=72` (bcrypt limit) |
| `CommonPasswordValidator` | Default |
| `NumericPasswordValidator` | Default |
| `SpecialCharactersValidator` | `min_special_characters=1` |
| `UppercaseValidator` | `min_uppercase=1` |
| `LowercaseValidator` | `min_lowercase=1` |
| `NumericValidator` | `min_numeric=1` |