Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
DNS testing tools
Remoh
Commits
a5251758
Commit
a5251758
authored
Mar 27, 2020
by
Stephane Bortzmeyer
Browse files
Merge branch 'master' into check-all_ips
parents
279f4e71
22dea724
Changes
3
Hide whitespace changes
Inline
Side-by-side
README.md
View file @
a5251758
...
...
@@ -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$"
}
}
...
...
homer.py
View file @
a5251758
...
...
@@ -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:Pih46
k: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
)
...
...
tests.yaml
View file @
a5251758
...
...
@@ -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
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment