From 92f98958454fa695ac52b329c7fa2e97d0f20216 Mon Sep 17 00:00:00 2001 From: Robert Paprocki Date: Wed, 24 May 2017 15:12:48 -0700 Subject: [PATCH] feat(ssl) configurable ssl cipher suites Provide a shorthand config option to define cipher suites based on Mozilla's recommended cipher list, and an optional config to define custom ciphers. --- kong-0.10.2-0.rockspec | 1 + kong.conf.default | 12 +++ kong/conf_loader.lua | 10 +++ kong/templates/kong_defaults.lua | 2 + kong/templates/nginx_kong.lua | 10 +++ kong/tools/ciphers.lua | 115 +++++++++++++++++++++++++++ spec/01-unit/02-conf_loader_spec.lua | 38 +++++++++ 7 files changed, 188 insertions(+) create mode 100644 kong/tools/ciphers.lua diff --git a/kong-0.10.2-0.rockspec b/kong-0.10.2-0.rockspec index eb452f5f043a..cef001449c29 100644 --- a/kong-0.10.2-0.rockspec +++ b/kong-0.10.2-0.rockspec @@ -79,6 +79,7 @@ build = { ["kong.api.routes.certificates"] = "kong/api/routes/certificates.lua", ["kong.api.routes.snis"] = "kong/api/routes/snis.lua", + ["kong.tools.ciphers"] = "kong/tools/ciphers.lua", ["kong.tools.dns"] = "kong/tools/dns.lua", ["kong.tools.utils"] = "kong/tools/utils.lua", ["kong.tools.public"] = "kong/tools/public.lua", diff --git a/kong.conf.default b/kong.conf.default index 642ec4408d84..aefaeb8742b9 100644 --- a/kong.conf.default +++ b/kong.conf.default @@ -111,6 +111,18 @@ # itself on `proxy_listen`, and all SSL # settings will be ignored. +#ssl_cipher_suite = modern # Defines the TLS ciphers served by Nginx. + # Accepted values are 'modern', 'intermediate', + # 'old', or 'custom'. +# Note: See https://wiki.mozilla.org/Security/Server_Side_TLS for detailed +# descriptions of each cipher suite. + +#ssl_ciphers = # Defines a custom list of TLS ciphers to be + # served by Nginx. This list must conform to + # the pattern defined by `openssl ciphers`. + # This value is ignored if `ssl_ciphers_suite` + # is not 'custom'. + #ssl_cert = # If `ssl` is enabled, the absolute path to # the SSL certificate for the # `proxy_listen_ssl` address. diff --git a/kong/conf_loader.lua b/kong/conf_loader.lua index fa52c36602f6..675ad0c5c769 100644 --- a/kong/conf_loader.lua +++ b/kong/conf_loader.lua @@ -9,6 +9,7 @@ local pl_path = require "pl.path" local tablex = require "pl.tablex" local utils = require "kong.tools.utils" local log = require "kong.cmd.utils.log" +local ciphers = require "kong.tools.ciphers" local DEFAULT_PATHS = { "/etc/kong/kong.conf", @@ -252,6 +253,15 @@ local function check_and_infer(conf) end end + if conf.ssl_cipher_suite ~= "custom" then + local ok, err = pcall(function() + conf.ssl_ciphers = ciphers(conf.ssl_cipher_suite) + end) + if not ok then + errors[#errors + 1] = err + end + end + if conf.dns_resolver then for _, server in ipairs(conf.dns_resolver) do local dns = utils.normalize_ip(server) diff --git a/kong/templates/kong_defaults.lua b/kong/templates/kong_defaults.lua index 58f2ff6cc7e6..6f79bb4c8878 100644 --- a/kong/templates/kong_defaults.lua +++ b/kong/templates/kong_defaults.lua @@ -22,6 +22,8 @@ ssl_cert_key = NONE client_ssl = off client_ssl_cert = NONE client_ssl_cert_key = NONE +ssl_cipher_suite = modern +ssl_ciphers = ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256 admin_ssl = on admin_ssl_cert = NONE admin_ssl_cert_key = NONE diff --git a/kong/templates/nginx_kong.lua b/kong/templates/nginx_kong.lua index 9d7f7d20a82d..b10cab9d18a6 100644 --- a/kong/templates/nginx_kong.lua +++ b/kong/templates/nginx_kong.lua @@ -91,6 +91,11 @@ server { ssl_certificate_by_lua_block { kong.ssl_certificate() } + + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + ssl_prefer_server_ciphers on; + ssl_ciphers ${{SSL_CIPHERS}}; > end > if client_ssl then @@ -159,6 +164,11 @@ server { ssl_certificate ${{ADMIN_SSL_CERT}}; ssl_certificate_key ${{ADMIN_SSL_CERT_KEY}}; ssl_protocols TLSv1.1 TLSv1.2; + + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + ssl_prefer_server_ciphers on; + ssl_ciphers ${{SSL_CIPHERS}}; > end location / { diff --git a/kong/tools/ciphers.lua b/kong/tools/ciphers.lua new file mode 100644 index 000000000000..733b0b0058a9 --- /dev/null +++ b/kong/tools/ciphers.lua @@ -0,0 +1,115 @@ +local _ciphers = { + modern = { + "ECDHE-ECDSA-AES256-GCM-SHA384", + "ECDHE-RSA-AES256-GCM-SHA384", + "ECDHE-ECDSA-CHACHA20-POLY1305", + "ECDHE-RSA-CHACHA20-POLY1305", + "ECDHE-ECDSA-AES128-GCM-SHA256", + "ECDHE-RSA-AES128-GCM-SHA256", + "ECDHE-ECDSA-AES256-SHA384", + "ECDHE-RSA-AES256-SHA384", + "ECDHE-ECDSA-AES128-SHA256", + "ECDHE-RSA-AES128-SHA256", + }, + intermediate = { + "ECDHE-ECDSA-CHACHA20-POLY1305", + "ECDHE-RSA-CHACHA20-POLY1305", + "ECDHE-ECDSA-AES128-GCM-SHA256", + "ECDHE-RSA-AES128-GCM-SHA256", + "ECDHE-ECDSA-AES256-GCM-SHA384", + "ECDHE-RSA-AES256-GCM-SHA384", + "DHE-RSA-AES128-GCM-SHA256", + "DHE-RSA-AES256-GCM-SHA384", + "ECDHE-ECDSA-AES128-SHA256", + "ECDHE-RSA-AES128-SHA256", + "ECDHE-ECDSA-AES128-SHA", + "ECDHE-RSA-AES256-SHA384", + "ECDHE-RSA-AES128-SHA", + "ECDHE-ECDSA-AES256-SHA384", + "ECDHE-ECDSA-AES256-SHA", + "ECDHE-RSA-AES256-SHA", + "DHE-RSA-AES128-SHA256", + "DHE-RSA-AES128-SHA", + "DHE-RSA-AES256-SHA256", + "DHE-RSA-AES256-SHA", + "ECDHE-ECDSA-DES-CBC3-SHA", + "ECDHE-RSA-DES-CBC3-SHA", + "EDH-RSA-DES-CBC3-SHA", + "AES128-GCM-SHA256", + "AES256-GCM-SHA384", + "AES128-SHA256", + "AES256-SHA256", + "AES128-SHA", + "AES256-SHA", + "DES-CBC3-SHA", + "!DSS", + }, + old = { + "ECDHE-ECDSA-CHACHA20-POLY1305", + "ECDHE-RSA-CHACHA20-POLY1305", + "ECDHE-RSA-AES128-GCM-SHA256", + "ECDHE-ECDSA-AES128-GCM-SHA256", + "ECDHE-RSA-AES256-GCM-SHA384", + "ECDHE-ECDSA-AES256-GCM-SHA384", + "DHE-RSA-AES128-GCM-SHA256", + "DHE-DSS-AES128-GCM-SHA256", + "kEDH+AESGCM", + "ECDHE-RSA-AES128-SHA256", + "ECDHE-ECDSA-AES128-SHA256", + "ECDHE-RSA-AES128-SHA", + "ECDHE-ECDSA-AES128-SHA", + "ECDHE-RSA-AES256-SHA384", + "ECDHE-ECDSA-AES256-SHA384", + "ECDHE-RSA-AES256-SHA", + "ECDHE-ECDSA-AES256-SHA", + "DHE-RSA-AES128-SHA256", + "DHE-RSA-AES128-SHA", + "DHE-DSS-AES128-SHA256", + "DHE-RSA-AES256-SHA256", + "DHE-DSS-AES256-SHA", + "DHE-RSA-AES256-SHA", + "ECDHE-RSA-DES-CBC3-SHA", + "ECDHE-ECDSA-DES-CBC3-SHA", + "EDH-RSA-DES-CBC3-SHA", + "AES128-GCM-SHA256", + "AES256-GCM-SHA384", + "AES128-SHA256", + "AES256-SHA256", + "AES128-SHA", + "AES256-SHA", + "AES", + "DES-CBC3-SHA", + "HIGH", + "SEED", + "!aNULL", + "!eNULL", + "!EXPORT", + "!DES", + "!RC4", + "!MD5", + "!PSK", + "!RSAPSK", + "!aDH", + "!aECDH", + "!EDH-DSS-DES-CBC3-SHA", + "!KRB5-DES-CBC3-SHA", + "!SRP", + }, +} + + +local ciphers = setmetatable(_ciphers, { + __index = function(t, k) error("Undefined cipher suite " .. tostring(k)) end, +}) + + +local function build_cipher_string(suite) + return table.concat(ciphers[suite], ":") +end + + +return setmetatable({}, { + __call = function(_, suite) + return build_cipher_string(suite) + end, +}) diff --git a/spec/01-unit/02-conf_loader_spec.lua b/spec/01-unit/02-conf_loader_spec.lua index 5b099fc2262c..2c4a7e7ad869 100644 --- a/spec/01-unit/02-conf_loader_spec.lua +++ b/spec/01-unit/02-conf_loader_spec.lua @@ -341,6 +341,44 @@ describe("Configuration loader", function() assert.True(helpers.path.isabs(conf.ssl_cert)) assert.True(helpers.path.isabs(conf.ssl_cert_key)) end) + it("defines ssl_ciphers by default", function() + local conf, err = conf_loader(nil, {}) + assert.is_nil(err) + -- looks kinda like a cipher suite + assert.matches(":", conf.ssl_ciphers, nil, true) + end) + it("explicitly defines ssl_ciphers", function() + local conf, err = conf_loader(nil, { + ssl_cipher_suite = "old" + }) + assert.is_nil(err) + -- looks kinda like a cipher suite + assert.matches(":", conf.ssl_ciphers, nil, true) + end) + it("errors on invalid ssl_cipher_suite", function() + local conf, _, errors = conf_loader(nil, { + ssl_cipher_suite = "foo" + }) + assert.is_nil(conf) + assert.equal(1, #errors) + assert.matches("Undefined cipher suite foo", errors[1], nil, true) + end) + it("overrides ssl_ciphers when ssl_cipher_suite is custom", function() + local conf, err = conf_loader(nil, { + ssl_cipher_suite = "custom", + ssl_ciphers = "foo:bar", + }) + assert.is_nil(err) + assert.equals("foo:bar", conf.ssl_ciphers) + end) + it("doesn't override ssl_ciphers when undefined", function() + local ciphers = require "kong.tools.ciphers" + local conf, err = conf_loader(nil, { + ssl_cipher_suite = "custom", + }) + assert.is_nil(err) + assert.same(ciphers("modern"), conf.ssl_ciphers) + end) end) describe("client", function() it("requires both proxy SSL cert and key", function()