Commit a5251758 authored by Stephane Bortzmeyer's avatar Stephane Bortzmeyer
Browse files

Merge branch 'master' into check-all_ips

parents 279f4e71 22dea724
......@@ -51,6 +51,12 @@ Possible options, besides `--dot`:
* --insecure or -k: Does not check the certificate
* -4: Uses only IPv4
* -6: Uses only IPv6
* --dnssec: requests DNSSEC data (signatures)
* --noedns: no EDNS (default is to indicate EDNS support)
* --ecs: send ECS, my subnet to auth. servers (default is to refuse
it)
* --key KEYINBASE64: authentifies a DoT resolver with its public
key. Example: `homer.py --key "62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4=" --dot 145.100.185.15 IN NS`
* --check: Run a set of tests (see below)
### Check
......@@ -133,6 +139,7 @@ monitoring plugins conventions:
* -P: uses the HTTP method POST
* -h: uses the HTTP method HEAD
* -i: insecure (do not check the certificate)
* -k: authenticated the DoT server with this public key
For Icinga, the following definition enables the plugin:
......@@ -165,7 +172,8 @@ object CheckCommand "dot_monitor" {
"-t" = "$dot_type$",
"-P" = "$dot_post$",
"-i" = "$dot_insecure$",
"-h" = "$dot_head$"
"-h" = "$dot_head$",
"-k" = "$dot_key$"
}
}
......
......@@ -41,9 +41,12 @@ post = False
head = False
dnssec = False
edns = True
no_ecs = True
sni = True
rtype = 'AAAA'
vhostname = None
tests = 1 # Number of repeated tests
key = None # SPKI
ifile = None # Input file
delay = None
forceIPv4 = False
......@@ -199,8 +202,13 @@ class CustomException(Exception):
class Request:
def __init__(self, qname, qtype=rtype, use_edns=edns, want_dnssec=dnssec):
if no_ecs:
opt = dns.edns.ECSOption(address='', srclen=0) # Disable ECS (RFC 7871, section 7.1.2)
options = [opt]
else:
options = None
self.message = dns.message.make_query(qname, dns.rdatatype.from_text(qtype),
use_edns=use_edns, want_dnssec=want_dnssec)
use_edns=use_edns, want_dnssec=want_dnssec, options=options)
self.message.flags |= dns.flags.AD # Ask for validation
self.ok = True
......@@ -355,7 +363,8 @@ class ConnectionDoT(Connection):
OpenSSL.SSL.VERIFY_CLIENT_ONCE,
lambda conn, cert, errno, depth, preverify_ok: preverify_ok)
self.session = OpenSSL.SSL.Connection(self.context, self.sock)
self.session.set_tlsext_host_name(canonicalize(self.check).encode()) # Server Name Indication (SNI)
if sni:
self.session.set_tlsext_host_name(canonicalize(self.check).encode()) # Server Name Indication (SNI)
try:
self.session.connect((self.addr))
# TODO We may here have exceptions such as OpenSSL.SSL.ZeroReturnError
......@@ -364,23 +373,29 @@ class ConnectionDoT(Connection):
if self.verbose:
print("Timeout")
return False
self.cert = self.session.get_peer_certificate()
# RFC 7858, section 4.2 and appendix A
self.cert = self.session.get_peer_certificate()
self.publickey = self.cert.get_pubkey()
if verbose or key is not None:
self.hasher.update(OpenSSL.crypto.dump_publickey(OpenSSL.crypto.FILETYPE_ASN1,
self.publickey))
self.digest = self.hasher.digest()
key_string = base64.standard_b64encode(self.digest).decode()
if verbose:
print("Certificate #%x for \"%s\", delivered by \"%s\"" % \
(self.cert.get_serial_number(),
self.cert.get_subject().commonName,
self.cert.get_issuer().commonName))
self.hasher.update(OpenSSL.crypto.dump_publickey(OpenSSL.crypto.FILETYPE_ASN1,
self.publickey))
self.digest = self.hasher.digest()
print("Public key is pin-sha256=\"%s\"" % \
base64.standard_b64encode(self.digest).decode())
key_string)
if not insecure:
valid = validate_hostname(self.check, self.cert)
if not valid:
error("Certificate error: \"%s\" is not in the certificate" % (self.check))
if key is None:
valid = validate_hostname(self.check, self.cert)
if not valid:
error("Certificate error: \"%s\" is not in the certificate" % (self.check))
else:
if key_string != key:
error("Key error: expected \"%s\", got \"%s\"" % (key, key_string))
signal.alarm(0)
return True
......@@ -716,7 +731,9 @@ if not monitoring:
optlist, args = getopt.getopt (sys.argv[1:], "hvPkeV:r:f:d:t46",
["help", "verbose", "dot", "head",
"insecure", "POST", "vhost=",
"dnssec", "noedns","repeat=", "file=", "delay=", "v4only", "v6only",
"dnssec", "noedns", "ecs", "repeat=", "file=", "delay=",
"key=", "nosni",
"v4only", "v6only",
"check", "mandatory-level="])
for option, value in optlist:
if option == "--help" or option == "-h":
......@@ -736,8 +753,15 @@ if not monitoring:
insecure = True
elif option == "--dnssec":
dnssec = True
elif option == "--noedns":
elif option == "--nosni":
sni = False
elif option == "--noedns": # Warning: it will mean the
# resolver may send ECS
# information to the
# authoritative name servers.
edns = False
elif option == "--ecs":
no_ecs = False
elif option == "--repeat" or option == "-r":
tests = int(value)
if tests <= 1:
......@@ -748,6 +772,8 @@ if not monitoring:
error("--delay needs a value > 0")
elif option == "--file" or option == "-f":
ifile = value
elif option == "--key":
key = value
elif option == "-4" or option == "v4only":
forceIPv4 = True
elif option == "-6" or option == "v6only":
......@@ -769,6 +795,9 @@ if not monitoring:
if dot and (post or head):
usage("POST or HEAD makes non sense for DoT")
sys.exit(1)
if not edns and not no_ecs:
usage("ECS requires EDNS")
sys.exit(1)
if mandatory_level is not None and \
mandatory_level not in mandatory_levels.keys():
usage("Unknown mandatory level \"%s\"" % mandatory_level)
......@@ -794,7 +823,7 @@ else: # Monitoring plugin
dot = (me == "check_dot")
name = None
try:
optlist, args = getopt.getopt (sys.argv[1:], "H:n:p:V:t:e:Pih46")
optlist, args = getopt.getopt (sys.argv[1:], "H:n:p:V:t:e:Pih46k:x")
for option, value in optlist:
if option == "-H":
host = value
......@@ -814,10 +843,14 @@ else: # Monitoring plugin
head = True
elif option == "-i":
insecure = True
elif option == "-x":
sni = False
elif option == "-4":
forceIPv4 = True
elif option == "-6":
forceIPv6 = True
elif option == "-k":
key = value
else:
# Should never occur, it is trapped by getopt
print("Unknown option %s" % option)
......
......@@ -467,6 +467,34 @@ tests:
partstderr: 'not connect to'
stdout: ''
- exe: './homer.py'
name: '[dot] Authenticates with the key (SPKI)'
markers:
- 'dot'
timeout: 5
args:
- '--dot'
- '--key'
- '62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4='
- '145.100.185.15'
- 'sinodun.com'
retcode: 0
stderr: ''
- exe: './homer.py'
name: '[dot] Authenticates with the WRONG key (SPKI)'
markers:
- 'dot'
timeout: 5
args:
- '--dot'
- '--key'
- '62pKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL5='
- '145.100.185.15'
- 'sinodun.com'
retcode: 1
partstderr: 'Key error'
################################################################################
# check_dot
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment