Complete production-ready Caddy infrastructure with security hardening

- Add comprehensive Caddy role with HTTPS/TLS, DNS challenges, and systemd security
- Implement optimized systemd overrides with enhanced security restrictions
- Create detailed documentation with usage examples and variable references
- Establish proper Ansible configuration with vault integration
- Update site.yml for infrastructure orchestration with role-based deployment
- Add host-specific configuration structure for scalable multi-environment setup
This commit is contained in:
2025-11-12 22:36:34 +01:00
parent 0b6eea6113
commit 7788410bfc
12 changed files with 795 additions and 3 deletions

View File

@@ -0,0 +1,139 @@
# Caddy configuration file
# Generated by Ansible - DO NOT EDIT MANUALLY
# Global configuration
{
admin {{ caddy_admin_listen }}
{% if caddy_tls_enabled and caddy_tls_email %}
# ACME configuration for Let's Encrypt
email {{ caddy_tls_email }}
acme_ca {{ caddy_acme_ca }}
{% endif %}
{% if not caddy_auto_https %}
auto_https off
{% endif %}
}
# Primary domain: {{ caddy_domain }}
{{ caddy_domain }} {
{% if caddy_tls_enabled %}
{% if caddy_dns_provider == "cloudflare" and cloudflare_api_token %}
# DNS challenge for automatic TLS (secure: no environment files)
tls {
dns cloudflare {{ cloudflare_api_token }}
resolvers {{ caddy_dns_resolvers | join(' ') }}
}
{% elif caddy_tls_email %}
# HTTP challenge for automatic TLS
tls {{ caddy_tls_email }}
{% endif %}
{% else %}
# TLS disabled
{% endif %}
# Serve static content
root * {{ caddy_default_site_root }}
file_server
# Logging
log {
{% if caddy_log_format == "json" %}
output file {{ caddy_log_dir }}/{{ caddy_domain | replace('.', '_') }}.log {
roll_size 100mb
roll_keep 5
}
format json {
time_format "2006-01-02T15:04:05.000Z07:00"
}
level {{ caddy_log_level }}
{% else %}
output file {{ caddy_log_dir }}/{{ caddy_domain | replace('.', '_') }}.log {
roll_size 100mb
roll_keep 5
}
level {{ caddy_log_level }}
{% endif %}
}
}
# Additional configured sites
{% for site in caddy_sites %}
{{ site.domain }}{% if site.port is defined %}:{{ site.port }}{% endif %} {
{% if caddy_tls_enabled and site.tls != "off" %}
{% if site.dns_challenge | default(false) and caddy_dns_provider == "cloudflare" and cloudflare_api_token %}
# DNS challenge for this site (secure: direct variable substitution)
tls {
dns cloudflare {{ cloudflare_api_token }}
resolvers {{ caddy_dns_resolvers | join(' ') }}
}
{% elif caddy_tls_email and site.tls != "off" %}
# HTTP challenge for this site
tls {{ caddy_tls_email }}
{% endif %}
{% elif site.tls == "off" %}
# TLS explicitly disabled for this site
tls off
{% endif %}
{% if site.root is defined %}
# Static file serving
root * {{ site.root }}
file_server
{% endif %}
{% if site.backend is defined %}
# Reverse proxy
reverse_proxy {{ site.backend }} {
# Standard proxy headers
header_up Host {upstream_hostport}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
header_up X-Forwarded-Host {host}
}
{% endif %}
# Logging for this site
log {
{% if caddy_log_format == "json" %}
output file {{ caddy_log_dir }}/{{ site.domain | replace('.', '_') }}.log {
roll_size 100mb
roll_keep 5
}
format json {
time_format "2006-01-02T15:04:05.000Z07:00"
}
level {{ caddy_log_level }}
{% else %}
output file {{ caddy_log_dir }}/{{ site.domain | replace('.', '_') }}.log {
roll_size 100mb
roll_keep 5
}
level {{ caddy_log_level }}
{% endif %}
}
{% if site.extra_config is defined %}
# Additional site configuration
{{ site.extra_config | indent(4) }}
{% endif %}
}
{% endfor %}
{% if caddy_tls_enabled %}
# HTTP to HTTPS redirects
http://{{ caddy_domain }} {
redir https://{host}{uri} permanent
}
{% for site in caddy_sites %}
{% if site.tls != "off" %}
http://{{ site.domain }} {
redir https://{host}{uri} permanent
}
{% endif %}
{% endfor %}
{% endif %}

View File

@@ -0,0 +1,68 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome to jnss</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 2rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
text-align: center;
background: rgba(255,255,255,0.1);
padding: 3rem;
border-radius: 20px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255,255,255,0.2);
}
h1 {
font-size: 3rem;
margin-bottom: 1rem;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
p {
font-size: 1.2rem;
opacity: 0.9;
margin: 1rem 0;
}
.status {
background: rgba(0,255,0,0.2);
padding: 1rem;
border-radius: 10px;
margin: 2rem 0;
border: 1px solid rgba(0,255,0,0.3);
}
.timestamp {
font-family: monospace;
font-size: 0.9rem;
opacity: 0.7;
margin-top: 2rem;
}
</style>
</head>
<body>
<div class="container">
<h1>Welcome to jnss</h1>
<p>🚀 Server infrastructure is online and secure</p>
<div class="status">
✅ Caddy web server running<br>
🔒 Enterprise-grade security hardening active<br>
📊 Structured logging operational
</div>
<p>Infrastructure managed with Ansible</p>
<div class="timestamp">
Deployed: {{ ansible_facts['date_time']['iso8601'] }}
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,38 @@
[Service]
# Reload configuration with --force flag for reliability
ExecReload=
ExecReload=/usr/bin/caddy reload --config {{ caddy_config_file }} --force
{% if caddy_systemd_security | default(true) %}
# Enhanced security hardening beyond base service
NoNewPrivileges=true
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
RemoveIPC=true
# Filesystem restrictions (upgrade from ProtectSystem=full)
ProtectSystem=strict
ProtectHome=true
ReadWritePaths={{ caddy_data_dir }} {{ caddy_log_dir }}
BindReadOnlyPaths={{ caddy_config_dir }}
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectClock=true
# Network and namespace restrictions
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true
# Process restrictions
LimitNPROC=1048576
MemoryDenyWriteExecute=true
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
# Logging (explicit configuration)
StandardOutput=journal
StandardError=journal
SyslogIdentifier=caddy
{% endif %}