Skip to content

Commit

Permalink
Auto-Generating SSL certificates
Browse files Browse the repository at this point in the history
  • Loading branch information
subnetmarco committed Jun 8, 2016
1 parent 85b2d3e commit 68a0c29
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 14 deletions.
1 change: 1 addition & 0 deletions kong-0.8.2-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ build = {
["kong.cmd.utils.nginx_conf_compiler"] = "kong/cmd/utils/nginx_conf_compiler.lua",
["kong.cmd.utils.nginx_signals"] = "kong/cmd/utils/nginx_signals.lua",
["kong.cmd.utils.serf_signals"] = "kong/cmd/utils/serf_signals.lua",
["kong.cmd.utils.ssl"] = "kong/cmd/utils/ssl.lua",

["kong.api.init"] = "kong/api/init.lua",
["kong.api.api_helpers"] = "kong/api/api_helpers.lua",
Expand Down
19 changes: 17 additions & 2 deletions kong/cmd/utils/nginx_conf_compiler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ local pl_utils = require "pl.utils"
local pl_file = require "pl.file"
local pl_path = require "pl.path"
local pl_dir = require "pl.dir"
local ssl = require "kong.cmd.utils.ssl"
local log = require "kong.cmd.utils.log"

local function gather_system_infos(compile_env)
Expand Down Expand Up @@ -63,10 +64,20 @@ local function compile_conf(kong_config, conf_template)
compile_env.nginx_vars[k] = v
end

local ssl_data, err = ssl.get_ssl_cert_and_key(kong_config, kong_config.prefix)
if not ssl_data then return nil, err end

if kong_config.cassandra_ssl and kong_config.cassandra_ssl_trusted_cert then
compile_env["lua_ssl_trusted_certificate"] = kong_config.cassandra_ssl_trusted_cert
--compile_env["ssl_certificate"] =
--compile_env["ssl_certificate_key"] =
end

if kong_config.ssl then
compile_env["ssl_cert"] = ssl_data.ssl_cert
compile_env["ssl_cert_key"] = ssl_data.ssl_cert_key
end

if kong_config.dnsmasq then
compile_env["dns_resolver"] = "127.0.0.1:"..kong_config.dnsmasq_port
end

if kong_config.nginx_optimizations then
Expand Down Expand Up @@ -119,6 +130,10 @@ local function prepare_prefix(kong_config, nginx_prefix)
local ok, _, _, stderr = touch(acc_logs_path)
if not ok then return nil, stderr end

-- auto-generate default SSL certificate
local ok, err = ssl.prepare_ssl_cert_and_key(nginx_prefix)
if not ok then return nil, err end

local nginx_config_path = pl_path.join(nginx_prefix, "nginx.conf")
local kong_nginx_conf_path = pl_path.join(nginx_prefix, "nginx-kong.conf")

Expand Down
71 changes: 71 additions & 0 deletions kong/cmd/utils/ssl.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
local utils = require "kong.tools.utils"
local pl_path = require "pl.path"
local pl_utils = require "pl.utils"
local pl_dir = require "pl.dir"
local log = require "kong.cmd.utils.log"
local fmt = string.format

local _M = {}

local SSL_FOLDER = "ssl"
local SSL_CERT = "kong-default.crt"
local SSL_CERT_KEY = "kong-default.key"
local SSL_CERT_CSR = "kong-default.csr"

function _M.get_ssl_cert_and_key(kong_config, nginx_prefix)
local ssl_cert, ssl_cert_key
if kong_config.ssl_cert and kong_config.ssl_cert_key then
ssl_cert = kong_config.ssl_cert
ssl_cert_key = kong_config.ssl_cert_key
else
ssl_cert = pl_path.join(nginx_prefix, SSL_FOLDER, SSL_CERT)
ssl_cert_key = pl_path.join(nginx_prefix, SSL_FOLDER, SSL_CERT_KEY)
end

-- Check that the files exist
if ssl_cert and not pl_path.exists(ssl_cert) then
return false, "Can't find SSL certificate at: "..ssl_cert
end
if ssl_cert_key and not pl_path.exists(ssl_cert_key) then
return false, "Can't find SSL key at: "..ssl_cert_key
end

return { ssl_cert = ssl_cert, ssl_cert_key = ssl_cert_key }
end

function _M.prepare_ssl_cert_and_key(prefix)
-- Create SSL directory
local ssl_path = pl_path.join(prefix, SSL_FOLDER)
local ok, err = pl_dir.makepath(ssl_path)
if not ok then return nil, err end

local ssl_cert = pl_path.join(prefix, SSL_FOLDER, SSL_CERT)
local ssl_cert_key = pl_path.join(prefix, SSL_FOLDER, SSL_CERT_KEY)
local ssl_cert_csr = pl_path.join(prefix, SSL_FOLDER, SSL_CERT_CSR)

if not (pl_path.exists(ssl_cert) and pl_path.exists(ssl_cert_key)) then
-- Autogenerating the certificates for the first time
log.verbose("Auto-generating the default SSL certificate and key..")

local passphrase = utils.random_string()
local commands = {
fmt("openssl genrsa -des3 -out %s -passout pass:%s 1024", ssl_cert_key, passphrase),
fmt("openssl req -new -key %s -out %s -subj \"/C=US/ST=California/L=San Francisco/O=Kong/OU=IT Department/CN=localhost\" -passin pass:%s", ssl_cert_key, ssl_cert_csr, passphrase),
fmt("cp %s %s.org", ssl_cert_key, ssl_cert_key),
fmt("openssl rsa -in %s.org -out %s -passin pass:%s", ssl_cert_key, ssl_cert_key, passphrase),
fmt("openssl x509 -req -in %s -signkey %s -out %s", ssl_cert_csr, ssl_cert_key, ssl_cert),
fmt("rm %s", ssl_cert_csr),
fmt("rm %s.org", ssl_cert_key)
}
for _, cmd in ipairs(commands) do
local ok, _, _, stderr = pl_utils.executeex(cmd)
if not ok then
return nil, "There was an error when auto-generating the default SSL certificate: "..stderr
end
end

return true
end
end

return _M
4 changes: 3 additions & 1 deletion kong/conf_loader.lua
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ local function check_and_infer(conf)

if conf.dns_resolver and conf.dnsmasq then
errors[#errors+1] = "when specifying a custom DNS resolver you must turn off dnsmasq"
elseif not conf.dns_resolver and not conf.dnsmasq then
errors[#errors+1] = "you must specify at least dnsmasq or a custom DNS resolver"
end

local ipv4_port_pattern = "^(%d+)%.(%d+)%.(%d+)%.(%d+):(%d+)$"
Expand All @@ -126,7 +128,7 @@ local function check_and_infer(conf)
if not conf.cluster_listen_rpc:match(ipv4_port_pattern) then
errors[#errors+1] = "cluster_listen_rpc must be in the form of IPv4:port"
end
if cluster_advertise and not conf.cluster_advertise:match(ipv4_port_pattern) then
if conf.cluster_advertise and not conf.cluster_advertise:match(ipv4_port_pattern) then
errors[#errors+1] = "cluster_advertise must be in the form of IPv4:port"
end
if conf.cluster_ttl_on_failure < 60 then
Expand Down
7 changes: 7 additions & 0 deletions spec/01-unit/01-conf/01-conf_loader_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@ describe("Configuration loader", function()
assert.equal("when specifying a custom DNS resolver you must turn off dnsmasq", err)
assert.is_nil(conf)

local conf, err = conf_loader(nil, {
dnsmasq = false,
dns_resolver = nil
})
assert.equal("you must specify at least dnsmasq or a custom DNS resolver", err)
assert.is_nil(conf)

conf, err = conf_loader(nil, {
dnsmasq = false,
dns_resolver = "8.8.8.8:53"
Expand Down
96 changes: 87 additions & 9 deletions spec/01-unit/01-conf/02-conf_compilation_spec.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
local nginx_conf_compiler = require "kong.cmd.utils.nginx_conf_compiler"
local conf_loader = require "kong.conf_loader"
local helpers = require "spec.helpers"
local pl_file = require "pl.file"
local pl_tablex = require "pl.tablex"

describe("NGINX conf compiler", function()
local custom_conf
Expand Down Expand Up @@ -34,14 +36,6 @@ describe("NGINX conf compiler", function()
assert.matches("listen 0.0.0.0:80;", kong_nginx_conf, nil, true)
assert.matches("listen 127.0.0.1:8001;", kong_nginx_conf, nil, true)
end)
it("disables SSL", function()
local kong_nginx_conf = nginx_conf_compiler.compile_kong_conf(custom_conf)
assert.not_matches("listen %d+%.%d+%.%d+%.%d+:%d+ ssl;", kong_nginx_conf)
assert.not_matches("ssl_certificate", kong_nginx_conf)
assert.not_matches("ssl_certificate_key", kong_nginx_conf)
assert.not_matches("ssl_protocols", kong_nginx_conf)
assert.not_matches("ssl_certificate_by_lua_block", kong_nginx_conf)
end)
it("sets lua_ssl_trusted_certificate", function()
local conf = assert(conf_loader(helpers.test_conf_path, {
cassandra_ssl = true,
Expand All @@ -50,6 +44,88 @@ describe("NGINX conf compiler", function()
local kong_nginx_conf = nginx_conf_compiler.compile_kong_conf(conf)
assert.matches("lua_ssl_trusted_certificate '/path/to/ca.cert';", kong_nginx_conf, nil, true)
end)

describe("SSL", function()
local custom_conf_default_ssl, custom_conf_other_ssl
setup(function()
custom_conf_default_ssl = assert(conf_loader(helpers.test_conf_path, {
ssl = true
}))

custom_conf_other_ssl = assert(conf_loader(helpers.test_conf_path, {
ssl = true,
ssl_cert = "/tmp/custom.crt",
ssl_cert_key = "/tmp/custom.key"
}))

assert(pl_file.write("/tmp/custom.crt", "CUSTOM"))
assert(pl_file.write("/tmp/custom.key", "CUSTOM"))
end)

teardown(function()
assert(pl_file.delete("/tmp/custom.crt"))
assert(pl_file.delete("/tmp/custom.key"))
end)

it("default SSL", function()
local kong_nginx_conf = nginx_conf_compiler.compile_kong_conf(custom_conf_default_ssl)
assert.matches("ssl_certificate spec/fixtures/kong_spec.crt;", kong_nginx_conf)
assert.matches("ssl_certificate_key spec/fixtures/kong_spec.key;", kong_nginx_conf)
end)
it("custom SSL", function()
local kong_nginx_conf = nginx_conf_compiler.compile_kong_conf(custom_conf_other_ssl)
assert.matches("ssl_certificate /tmp/custom.crt;", kong_nginx_conf)
assert.matches("ssl_certificate_key /tmp/custom.key;", kong_nginx_conf)
end)
it("disables SSL", function()
local kong_nginx_conf = nginx_conf_compiler.compile_kong_conf(custom_conf)
assert.not_matches("listen %d+%.%d+%.%d+%.%d+:%d+ ssl;", kong_nginx_conf)
assert.not_matches("ssl_certificate", kong_nginx_conf)
assert.not_matches("ssl_certificate_key", kong_nginx_conf)
assert.not_matches("ssl_protocols", kong_nginx_conf)
assert.not_matches("ssl_certificate_by_lua_block", kong_nginx_conf)
end)
it("should return an error if the files do not exist", function()
local conf = pl_tablex.deepcopy(custom_conf_other_ssl)
conf.ssl_cert = "/hello.crt"
local _, err = nginx_conf_compiler.compile_kong_conf(conf)
assert.equal("Can't find SSL certificate at: /hello.crt", err)

conf = pl_tablex.deepcopy(custom_conf_other_ssl)
conf.ssl_cert_key = "/hello.key"
local _, err = nginx_conf_compiler.compile_kong_conf(conf)
assert.equal("Can't find SSL key at: /hello.key", err)
end)
end)

describe("DNS", function()
local custom_conf_dnsmasq, custom_conf_resolver
setup(function()
custom_conf_dnsmasq = assert(conf_loader(helpers.test_conf_path, {
dnsmasq = true,
dns_resolver = ""
}))
custom_conf_resolver = assert(conf_loader(helpers.test_conf_path, {
dnsmasq = false,
dns_resolver = "8.8.8.8:43"
}))
end)

it("compiles with dnsmasq", function()
local kong_nginx_conf = nginx_conf_compiler.compile_kong_conf(custom_conf_dnsmasq)
assert.matches("resolver 127.0.0.1:8053 ipv6=off;", kong_nginx_conf)
end)
it("compiles with dnsmasq and a custom port", function()
local conf = pl_tablex.deepcopy(custom_conf_dnsmasq)
conf.dnsmasq_port = 4000
local kong_nginx_conf = nginx_conf_compiler.compile_kong_conf(conf)
assert.matches("resolver 127.0.0.1:4000 ipv6=off;", kong_nginx_conf)
end)
it("compiles with custom resolver", function()
local kong_nginx_conf = nginx_conf_compiler.compile_kong_conf(custom_conf_resolver)
assert.matches("resolver 8.8.8.8:43 ipv6=off;", kong_nginx_conf)
end)
end)
end)

describe("compile_nginx_conf()", function()
Expand Down Expand Up @@ -106,12 +182,14 @@ describe("NGINX conf compiler", function()
assert.equal(tmp.." is not a directory", err)
assert.is_nil(ok)
end)
it("creates NGINX conf and log files", function()
it("creates NGINX conf, log files and default SSL certs", function()
assert(nginx_conf_compiler.prepare_prefix(helpers.test_conf, prefix))
assert.truthy(exists(join(prefix, "nginx.conf")))
assert.truthy(exists(join(prefix, "nginx-kong.conf")))
assert.truthy(exists(join(prefix, "logs", "error.log")))
assert.truthy(exists(join(prefix, "logs", "access.log")))
assert.truthy(exists(join(prefix, "ssl", "kong-default.crt")))
assert.truthy(exists(join(prefix, "ssl", "kong-default.key")))
end)
end)
end)
26 changes: 26 additions & 0 deletions spec/01-unit/16-ssl_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
local pl_path = require "pl.path"
local pl_dir = require "pl.dir"
local ssl = require "kong.cmd.utils.ssl"

describe("SSL Utils", function()

setup(function()
pcall(pl_dir.rmtree, "/tmp/ssl")
end)

it("should auto-generate an SSL certificate and key", function()
assert(ssl.prepare_ssl_cert_and_key("/tmp"))
assert(pl_path.exists("/tmp/ssl/kong-default.crt"))
assert(pl_path.exists("/tmp/ssl/kong-default.key"))
end)

it("retrieve the default SSL certificate and key", function()
local ssl_data, err = ssl.get_ssl_cert_and_key({}, "/tmp")
assert.is_table(ssl_data)
assert.is_nil(err)

assert.is_string(ssl_data.ssl_cert)
assert.is_string(ssl_data.ssl_cert_key)
end)

end)
4 changes: 2 additions & 2 deletions spec/kong_tests.conf
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ proxy_listen = 0.0.0.0:9000
proxy_listen_ssl = 0.0.0.0:9443
cluster_listen = 0.0.0.0:9946
cluster_listen_rpc = 127.0.0.1:9373
ssl_cert = ../spec/fixtures/kong_spec.crt
ssl_cert_key = ../spec/fixtures/kong_spec.key
ssl_cert = spec/fixtures/kong_spec.crt
ssl_cert_key = spec/fixtures/kong_spec.key
dnsmasq = off
dns_resolver = 8.8.8.8
database = postgres
Expand Down

0 comments on commit 68a0c29

Please sign in to comment.