-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathnginx.ssl.conf
More file actions
166 lines (143 loc) · 6.42 KB
/
Copy pathnginx.ssl.conf
File metadata and controls
166 lines (143 loc) · 6.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# ============================================================================
# ThemisDB Admin UI - Optional TLS nginx configuration (Phase 2)
# ============================================================================
# This file enables HTTPS directly on the admin-ui container.
#
# OPTION A — RECOMMENDED: QNAP built-in reverse proxy (no changes needed here)
# Use Control Panel → Application Portal → Reverse Proxy.
# QNAP handles TLS termination; this file is not required.
#
# OPTION B — Direct TLS on this container
# 1. Mount your certificate and key into the container via docker-compose:
# volumes:
# - /share/certs/admin.crt:/etc/nginx/ssl/cert.crt:ro
# - /share/certs/admin.key:/etc/nginx/ssl/cert.key:ro
# 2. Replace nginx.conf with this file (rename or use the include directive).
#
# OPTION C — Let's Encrypt via acme.sh sidecar
# Use a Certbot or acme.sh container to issue and renew certs.
# Mount the resulting .crt/.key at the paths below.
#
# CERT PATHS — adjust to match your actual certificate locations:
# /etc/nginx/ssl/cert.crt — full chain PEM certificate
# /etc/nginx/ssl/cert.key — private key (RSA 2048+ or ECDSA P-256)
# ============================================================================
limit_req_zone $binary_remote_addr zone=admin_api:10m rate=30r/m;
limit_req_zone $binary_remote_addr zone=admin_login:10m rate=5r/m;
map $http_origin $cors_allowed {
default "";
"" "1";
~^https?://localhost(:[0-9]+)?$ "1";
~^https?://127\.0\.0\.1(:[0-9]+)?$ "1";
}
upstream themisdb_backend {
server themis:8080;
}
# ---------------------------------------------------------------------------
# HTTP → HTTPS redirect
# ---------------------------------------------------------------------------
server {
listen 80;
server_name _;
return 301 https://$host$request_uri;
}
# ---------------------------------------------------------------------------
# HTTPS server
# ---------------------------------------------------------------------------
server {
listen 443 ssl;
server_name _;
# -- TLS certificates --------------------------------------------------
ssl_certificate /etc/nginx/ssl/cert.crt;
ssl_certificate_key /etc/nginx/ssl/cert.key;
# -- TLS hardening (TLS 1.2+ only, modern cipher suite) ---------------
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# OCSP Stapling (requires resolver and CA cert)
# ssl_stapling on;
# ssl_stapling_verify on;
# ssl_trusted_certificate /etc/nginx/ssl/chain.crt;
# resolver 1.1.1.1 8.8.8.8 valid=300s;
# -- HSTS (enable only once TLS is confirmed working) ------------------
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# -- Standard 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" always;
add_header Permissions-Policy "geolocation=(), camera=()" always;
# -- Static admin UI ---------------------------------------------------
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
location = /health-ui {
return 200 'ok';
add_header Content-Type text/plain;
}
# -- API reverse proxy -------------------------------------------------
location /api/ {
limit_req zone=admin_api burst=10 nodelay;
limit_req_status 429;
if ($cors_allowed = "") {
return 403 '{"error":"cors_forbidden","message":"Origin not allowed"}';
}
set $csrf_check 0;
if ($request_method = POST) { set $csrf_check 1; }
if ($request_method = PUT) { set $csrf_check 1; }
if ($request_method = PATCH) { set $csrf_check 1; }
if ($request_method = DELETE) { set $csrf_check 1; }
set $is_login_post 0;
if ($request_uri ~ "^/api/auth/sessions$") { set $is_login_post 1; }
set $csrf_required "${csrf_check}${is_login_post}";
if ($csrf_required = "10") {
set $csrf_header $http_x_csrf_token;
if ($csrf_header = "") {
return 403 '{"error":"csrf_required","message":"X-CSRF-Token header missing"}';
}
}
proxy_pass http://themisdb_backend/;
proxy_http_version 1.1;
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 https;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
rewrite ^/api/(.*)$ /$1 break;
}
# Stricter rate limit on the login endpoint
location = /api/auth/sessions {
limit_req zone=admin_login burst=3 nodelay;
limit_req_status 429;
proxy_pass http://themisdb_backend/auth/sessions;
proxy_http_version 1.1;
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 https;
proxy_read_timeout 30s;
proxy_send_timeout 30s;
}
location = /openapi.json {
proxy_pass http://themisdb_backend/api/openapi.json;
proxy_set_header Host $host;
}
error_page 429 /429.html;
location = /429.html {
return 429 '{"error":"rate_limit_exceeded","message":"Too many requests — please wait and retry."}';
add_header Content-Type application/json;
}
error_page 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
internal;
}
}