-
-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathcheck_dnssec_expiry.sh
executable file
·195 lines (167 loc) · 7.38 KB
/
check_dnssec_expiry.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#!/usr/bin/env bash
# check_dnssec_expiry.sh
#
# Copyright 2017 by Mario Rimann <[email protected]>
# Licensed under the permissive MIT license, see LICENSE.md
#
# Development of this script was partially sponsored by my
# employer internezzo, see http://www.internezzo.ch
#
# If this script helps you to make your work easier, please consider
# to give feedback or do something good, see https://rimann.org/support
usage() {
cat - >&2 << _EOT_
usage $0 -z <zone> [-w <warning %>] [-c <critical %>] [-r <resolver>] [-f <always failing domain>]
-z <zone>
specify zone to check
-w <critical %>
warning time left percentage
-c <critical %>
critical time left percentage
-r <resolver>
specify which resolver to use.
-f <always failing domain>
specify a domain that will always fail DNSSEC.
used to test if DNSSEC is supported in used tools.
-t <DNS record type to check>
specify a DNS record type for calculating the remaining lifetime.
For example SOA, A, etc.
_EOT_
exit 255
}
# Parse the input options
while getopts ":z:w:c:r:f:h:t:" opt; do
case $opt in
z)
zone=$OPTARG
;;
w)
warning=$OPTARG
;;
c)
critical=$OPTARG
;;
r)
resolver=$OPTARG
;;
f)
alwaysFailingDomain=$OPTARG
;;
t)
recordType=$OPTARG
;;
h)
usage ;;
esac
done
# Check if dig is available at all - fail hard if not
pathToDig=$( which dig )
if [[ ! -e $pathToDig ]]; then
echo "No executable of dig found, cannot proceed without dig. Sorry!"
exit 1
fi
# Check if we got a zone to validate - fail hard if not
if [[ -z $zone ]]; then
echo "Missing zone to test - please provide a zone via the -z parameter."
usage
exit 3
fi
# Check if we got warning/critical percentage values, use defaults if not
if [[ -z $warning ]]; then
warning=20
fi
if [[ -z $critical ]]; then
critical=10
fi
# Use Google's 8.8.8.8 resolver as fallback if none is provided
if [[ -z $resolver ]]; then
resolver="8.8.8.8"
fi
if [[ -z $alwaysFailingDomain ]]; then
alwaysFailingDomain="dnssec-failed.org"
fi
# Use SOA record type as fallback
if [[ -z $recordType ]]; then
recordType="SOA"
fi
# Check the resolver to properly validate DNSSEC at all (if he doesn't, every further test is futile and a waste of bandwith)
checkResolverDoesDnssecValidation=$(dig +nocmd +nostats +noquestion $alwaysFailingDomain @${resolver} | grep "opcode: QUERY" | grep "status: SERVFAIL")
if [[ -z $checkResolverDoesDnssecValidation ]]; then
echo "WARNING: Resolver seems to not validate DNSSEC signatures - going further seems hopeless right now."
exit 1
fi
# Check if the resolver delivers an answer for the domain to test
checkDomainResolvableWithDnssecEnabledResolver=$(dig +short @${resolver} SOA $zone)
if [[ -z $checkDomainResolvableWithDnssecEnabledResolver ]]; then
checkDomainResolvableWithDnssecValidationExplicitelyDisabled=$(dig +short @${resolver} SOA $zone +cd)
if [[ ! -z $checkDomainResolvableWithDnssecValidationExplicitelyDisabled ]]; then
echo "CRITICAL: The domain $zone can be resolved without DNSSEC validation - but will fail on resolvers that do validate DNSSEC."
exit 2
else
echo "CRITICAL: The domain $zone cannot be resolved via $resolver as resolver while DNSSEC validation is active."
exit 2
fi
fi
# Check if the domain is DNSSEC signed at all
# (and emerge a WARNING in that case, since this check is about testing DNSSEC being "present" and valid which is not the case for an unsigned zone)
checkZoneItselfIsSignedAtAll=$( dig $zone @$resolver DS +short )
if [[ -z $checkZoneItselfIsSignedAtAll ]]; then
echo "WARNING: Zone $zone seems to be unsigned itself (= resolvable, but no DNSSEC involved at all)"
exit 1
fi
# Check if there are multiple RRSIG responses and check them one after the other
now=$(date +"%s")
rrsigEntries=$( dig @$resolver $recordType $zone +dnssec | grep RRSIG )
if [[ -z $rrsigEntries ]]; then
echo "CRITICAL: There is no RRSIG for the SOA of your zone."
exit 2
else
while read -r rrsig; do
# Get the RRSIG entry and extract the date out of it
expiryDateOfSignature=$( echo $rrsig | awk '{print $9}')
checkValidityOfExpirationTimestamp=$( echo $expiryDateOfSignature | egrep '[0-9]{14}')
if [[ -z $checkValidityOfExpirationTimestamp ]]; then
echo "UNKNOWN: Something went wrong while checking the expiration of the RRSIG entry - investigate please".
exit 3
fi
inceptionDateOfSignature=$( echo $rrsig | awk '{print $10}')
checkValidityOfInceptionTimestamp=$( echo $inceptionDateOfSignature | egrep '[0-9]{14}')
if [[ -z $checkValidityOfInceptionTimestamp ]]; then
echo "UNKNOWN: Something went wrong while checking the inception date of the RRSIG entry - investigate please".
exit 3
fi
# Fiddle out the expiry and inceptiondate of the signature to have a base to do some calculations afterwards
expiryDateAsString="${expiryDateOfSignature:0:4}-${expiryDateOfSignature:4:2}-${expiryDateOfSignature:6:2} ${expiryDateOfSignature:8:2}:${expiryDateOfSignature:10:2}:00"
expiryDateOfSignatureAsUnixTime=$( date -u -d "$expiryDateAsString" +"%s" 2>/dev/null )
if [[ $? -ne 0 ]]; then
# if we come to this place, something must have gone wrong converting the date-string. This can happen as e.g. MacOS X and Linux don't behave the same way in this topic...
expiryDateOfSignatureAsUnixTime=$( date -j -u -f "%Y-%m-%d %T" "$expiryDateAsString" +"%s" )
fi
inceptionDateAsString="${inceptionDateOfSignature:0:4}-${inceptionDateOfSignature:4:2}-${inceptionDateOfSignature:6:2} ${inceptionDateOfSignature:8:2}:${inceptionDateOfSignature:10:2}:00"
inceptionDateOfSignatureAsUnixTime=$( date -u -d "$inceptionDateAsString" +"%s" 2>/dev/null )
if [[ $? -ne 0 ]]; then
# if we come to this place, something must have gone wrong converting the date-string. This can happen as e.g. MacOS X and Linux don't behave the same way in this topic...
inceptionDateOfSignatureAsUnixTime=$( date -j -u -f "%Y-%m-%d %T" "$inceptionDateAsString" +"%s" )
fi
# calculate the remaining lifetime of the signature
totalLifetime=$( expr $expiryDateOfSignatureAsUnixTime - $inceptionDateOfSignatureAsUnixTime)
remainingLifetimeOfSignature=$( expr $expiryDateOfSignatureAsUnixTime - $now)
remainingPercentage=$( expr "100" \* $remainingLifetimeOfSignature / $totalLifetime)
# store the result of this single RRSIG's check
if [[ -z $maxRemainingLifetime || $remainingLifetimeOfSignature -gt $maxRemainingLifetime ]]; then
maxRemainingLifetime=$remainingLifetimeOfSignature
maxRemainingPercentage=$remainingPercentage
fi
done <<< "$rrsigEntries"
fi
# determine if we need to alert, and if so, how loud to cry, depending on warning/critial threshholds provided
if [[ $maxRemainingPercentage -lt $critical ]]; then
echo "CRITICAL: DNSSEC signature for $zone is very short before expiration! ($maxRemainingPercentage% remaining) | sig_lifetime=$maxRemainingLifetime sig_lifetime_percentage=$remainingPercentage%;$warning;$critical"
exit 2
elif [[ $remainingPercentage -lt $warning ]]; then
echo "WARNING: DNSSEC signature for $zone is short before expiration! ($maxRemainingPercentage% remaining) | sig_lifetime=$maxRemainingLifetime sig_lifetime_percentage=$remainingPercentage%;$warning;$critical"
exit 1
else
echo "OK: DNSSEC signatures for $zone seem to be valid and not expired ($maxRemainingPercentage% remaining) | sig_lifetime=$maxRemainingLifetime sig_lifetime_percentage=$remainingPercentage%;$warning;$critical"
exit 0
fi