Skip to content

Commit

Permalink
feat(healthcheck) support to mTLS in active checks (#41)
Browse files Browse the repository at this point in the history
* feat(healthcheck) support to mTLS in active checks

* chore(ci) install penlight in test env
  • Loading branch information
locao authored Jun 17, 2020
1 parent 68f0cc6 commit 8a6360c
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 4 deletions.
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@

sudo: required

language: c

compiler: gcc
Expand Down Expand Up @@ -51,6 +49,7 @@ install:
- export PATH=$OPENRESTY_PREFIX/nginx/sbin:$LUAROCKS_PREFIX/bin:$PATH
- sudo luarocks install luacheck > build.log 2>&1 || (cat build.log && exit 1)
- sudo luarocks install lua-resty-worker-events > build.log 2>&1 || (cat build.log && exit 1)
- sudo luarocks install penlight > build.log 2>&1 || (cat build.log && exit 1)
- luarocks --version
- nginx -V

Expand Down
32 changes: 30 additions & 2 deletions lib/resty/healthcheck.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ local resty_lock = require ("resty.lock")
local re_find = ngx.re.find
local bit = require("bit")
local ngx_now = ngx.now
local ssl = require("ngx.ssl")

-- constants
local EVENT_SOURCE_PREFIX = "lua-resty-healthcheck"
Expand Down Expand Up @@ -828,14 +829,23 @@ function checker:run_single_check(ip, port, hostname, hostheader)
end

if self.checks.active.type == "https" then
local session
session, err = sock:sslhandshake(nil, hostname,
local session, err
if self.ssl_cert and self.ssl_key then
session, err = sock:tlshandshake({
verify = self.checks.active.https_verify_certificate,
client_cert = self.ssl_cert,
client_priv_key = self.ssl_key
})
else
session, err = sock:sslhandshake(nil, hostname,
self.checks.active.https_verify_certificate)
end
if not session then
sock:close()
self:log(ERR, "failed SSL handshake with '", hostname, " (", ip, ":", port, ")': ", err)
return self:report_tcp_failure(ip, port, hostname, "connect", "active")
end

end

local path = self.checks.active.http_path
Expand Down Expand Up @@ -1274,6 +1284,8 @@ end
-- * `name`: name of the health checker
-- * `shm_name`: the name of the `lua_shared_dict` specified in the Nginx configuration to use
-- * `checks.active.type`: "http", "https" or "tcp" (default is "http")
-- * `ssl_cert`: certificate for mTLS connections (string or parsed object)
-- * `ssl_key`: key for mTLS connections (string or parsed object)
-- * `checks.active.timeout`: socket timeout for active checks (in seconds)
-- * `checks.active.concurrency`: number of targets to check concurrently
-- * `checks.active.http_path`: path to use in `GET` HTTP request to run on active checks
Expand Down Expand Up @@ -1339,6 +1351,22 @@ function _M.new(opts)
self.shm = ngx.shared[tostring(opts.shm_name)]
assert(self.shm, ("no shm found by name '%s'"):format(opts.shm_name))

-- load certificate and key
if opts.ssl_cert and opts.ssl_key then
if type(opts.ssl_cert) == "cdata" then
self.ssl_cert = opts.ssl_cert
else
self.ssl_cert = assert(ssl.parse_pem_cert(opts.ssl_cert))
end

if type(opts.ssl_key) == "cdata" then
self.ssl_key = opts.ssl_key
else
self.ssl_key = assert(ssl.parse_pem_priv_key(opts.ssl_key))
end

end

-- other properties
self.targets = nil -- list of targets, initially loaded, maintained by events
self.events = nil -- hash table with supported events (prevent magic strings)
Expand Down
1 change: 1 addition & 0 deletions lua-resty-healthcheck-scm-1.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ description = {
}
dependencies = {
"lua-resty-worker-events == 0.3.1",
"penlight == 1.7.0",
}
build = {
type = "builtin",
Expand Down
128 changes: 128 additions & 0 deletions t/17-mtls.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);

workers(1);

plan tests => repeat_each() * 4;

my $pwd = cwd();

our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
lua_shared_dict test_shm 8m;
lua_shared_dict my_worker_events 8m;
};

run_tests();

__DATA__

=== TEST 1: configure a MTLS probe
--- http_config eval
qq{
$::HttpConfig
}
--- config
location = /t {
content_by_lua_block {
local we = require "resty.worker.events"
assert(we.configure{ shm = "my_worker_events", interval = 0.1 })

local pl_file = require "pl.file"
local cert = pl_file.read("t/util/cert.pem", true)
local key = pl_file.read("t/util/key.pem", true)

local healthcheck = require("resty.healthcheck")
local checker = healthcheck.new({
name = "testing_mtls",
shm_name = "test_shm",
type = "http",
ssl_cert = cert,
ssl_key = key,
checks = {
active = {
http_path = "/status",
healthy = {
interval = 999, -- we don't want active checks
successes = 3,
},
unhealthy = {
interval = 999, -- we don't want active checks
tcp_failures = 3,
http_failures = 3,
}
},
passive = {
healthy = {
successes = 3,
},
unhealthy = {
tcp_failures = 3,
http_failures = 3,
}
}
}
})
ngx.say(checker ~= nil) -- true
}
}
--- request
GET /t
--- response_body
true


=== TEST 2: configure a MTLS probe with parsed cert/key
--- http_config eval
qq{
$::HttpConfig
}
--- config
location = /t {
content_by_lua_block {
local we = require "resty.worker.events"
assert(we.configure{ shm = "my_worker_events", interval = 0.1 })

local pl_file = require "pl.file"
local ssl = require "ngx.ssl"
local cert = ssl.parse_pem_cert(pl_file.read("t/util/cert.pem", true))
local key = ssl.parse_pem_priv_key(pl_file.read("t/util/key.pem", true))

local healthcheck = require("resty.healthcheck")
local checker = healthcheck.new({
name = "testing_mtls",
shm_name = "test_shm",
type = "http",
ssl_cert = cert,
ssl_key = key,
checks = {
active = {
http_path = "/status",
healthy = {
interval = 999, -- we don't want active checks
successes = 3,
},
unhealthy = {
interval = 999, -- we don't want active checks
tcp_failures = 3,
http_failures = 3,
}
},
passive = {
healthy = {
successes = 3,
},
unhealthy = {
tcp_failures = 3,
http_failures = 3,
}
}
}
})
ngx.say(checker ~= nil) -- true
}
}
--- request
GET /t
--- response_body
true
19 changes: 19 additions & 0 deletions t/util/cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDCTCCAfGgAwIBAgIUWWntedJ1yLAJE2baK/Mg06osmGAwDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UECgwJS29uZyBJbmMuMB4XDTIwMDQyMzIwMjcwMFoXDTMwMDQy
MTIwMjcwMFowFDESMBAGA1UECgwJS29uZyBJbmMuMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAvVBrEH34MzwKlkBapiNyXr9huSShuojy+7i/01BSFng3
1TiejXJ3pEjykZqt7ENkZ6+BTYUdb9klK221yXiSyX71x97O0WHHuhH/m4XwGiIH
YPBHdg+ExdMRflXgwtlW3of2hTWxkPkPQDPhoSQVMc5DkU7EOgrTxkv1rUWVAed4
gSK4IT2AkhKwOSkewZANj2bnK5Evf71ACyJd7IQbJAIYoKBwRJAUXJMA7XAreIB+
nEr9whNYTklhB4aEa2wtOQuiQubIMJzdOryEX5nufH+tL4p1QKhRPFAqqtJ2Czgw
YZY/v9IrThl19r0nL7FIvxFDNIMeOamJxDLQqsh9NwIDAQABo1MwUTAdBgNVHQ4E
FgQU9t6YAdQ5mOXeqvptN5l3yYZGibEwHwYDVR0jBBgwFoAU9t6YAdQ5mOXeqvpt
N5l3yYZGibEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAhi83
aXsfJGqr9Zb1guWxbI8uKoG6o88ptXjV2c6dJnxXag0A/Rj+bX2bcPkN2kvQksNl
MBUQlniOydZfsBUAoC0V7yyGUv9eO2RIeFnnNpRXNu+n+Kg2bvgvu8BKNNNOASZv
+Vmzvo9lbfhS9MNAxYk9eTiPNUZ3zn2RfFyT6YWWJbRjk//EAlchyud3XGug9/hw
c05dtzWEYT8GdzMd+Y1/2kR5r/CapSj7GEqL5T3+zDIfjbhTokV7WBrw6og2avoZ
vzrF8xWucry5/2mKQbRxMyCtKYUKTcoLzF4HrNQCETm0n9qUODrHER7Wit9fQFZX
1GEA3BkX2tsbIVVaig==
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions t/util/key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC9UGsQffgzPAqW
QFqmI3Jev2G5JKG6iPL7uL/TUFIWeDfVOJ6NcnekSPKRmq3sQ2Rnr4FNhR1v2SUr
bbXJeJLJfvXH3s7RYce6Ef+bhfAaIgdg8Ed2D4TF0xF+VeDC2Vbeh/aFNbGQ+Q9A
M+GhJBUxzkORTsQ6CtPGS/WtRZUB53iBIrghPYCSErA5KR7BkA2PZucrkS9/vUAL
Il3shBskAhigoHBEkBRckwDtcCt4gH6cSv3CE1hOSWEHhoRrbC05C6JC5sgwnN06
vIRfme58f60vinVAqFE8UCqq0nYLODBhlj+/0itOGXX2vScvsUi/EUM0gx45qYnE
MtCqyH03AgMBAAECggEAA1hWa/Yt2onnDfyZHXJm5PGwwlq5WNhuorADA7LZoHgD
VIspkgpBvu9jCduX0yLltUdOm5YMjRtjIr9PhP3SaikKIrv3H5AAvXLv90mIko2j
X70fJiDkEbLHDlpqHEdG16vDWVs3hf5AnLvN8tD2ZujkHL8tjHEAiPJyptsh5OSw
XaltCD67U940XXJ89x0zFZ/3RoRk78wX3ELz7/dY0cMnslMavON+LYTq9hQZyVmm
nOhZICWerKjax4t5f9PZ/zM6IhEVrUhw2WrC31tgRo+ITCIA/nkKid8vNhkiLVdw
jTyAYDLgYW7K8/zVrzmV9TOr3CaZHLQxnF/LMpIEAQKBgQDjnA/G4g2mDD7lsqU1
N3it87v2VBnZPFNW6L17Qig+2BDTXg1kadFBlp8qtEJI+H5axVSmzsrlmATJVhUK
iYOQwiEsQnt4tGmWZI268NAIUtv0TX0i9yscsezmvGABMcyBCF7ZwFhUfhy0pn1t
kzmbYN4AjYdcisCnSusoMD92NwKBgQDU7YVNuieMIZCIuSxG61N1+ZyX3Ul5l6KU
m1xw1PZvugqXnQlOLV/4Iaz86Vvlt2aDqTWO/iv4LU7ixNdhRtxFIU/b2a8DzDOw
ijhzMGRJqJOdi1NfciiIWHyrjRmGbhCgm784vqV7qbQomiIsjgnDvjoZkossZMiJ
63vs7huxAQKBgQDiQjT8w6JFuk6cD+Zi7G2unmfvCtNXO7ys3Fffu3g+YJL5SrmN
ZBN8W7qFvQNXfo48tYTc/Rx8941qh4QLIYAD2rcXRE9xQgbkVbj+aHykiZnVVWJb
69CTidux0vist1BPxH5lf+tOsr7eZdKxpnTRnI2Thx1URSoWI0d4f93WKQKBgBXn
kW0bl3HtCgdmtU1ebCmY0ik1VJezp8AN84aQAgIga3KJbymhtVu7ayZhg1iwc1Vc
FOxu7WsMji75/QY+2e4qrSJ61GxZl3+z2HbRJaAGPZlZeew5vD26jKjBTTztGbzM
CPH3euKr5KLAqH9Y5VxDt4pl7vdULuUxWoBXRnYBAoGAHIFMYiCdXETtrFHKVTzc
vm4P24PnsNHoDTGMXPeRYRKF2+3VEJrwp1Q3fue4Go4zFB8I6nhNVIbh4dIHxFab
hyxZvGWGUgRvTvD4VYn/YHVoSf2/xNZ0r/S2LKomp+jwoWKfukbCoDjAOWvnK5iD
o41Tn0yhzBdnrYguKznGR3g=
-----END PRIVATE KEY-----

0 comments on commit 8a6360c

Please sign in to comment.