Skip to content

Commit 5dba76c

Browse files
author
Lachlan Evenson
committed
feat(router): make server_token flag configurable
Make server_token flag in nginx configurable via Kubernetes annotation.
1 parent ee87845 commit 5dba76c

File tree

5 files changed

+81
-0
lines changed

5 files changed

+81
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ _Note that Kubernetes annotation maps are all of Go type `map[string]string`. A
247247
| <a name="error-log-level"></a>deis-router | deployment | [router.deis.io/nginx.errorLogLevel](#error-log-level) | `"error"` | Log level used in the nginx `error_log` setting (valid values are: `debug`, `info`, `notice`, `warn`, `error`, `crit`, `alert`, and `emerg`). |
248248
| <a name="platform-domain"></a>deis-router | deployment | [router.deis.io/nginx.platformDomain](#platform-domain) | N/A | This defines the router's platform domain. Any domains added to a routable application _not_ containing the `.` character will be assumed to be subdomains of this platform domain. Thus, for example, a platform domain of `example.com` coupled with a routable app counting `foo` among its domains will result in router configuration that routes traffic for `foo.example.com` to that application. |
249249
| <a name="use-proxy-protocol"></a>deis-router | deployment | [router.deis.io/nginx.useProxyProtocol](#use-proxy-protocol) | `"false"` | PROXY is a simple protocol supported by nginx, HAProxy, Amazon ELB, and others. It provides a method to obtain information about a request's originating IP address from an external (to Kubernetes) load balancer in front of the router. Enabling this option allows the router to select the originating IP from the HTTP `X-Forwarded-For` header. |
250+
| <a name="disable-server-tokens"></a>deis-router | deployment | [router.deis.io/nginx.disableServerTokens](#disable-server-tokens) | `"false"` | Enables or disables emitting nginx version in error messages and in the “Server” response header field. |
250251
| <a name="enforce-whitelists"></a>deis-router | deployment | [router.deis.io/nginx.enforceWhitelists](#enforce-whitelists) | `"false"` | Whether to _require_ application-level whitelists that explicitly enumerate allowed clients by IP / CIDR range. With this enabled, each app will drop _all_ requests unless a whitelist has been defined. |
251252
| <a name="default-whitelist"></a>deis-router | deployment | [router.deis.io/nginx.defaultWhitelist](#default-whitelist) | N/A | A default (router-wide) whitelist expressed as a comma-delimited list of addresses (using IP or CIDR notation). Application-specific whitelists can either extend or override this default. |
252253
| <a name="whitelist-mode"></a>deis-router | deployment | [router.deis.io/nginx.whitelistMode](#whitelist-mode) | `"extend"` | Whether application-specific whitelists should extend or override the router-wide default whitelist (if defined). Valid values are `"extend"` and `"override"`. |

model/model.go

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ type RouterConfig struct {
4848
ErrorLogLevel string `key:"errorLogLevel" constraint:"^(debug|info|notice|warn|error|crit|alert|emerg)$"`
4949
PlatformDomain string `key:"platformDomain" constraint:"(?i)^([a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z0-9]+(-*[a-z0-9]+)+$"`
5050
UseProxyProtocol bool `key:"useProxyProtocol" constraint:"(?i)^(true|false)$"`
51+
DisableServerTokens bool `key:"disableServerTokens" constraint:"(?i)^(true|false)$"`
5152
EnforceWhitelists bool `key:"enforceWhitelists" constraint:"(?i)^(true|false)$"`
5253
DefaultWhitelist []string `key:"defaultWhitelist" constraint:"^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/([0-9]|[1-2][0-9]|3[0-2]))?(\\s*,\\s*)?)+$"`
5354
WhitelistMode string `key:"whitelistMode" constraint:"^(extend|override)$"`
@@ -71,6 +72,7 @@ func newRouterConfig() *RouterConfig {
7172
GzipConfig: newGzipConfig(),
7273
BodySize: "1m",
7374
ProxyRealIPCIDRs: []string{"10.0.0.0/8"},
75+
DisableServerTokens: false,
7476
ErrorLogLevel: "error",
7577
UseProxyProtocol: false,
7678
EnforceWhitelists: false,

model/model_validation_test.go

+8
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,14 @@ func TestValidUseProxyProtocol(t *testing.T) {
103103
testValidValues(t, newTestRouterConfig, "UseProxyProtocol", "useProxyProtocol", []string{"true", "false", "TRUE", "FALSE"})
104104
}
105105

106+
func TestValidServerTokens(t *testing.T) {
107+
testValidValues(t, newTestRouterConfig, "DisableServerTokens", "disableServerTokens", []string{"true", "false", "TRUE", "FALSE"})
108+
}
109+
110+
func TestInvalidServerTokens(t *testing.T) {
111+
testInvalidValues(t, newTestRouterConfig, "DisableServerTokens", "disableServerTokens", []string{"0", "-1", "foobar"})
112+
}
113+
106114
func TestInvalidEnforceWhitelists(t *testing.T) {
107115
testInvalidValues(t, newTestRouterConfig, "EnforceWhitelists", "enforceWhitelists", []string{"0", "-1", "foobar"})
108116
}

nginx/config.go

+3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ http {
4848
4949
client_max_body_size {{ $routerConfig.BodySize }};
5050
51+
{{ if $routerConfig.DisableServerTokens -}}
52+
server_tokens off;
53+
{{- end}}
5154
{{ range $realIPCIDR := $routerConfig.ProxyRealIPCIDRs -}}
5255
set_real_ip_from {{ $realIPCIDR }};
5356
{{ end -}}

nginx/config_test.go

+67
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package nginx
22

33
import (
4+
"bytes"
45
"fmt"
56
"io/ioutil"
67
"os"
78
"path/filepath"
89
"reflect"
10+
"regexp"
911
"testing"
12+
"text/template"
1013

14+
"github.com/Masterminds/sprig"
1115
"github.com/deis/router/model"
1216
)
1317

@@ -218,3 +222,66 @@ func checkCertAndKey(crtPath string, keyPath string, expectedCertContents string
218222

219223
return nil
220224
}
225+
226+
func TestDisableServerTokens(t *testing.T) {
227+
routerConfig := &model.RouterConfig{
228+
WorkerProcesses: "auto",
229+
MaxWorkerConnections: "768",
230+
TrafficStatusZoneSize: "1m",
231+
DefaultTimeout: "1300s",
232+
ServerNameHashMaxSize: "512",
233+
ServerNameHashBucketSize: "64",
234+
GzipConfig: &model.GzipConfig{
235+
Enabled: true,
236+
CompLevel: "5",
237+
Disable: "msie6",
238+
HTTPVersion: "1.1",
239+
MinLength: "256",
240+
Proxied: "any",
241+
Types: "application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/plain text/x-component",
242+
Vary: "on",
243+
},
244+
BodySize: "1m",
245+
ProxyRealIPCIDRs: []string{"10.0.0.0/8"},
246+
ErrorLogLevel: "error",
247+
UseProxyProtocol: false,
248+
EnforceWhitelists: false,
249+
WhitelistMode: "extend",
250+
SSLConfig: &model.SSLConfig{
251+
Enforce: false,
252+
Protocols: "TLSv1 TLSv1.1 TLSv1.2",
253+
SessionTimeout: "10m",
254+
UseSessionTickets: true,
255+
BufferSize: "4k",
256+
HSTSConfig: &model.HSTSConfig{
257+
Enabled: false,
258+
MaxAge: 15552000, // 180 days
259+
IncludeSubDomains: false,
260+
Preload: false,
261+
},
262+
},
263+
264+
DisableServerTokens: true,
265+
}
266+
267+
var b bytes.Buffer
268+
269+
tmpl, err := template.New("nginx").Funcs(sprig.TxtFuncMap()).Parse(confTemplate)
270+
271+
if err != nil {
272+
t.Fatalf("Encountered an error: %v", err)
273+
}
274+
275+
err = tmpl.Execute(&b, routerConfig)
276+
277+
if err != nil {
278+
t.Fatalf("Encountered an error: %v", err)
279+
}
280+
281+
validDirective := regexp.MustCompile(`(?m)^(\s*)server_tokens off;$`)
282+
283+
if !validDirective.Match(b.Bytes()) {
284+
t.Errorf("Expected: 'server_tokens off' in the configuration. Actual: no match")
285+
}
286+
287+
}

0 commit comments

Comments
 (0)