Ana içeriğe geç
Versiyon: 1.0.1

Security Guide

This guide covers security best practices for deploying and operating Milvaion in production environments. All security configurations are managed through appsettings.json, environment variables, and infrastructure settings.


Authentication Configuration

JWT Token Settings

Configure token security in appsettings.json:

{
"Milvasoft": {
"Identity": {
"Token": {
"UseUtcForDateTimes": true,
"ExpirationMinute": 90,
"TokenValidationParameters": {
"ValidateIssuer": true,
"ValidateAudience": true,
"ValidIssuer": "https://your-domain.com",
"ValidAudience": "milvaion-clients"
},
"SecurityKeyType": 0,
"SymmetricPublicKey": "your-256-bit-secret-key-here"
}
}
}
}
SettingDevelopmentProduction
ExpirationMinute9015-30
ValidateIssuerfalsetrue
ValidateAudiencefalsetrue
SymmetricPublicKeyAnyRandom 256-bit key

Generate a Secure Key

MilvaIdentity requires a 256-bit (32 byte) symmetric key for JWT signing.

The key may be provided in hexadecimal or Base64 format as long as it represents 32 cryptographically secure bytes.

Windows (PowerShell – Hex Format)

# Generates a 256-bit (32 byte) AES-compatible key in HEX format
$bytes = New-Object byte[] 32
[Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
($bytes | ForEach-Object { $_.ToString("x2") }) -join ""

Example output:

dc778fe1bf2572e91cba54a9835fb2268ad7fdc8bfc2e313d23f975b09111ae3

Linux / macOS (Hex Format)

openssl rand -hex 32

Optional: Base64 Format

# Windows
[Convert]::ToBase64String(
(1..32 | ForEach-Object { Get-Random -Maximum 256 })
)

# Linux / macOS
openssl rand -base64 32

⚠️ Important Security Notes

  • Never reuse keys between environments
  • Never commit production keys to source control
  • Store keys in environment variables or secret managers
  • Rotate JWT signing keys periodically (recommended: every 90 days)

Token Expiration Behavior

Milvaion handles token states with specific HTTP status codes:

Status CodeMeaningClient Action
401Invalid or missing tokenRedirect to login
419Token expiredRefresh token or re-login
403Insufficient permissionsShow access denied

Password Security

Password Policy Configuration

Configure password requirements in appsettings.json:

{
"Milvasoft": {
"Identity": {
"Password": {
"Hasher": {
"IterationCount": 10000
},
"RequiredLength": 8,
"RequiredUniqueChars": 2,
"RequireNonAlphanumeric": true,
"RequireLowercase": true,
"RequireUppercase": true,
"RequireDigit": true
}
}
}
}
SettingMinimumHigh Security
RequiredLength812+
IterationCount10000100000+
RequiredUniqueChars24

Account Lockout Protection

Protect against brute-force attacks:

{
"Milvasoft": {
"Identity": {
"Lockout": {
"AllowedForNewUsers": true,
"MaxFailedAccessAttempts": 5,
"DefaultLockoutTimeSpan": 15
}
}
}
}

Behavior:

  • After 5 failed attempts → Account locked for 15 minutes
  • Lockout duration shown to user
  • Successful login resets the counter

🌐 Cross-Origin Resource Sharing (CORS)

Milvaion uses a configuration-driven CORS model. All CORS policies are defined in appsettings.json and applied dynamically at startup.

⚠️ Misconfigured CORS is a common source of security vulnerabilities. Always restrict origins in production environments.


CORS Configuration

Example appsettings.json

{
"Cors": {
"DefaultPolicy": "Public",
"Policies": {
"Public": {
"Origins": [
"https://app.your-domain.com"
],
"Methods": [ "GET", "POST", "PUT", "PATCH", "DELETE" ],
"Headers": [ "Content-Type", "Authorization" ],
"AllowCredentials": false
}
}
}
}

Internal / Trusted Environments (Allow All with Credentials)

For internal APIs, admin panels, or gateway-protected services, Milvaion supports a controlled “allow all origins with credentials” mode using SetIsOriginAllowed.

{
"Cors": {
"DefaultPolicy": "AllowAll",
"Policies": {
"AllowAll": {
"AllowAnyOriginWithCredentials": true,
"Origins": [ "All" ],
"Methods": [ "All" ],
"Headers": [ "All" ],
"ExposedHeaders": [ "Content-Disposition" ],
"AllowCredentials": true
}
}
}
}

⚠️ Warning: This configuration MUST NOT be exposed to the public internet. Use it only behind a reverse proxy, VPN, or API gateway.


Environment-Based CORS Strategy

EnvironmentRecommended Policy
DevelopmentAllowAll
QA / StagingWhitelisted domains
Production (Public API)Strict origin whitelist
Production (Internal API)Gateway-only AllowAll

Example override:

// appsettings.Development.json
{
"Cors": {
"DefaultPolicy": "AllowAll"
}
}
// appsettings.Production.json
{
"Cors": {
"DefaultPolicy": "Public"
}
}

Default Policy Behavior

The policy defined by Cors:DefaultPolicy is automatically applied globally and default configuration value is AllowAll;

  "Cors": {
"DefaultPolicy": "AllowAll",
"Policies": {
"AllowAll": {
"AllowAnyOriginWithCredentials": true,
"Origins": [ "All" ],
"Methods": [ "All" ],
"Headers": [ "All" ],
"ExposedHeaders": [ "Content-Disposition" ],
"AllowCredentials": true
}
}
}

but fully configuration-driven.


Infrastructure Security

PostgreSQL

Secure Connection String

{
"ConnectionStrings": {
"DefaultConnectionString": "Host=postgres;Port=5432;Database=MilvaionDb;Username=milvaion_app;Password=SECURE_PASSWORD;SSL Mode=Require;Trust Server Certificate=false;Pooling=true;Maximum Pool Size=100"
}
}

Best Practices

PracticeImplementation
Dedicated userCreate milvaion_app with minimum required permissions
SSL/TLSEnable SSL Mode=Require
Network isolationUse private network, no public IP
Connection limitsSet Maximum Pool Size appropriately

Database User Permissions

-- Create application user with minimal permissions
CREATE USER milvaion_app WITH PASSWORD 'secure_password';

-- Grant only necessary permissions
GRANT CONNECT ON DATABASE MilvaionDb TO milvaion_app;
GRANT USAGE ON SCHEMA public TO milvaion_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO milvaion_app;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO milvaion_app;

-- For migrations (use a separate admin user)
CREATE USER milvaion_admin WITH PASSWORD 'admin_password';
GRANT CREATE ON SCHEMA public TO milvaion_admin;

Redis

API Configuration

{
"MilvaionConfig": {
"Redis": {
"ConnectionString": "redis:6379,ssl=true",
"Password": "STRONG_REDIS_PASSWORD",
"Database": 0,
"ConnectTimeout": 5000,
"SyncTimeout": 5000
}
}
}

Worker Configuration

{
"Worker": {
"Redis": {
"ConnectionString": "redis:6379",
"Password": "SAME_REDIS_PASSWORD",
"Database": 0,
"CancellationChannel": "Milvaion:JobScheduler:cancellation_channel"
}
}
}

Redis Server Hardening

# redis.conf
requirepass YOUR_STRONG_PASSWORD
bind 127.0.0.1 # Or private network IP only
protected-mode yes
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command DEBUG ""

RabbitMQ

API Configuration

{
"MilvaionConfig": {
"RabbitMQ": {
"Host": "rabbitmq",
"Port": 5672,
"Username": "milvaion_producer",
"Password": "SECURE_RABBITMQ_PASSWORD",
"VirtualHost": "/milvaion"
}
}
}

Worker Configuration

{
"Worker": {
"RabbitMQ": {
"Host": "rabbitmq",
"Port": 5672,
"Username": "milvaion_consumer",
"Password": "SECURE_RABBITMQ_PASSWORD",
"VirtualHost": "/milvaion"
}
}
}

Virtual Host Isolation

Create a dedicated virtual host for Milvaion:

# Create virtual host
rabbitmqctl add_vhost /milvaion

# Create users with specific permissions
rabbitmqctl add_user milvaion_producer SECURE_PASSWORD
rabbitmqctl set_permissions -p /milvaion milvaion_producer ".*" ".*" ".*"

rabbitmqctl add_user milvaion_consumer SECURE_PASSWORD
rabbitmqctl set_permissions -p /milvaion milvaion_consumer ".*" ".*" ".*"

# Remove default guest user
rabbitmqctl delete_user guest

Disable Management UI in Production

# Disable management plugin entirely
rabbitmq-plugins disable rabbitmq_management

# Or restrict via environment variable
RABBITMQ_MANAGEMENT_LISTENER_IP=127.0.0.1

Secrets Management

Environment Variables

Override sensitive configuration with environment variables:

# docker-compose.yml
services:
milvaion-api:
environment:
- ConnectionStrings__DefaultConnectionString=Host=postgres;...;Password=${DB_PASSWORD}
- Milvasoft__Identity__Token__SymmetricPublicKey=${JWT_SECRET}
- MilvaionConfig__Redis__Password=${REDIS_PASSWORD}
- MilvaionConfig__RabbitMQ__Password=${RABBITMQ_PASSWORD}
- MILVAION_ROOT_PASSWORD=${ROOT_PASSWORD}

worker:
environment:
- Worker__Redis__Password=${REDIS_PASSWORD}
- Worker__RabbitMQ__Password=${RABBITMQ_PASSWORD}

Docker Secrets

For Docker Swarm deployments:

services:
milvaion-api:
secrets:
- db_password
- jwt_secret
- redis_password
- rabbitmq_password

secrets:
db_password:
file: ./secrets/db_password.txt
jwt_secret:
file: ./secrets/jwt_secret.txt
redis_password:
file: ./secrets/redis_password.txt
rabbitmq_password:
file: ./secrets/rabbitmq_password.txt

Kubernetes Secrets

apiVersion: v1
kind: Secret
metadata:
name: milvaion-secrets
namespace: milvaion
type: Opaque
stringData:
db-password: "your-secure-db-password"
jwt-secret: "your-256-bit-jwt-secret"
redis-password: "your-redis-password"
rabbitmq-password: "your-rabbitmq-password"

Reference in deployment:

env:
- name: MilvaionConfig__Redis__Password
valueFrom:
secretKeyRef:
name: milvaion-secrets
key: redis-password

Logging Security

Log Level Configuration

Reduce sensitive information in logs for production:

{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft.AspNetCore.Authentication": "Warning",
"Microsoft.AspNetCore.Authorization": "Warning",
"Microsoft.EntityFrameworkCore.Database.Command": "Warning"
}
}
}
}

Seq Log Server Security

# docker-compose.yml
seq:
environment:
- SEQ_FIRSTRUN_ADMINPASSWORD=${SEQ_ADMIN_PASSWORD}
- ACCEPT_EULA=Y

⚠️ Important: Change the Seq admin password immediately after first deployment.


Worker Security

Resource Limits

Prevent resource exhaustion attacks:

# docker-compose.yml
worker:
deploy:
resources:
limits:
cpus: '2'
memory: 2048M
reservations:
cpus: '0.5'
memory: 512M

Worker Configuration

{
"Worker": {
"WorkerId": "worker-01",
"MaxParallelJobs": 32,
"ExecutionTimeoutSeconds": 300
}
}
SettingDescriptionSecurity Impact
MaxParallelJobsLimit concurrent executionsPrevents resource exhaustion
ExecutionTimeoutSecondsKill long-running jobsPrevents hung processes

Network Security

Reverse Proxy Configuration (nginx)

server {
listen 443 ssl http2;
server_name milvaion.your-domain.com;

ssl_certificate /etc/ssl/certs/milvaion.crt;
ssl_certificate_key /etc/ssl/private/milvaion.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;

# Security headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

location / {
proxy_pass http://milvaion-api:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

Firewall Rules

Only expose necessary ports:

ServicePortExposure
Milvaion API5000Via reverse proxy only
PostgreSQL5432Private network only
Redis6379Private network only
RabbitMQ5672Private network only
RabbitMQ Management15672Disabled or internal only
Seq5341Internal only

Security Checklist

Pre-Production

  • Change all default passwords (PostgreSQL, Redis, RabbitMQ, Seq)
  • Generate new JWT signing key (256-bit random)
  • Enable SSL/TLS for database connections
  • Set strong password policies
  • Configure account lockout
  • Set MILVA_ENV=prod environment variable
    • This will disable open api client and developer endpoints etc.

Infrastructure

  • Place databases on private networks
  • Configure reverse proxy with TLS termination
  • Set up firewall rules
  • Disable RabbitMQ management UI or restrict access
  • Enable Redis protected mode
  • Remove default guest user from RabbitMQ

Monitoring

  • Set up alerts for failed login attempts
  • Monitor worker health anomalies
  • Configure log retention policies
  • Enable database backup monitoring

Regular Maintenance

  • Rotate secrets quarterly
  • Update container images for security patches
  • Review activity logs regularly
  • Audit user permissions
  • Test backup restoration procedures

Incident Response

Signs of Compromise

IndicatorAction
Multiple failed logins from same IPReview logs, consider IP blocking
Unexpected admin user creationDisable account, investigate
Unusual job execution patternsReview job logs and payloads
Database connection spikesCheck for injection attempts
Worker heartbeat anomaliesInvestigate worker health

Response Steps

  1. Contain - Isolate affected systems
  2. Identify - Review logs in Seq/Grafana
  3. Eradicate - Remove threat, rotate all credentials
  4. Recover - Restore from known-good backups
  5. Document - Record incident details and lessons learned