Skip to content

Commit 9e70a6d

Browse files
feat(nginx): enable tls 1.3 0-rtt for safe http methods
and add a new annotation earlyDataMethods to tweak the list or disable early data completely. the early data feature also is also known as TLS 1.3 zero rount trip time (0-RTT) negotiation and allow the client to send the HTTP request as part of the SSL negotiation, saving one round trip compared to TLS 1.2 for non-resumed sessions. the feature is enabled for the safe HTTP methods GET, HEAD and OPTIONS by default to protect against replay attacks and the Early-Data header is forwarded to origins to allow blocking early data even for safe methods by returning the 425 (Too Early) status code. non-whitelisted HTTP methods will be denied using 425 status by the router and the feature can be disabled completely by setting tje list of methods to an empty string.
1 parent addb06a commit 9e70a6d

4 files changed

Lines changed: 26 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ _Note that Kubernetes annotation maps are all of Go type `map[string]string`. A
270270
| <a name="ssl-hsts-max-age"></a>deis-router | deployment | [router.deis.io/nginx.ssl.hsts.maxAge](#ssl-hsts-max-age) | `"10886400"` | Maximum number of seconds user agents should observe HSTS rewrites. |
271271
| <a name="ssl-hsts-include-sub-domains"></a>deis-router | deployment | [router.deis.io/nginx.ssl.hsts.includeSubDomains](#ssl-hsts-include-sub-domains) | `"false"` | Whether to enforce HSTS for subsequent requests to all subdomains of the original request. |
272272
| <a name="ssl-hsts-preload"></a>deis-router | deployment | [router.deis.io/nginx.ssl.hsts.preload](#ssl-hsts-preload) | `"false"` | Whether to allow the domain to be included in the HSTS preload list. |
273+
| <a name="ssl-early-data-methods"></a>deis-router | deployment | [router.deis.io/nginx.ssl.earlyDataMethods](#ssl-early-data-methods) | `"GET|HEAD|OPTIONS"` | enables nginx `ssl_early_data` (TLS 1.3 0-RTT) for the listes HTTP methods (set to `""` to disable, valid methods: `"GET|HEAD|POST|PUT|DELETE|PATCH|OPTIONS"`). Unsafe or non-idempotent methods should be avoided, to prevent replay attacks. The header `Early-Data: 1` is forwarded to apps, when Early Data is used and they can reply with HTTP status 425 to block it, causing the client to retry without Early-Data. Requires "TLSv1.3" in `"protocols"` to work.|
273274
| <a name="proxy-buffers-enabled"></a>deis-router | deployment | [router.deis.io/nginx.proxyBuffers.enabled](#proxy-buffers-enabled) | `"false"` | Whether to enabled proxy buffering for all applications (this can be overridden on an application basis). |
274275
| <a name="proxy-buffers-number"></a>deis-router | deployment | [router.deis.io/nginx.proxyBuffers.number](#proxy-buffers-number) | `"8"` | `number` argument to the nginx `proxy_buffers` directive for all applications (this can be overridden on an application basis). |
275276
| <a name="proxy-buffers-size"></a>deis-router | deployment | [router.deis.io/nginx.proxyBuffers.size](#proxy-buffers-size) | `"4k"` | `size` argument to the nginx `proxy_buffers` directive expressed in bytes (no suffix), kilobytes (suffixes `k` and `K`), or megabytes (suffixes `m` and `M`). This setting applies to all applications, but can be overridden on an application basis. |

model/model.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ type SSLConfig struct {
218218
UseSessionTickets bool `key:"useSessionTickets" constraint:"(?i)^(true|false)$"`
219219
BufferSize string `key:"bufferSize" constraint:"^[1-9]\\d*[kKmM]?$"`
220220
HSTSConfig *HSTSConfig `key:"hsts"`
221+
EarlyDataMethods string `key:"earlyDataMethods" constraint:"^((GET|HEAD|POST|PUT|DELETE|PATCH|OPTIONS)(\\|\\b|$))*$"`
221222
DHParam string
222223
}
223224

@@ -238,6 +239,7 @@ func newSSLConfig() *SSLConfig {
238239
UseSessionTickets: true,
239240
BufferSize: "4k",
240241
HSTSConfig: newHSTSConfig(),
242+
EarlyDataMethods: "GET|HEAD|OPTIONS",
241243
}
242244
}
243245

model/model_validation_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,14 @@ func TestValidHSTSPreload(t *testing.T) {
375375
testValidValues(t, newTestHSTSConfig, "Preload", "preload", []string{"true", "false", "TRUE", "FALSE"})
376376
}
377377

378+
func TestInvalidEarlyDataMethods(t *testing.T) {
379+
testInvalidValues(t, newTestSSLConfig, "EarlyDataMethods", "earlyDataMethods", []string{"0", "-1", "foobar", "GET||HEAD", "|GET", "GET|", "get|head"})
380+
}
381+
382+
func TestValidEarlyDataMethods(t *testing.T) {
383+
testValidValues(t, newTestSSLConfig, "EarlyDataMethods", "earlyDataMethods", []string{"", "GET", "GET|HEAD", "GET|HEAD|OPTIONS"})
384+
}
385+
378386
func TestInvalidProxyBuffersEnabled(t *testing.T) {
379387
testInvalidValues(t, newTestProxyBuffersConfig, "Enabled", "enabled", []string{"0", "-1", "foobar"})
380388
}

nginx/config.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ http {
125125
'https' 'max-age={{ $hstsConfig.MaxAge }}{{ if $hstsConfig.IncludeSubDomains }}; includeSubDomains{{ end }}{{ if $hstsConfig.Preload }}; preload{{ end }}';
126126
}
127127
{{ end }}
128+
{{ if ne $sslConfig.EarlyDataMethods "" }}
129+
# Only allow early data (TLSv1.3 0-RTT) for select methods
130+
map $request_method $ssl_block_early_data {
131+
default $ssl_early_data;
132+
"~^{{ $sslConfig.EarlyDataMethods }}$" 0;
133+
}
134+
{{ end }}
128135
129136
{{ if $routerConfig.RequestIDs }}
130137
map $http_x_correlation_id $correlation_id {
@@ -168,6 +175,7 @@ http {
168175
proxy_http_version 1.1;
169176
proxy_set_header Upgrade $http_upgrade;
170177
proxy_set_header Connection $connection_upgrade;
178+
{{ if ne $sslConfig.EarlyDataMethods "" }}proxy_set_header Early-Data $ssl_early_data;{{ end }}
171179
proxy_pass http://{{$routerConfig.DefaultServiceIP}}:80;
172180
}
173181
}
@@ -186,6 +194,7 @@ http {
186194
ssl_protocols {{ $sslConfig.Protocols }};
187195
{{ if ne $sslConfig.Ciphers "" }}ssl_ciphers {{ $sslConfig.Ciphers }};{{ end }}
188196
ssl_prefer_server_ciphers on;
197+
ssl_early_data {{ if ne $sslConfig.EarlyDataMethods "" }}on{{ else }}off{{ end }};
189198
{{ if $routerConfig.PlatformCertificate }}
190199
ssl_certificate /opt/router/ssl/platform.crt;
191200
ssl_certificate_key /opt/router/ssl/platform.key;
@@ -261,6 +270,7 @@ http {
261270
ssl_protocols {{ $sslConfig.Protocols }};
262271
{{ if ne $sslConfig.Ciphers "" }}ssl_ciphers {{ $sslConfig.Ciphers }};{{ end }}
263272
ssl_prefer_server_ciphers on;
273+
ssl_early_data {{ if ne $sslConfig.EarlyDataMethods "" }}on{{ else }}off{{ end }};
264274
ssl_certificate /opt/router/ssl/{{ $domain }}.crt;
265275
ssl_certificate_key /opt/router/ssl/{{ $domain }}.key;
266276
{{ if ne $sslConfig.SessionCache "" }}ssl_session_cache {{ $sslConfig.SessionCache }};
@@ -278,6 +288,10 @@ http {
278288
279289
vhost_traffic_status_filter_by_set_key {{ $appConfig.Name }} application::*;
280290
291+
if ($ssl_block_early_data) {
292+
return 425;
293+
}
294+
281295
{{range $location := $appConfig.Locations}}
282296
location {{ $location.Path }} {
283297
{{ if $routerConfig.RequestIDs }}
@@ -304,6 +318,7 @@ http {
304318
proxy_http_version 1.1;
305319
proxy_set_header Upgrade $http_upgrade;
306320
proxy_set_header Connection $connection_upgrade;
321+
{{ if ne $sslConfig.EarlyDataMethods "" }}proxy_set_header Early-Data $ssl_early_data;{{ end }}
307322
{{ if $routerConfig.RequestIDs }}
308323
proxy_set_header X-Request-Id $request_id;
309324
proxy_set_header X-Correlation-Id $correlation_id;

0 commit comments

Comments
 (0)