},
"locked": {
"lastModified": 1,
- "narHash": "sha256-nTSS6oSOmi4T40fXl2o8wfw1/6o2/PP4f8rHtVTGw2s=",
+ "narHash": "sha256-s6HoAgXQrELPNK0BwuMRmJiuAmNN8VvNhhS0K9hYmh4=",
"path": "../flakes",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-yqURiQf32DNTy5zfAIatoWwFTqvsGDQd+221BoSfsCY=",
+ "narHash": "sha256-KR4/Na/SqEfg9PNnBLk17lTn4LUU7irZGrgvw7TEUYQ=",
"path": "../systems/backup-2",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-2Q1QywPMmeYtlrSNE869LwUJQjtbRUXbDhNFT4WBRJE=",
+ "narHash": "sha256-7B/UHUhGyJRBRjEms+zI8ZhBAN1vE365GZw2ciJVg1M=",
"path": "../systems/dilion",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-E88xTYPerBoKGo+EB6RThKwM1AxuhPWhs583WxwD8cA=",
+ "narHash": "sha256-q1+zzXLioBDjua4Omke9ki0hUaW2rtqTMRUXZ/+uHwU=",
"path": "../systems/eldiron",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-Ejc4fEaRV8u1yWV+u4z6F2SAGDBYEubbgRoG7tE3ctM=",
+ "narHash": "sha256-tsZO/C4md/8qRfxIsvVgeMkB0iAEl4IJC5/i8t/li2I=",
"path": "../systems/monitoring-1",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-6hR+IuCejk0AIiwggSgrvCQXiRzbF5IiMFr3YqbBwZI=",
+ "narHash": "sha256-UrrTxZeyqV2cFsC3XKVrJoay7LdnE6OTZnBJfimPle4=",
"path": "../systems/quatresaisons",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "./private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-nTSS6oSOmi4T40fXl2o8wfw1/6o2/PP4f8rHtVTGw2s=",
+ "narHash": "sha256-s6HoAgXQrELPNK0BwuMRmJiuAmNN8VvNhhS0K9hYmh4=",
"path": "./flakes",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-yqURiQf32DNTy5zfAIatoWwFTqvsGDQd+221BoSfsCY=",
+ "narHash": "sha256-KR4/Na/SqEfg9PNnBLk17lTn4LUU7irZGrgvw7TEUYQ=",
"path": "../systems/backup-2",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-2Q1QywPMmeYtlrSNE869LwUJQjtbRUXbDhNFT4WBRJE=",
+ "narHash": "sha256-7B/UHUhGyJRBRjEms+zI8ZhBAN1vE365GZw2ciJVg1M=",
"path": "../systems/dilion",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-E88xTYPerBoKGo+EB6RThKwM1AxuhPWhs583WxwD8cA=",
+ "narHash": "sha256-q1+zzXLioBDjua4Omke9ki0hUaW2rtqTMRUXZ/+uHwU=",
"path": "../systems/eldiron",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-Ejc4fEaRV8u1yWV+u4z6F2SAGDBYEubbgRoG7tE3ctM=",
+ "narHash": "sha256-tsZO/C4md/8qRfxIsvVgeMkB0iAEl4IJC5/i8t/li2I=",
"path": "../systems/monitoring-1",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-6hR+IuCejk0AIiwggSgrvCQXiRzbF5IiMFr3YqbBwZI=",
+ "narHash": "sha256-UrrTxZeyqV2cFsC3XKVrJoay7LdnE6OTZnBJfimPle4=",
"path": "../systems/quatresaisons",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "./private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-yqURiQf32DNTy5zfAIatoWwFTqvsGDQd+221BoSfsCY=",
+ "narHash": "sha256-KR4/Na/SqEfg9PNnBLk17lTn4LUU7irZGrgvw7TEUYQ=",
"path": "../systems/backup-2",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-2Q1QywPMmeYtlrSNE869LwUJQjtbRUXbDhNFT4WBRJE=",
+ "narHash": "sha256-7B/UHUhGyJRBRjEms+zI8ZhBAN1vE365GZw2ciJVg1M=",
"path": "../systems/dilion",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-E88xTYPerBoKGo+EB6RThKwM1AxuhPWhs583WxwD8cA=",
+ "narHash": "sha256-q1+zzXLioBDjua4Omke9ki0hUaW2rtqTMRUXZ/+uHwU=",
"path": "../systems/eldiron",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-Ejc4fEaRV8u1yWV+u4z6F2SAGDBYEubbgRoG7tE3ctM=",
+ "narHash": "sha256-tsZO/C4md/8qRfxIsvVgeMkB0iAEl4IJC5/i8t/li2I=",
"path": "../systems/monitoring-1",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-6hR+IuCejk0AIiwggSgrvCQXiRzbF5IiMFr3YqbBwZI=",
+ "narHash": "sha256-UrrTxZeyqV2cFsC3XKVrJoay7LdnE6OTZnBJfimPle4=",
"path": "../systems/quatresaisons",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "./private/monitoring",
"type": "path"
},
dns = {
commands = {
check_dns = "$USER1$/check_dns -H $ARG1$ -s $HOSTADDRESS$ $ARG2$";
+ check_dns_soa = "$USER2$/check_dns_soa -H $ARG1$ -z $ARG2$ -M $ARG3$";
+ check_dnssec = "$USER2$/check_dnssec -z $ARG1$";
check_external_dns = "$USER1$/check_dns -H $ARG2$ -s $ARG1$ $ARG3$";
};
+ chunk = let
+ soa_plugin = pkgs.fetchurl {
+ name = "check_dns_soa";
+ url = "https://exchange.nagios.org/components/com_mtree/attachment.php?link_id=1429&cf_id=24";
+ sha256 = "sha256-Yy4XO19Fb7WdHZZmhUfyyAGBnxJyFWwc7U3HiWyE8wc=";
+ };
+ in ''
+ cp ${./plugins}/check_dnssec $out/
+ patchShebangs $out/check_dnssec
+ wrapProgram $out/check_dnssec --prefix PATH : ${lib.makeBinPath [
+ pkgs.bind.dnsutils pkgs.gnugrep pkgs.gawk pkgs.which pkgs.coreutils
+ ]}
+
+ cp ${soa_plugin} $out/check_dns_soa
+ chmod +xw $out/check_dns_soa
+ patchShebangs $out/check_dns_soa
+ sed -i -e 's/^use utils qw.*$/my %ERRORS = ("OK" => 0, "WARNING" => 1, "CRITICAL" => 2, "UNKNOWN" => 3);my $TIMEOUT = 10;/' -e '/^use lib /d' $out/check_dns_soa
+ wrapProgram $out/check_dns_soa --prefix PERL5LIB : ${pkgs.perlPackages.makePerlPath [
+ pkgs.perlPackages.NetDNS
+ ]}
+ '';
};
mdadm = {
commands = {
--- /dev/null
+#!/usr/bin/env bash
+
+# check_dnssec_expiry.sh
+#
+# Copyright 2017 by Mario Rimann <mario@rimann.org>
+# 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 validated 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
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
{ lib, pkgs, config, dns-nix, ... }:
+let
+ zonesWithDNSSec = lib.filterAttrs (k: v: v.dnssec.enable) config.myServices.dns.zones;
+ zoneToFile = name: v: pkgs.runCommand "${name}.zone" {
+ text = v;
+ passAsFile = [ "text" ];
+ # Automatically change the increment when relevant change
+ # happened (both serial and mta-sts)
+ } ''
+ mv "$textPath" $out
+ increment=$(( 100*($(date -u +%-H) * 60 + $(date -u +%-M))/1440 ))
+ sed -i -e "s/2022121902/$(date -u +%Y%m%d)$increment/g" $out
+ sed -i -e "s/20200109150200Z/$(date -u +%Y%m%d%H%M%SZ)/g" $out
+ '';
+in
{
options.myServices.dns = {
enable = lib.mkEnableOption "enable DNS resolver";
servers = config.myEnv.servers;
ips = i: { A = i.ip4; AAAA = i.ip6; };
letsencrypt = [ { tag = "issue"; value = "letsencrypt.org"; issuerCritical = false; } ];
- toKV = a: builtins.concatStringsSep ";" (builtins.attrValues (builtins.mapAttrs (n: v: "${n}=${v}") a));
+ toKV = a: let
+ removeOrder = n: lib.last (builtins.split "__" n);
+ in
+ builtins.concatStringsSep ";" (builtins.attrValues (builtins.mapAttrs (n: v: "${removeOrder n}=${v}") a));
mailMX = {
hasEmail = true;
subdomains = let
SOA = {
# yyyymmdd?? (increment ?? at each change)
serial = 2022121902; # Don't change this value, it is replaced automatically!
- refresh = 10800;
- retry = 3600;
- expire = 604800;
- minimum = 10800; # negative cache ttl
+ refresh = 3*60*60;
+ retry = 60*60;
+ expire = 14*24*60*60;
+ minimum = 3*60*60; # negative cache ttl
adminEmail = "hostmaster@immae.eu"; #email-address s/@/./
nameServer = "ns1.immae.eu.";
};
(toKV config.myEnv.mail.dkim.immae_eu.public)
];
};
- mailCommon = name: {
+ mailCommon = name: quarantine: {
MX = let
mxes = lib.filterAttrs (n: v: v ? mx && v.mx.enable) servers;
in
# MTA-STS
# https://blog.delouw.ch/2018/12/16/using-mta-sts-to-enhance-email-transport-security-and-privacy/
# https://support.google.com/a/answer/9261504
- _mta-sts.TXT = [ (toKV { v = "STSv1"; id = "20200109150200Z"; }) ]; # Don't change this value, it is updated automatically!
- _tls.subdomains._smtp.TXT = [ (toKV { v = "TLSRPTv1"; "rua" = "mailto:postmaster+mta-sts@immae.eu"; }) ];
+ _mta-sts.TXT = [ (toKV { _00__v = "STSv1"; id = "20200109150200Z"; }) ]; # Don't change this value, it is updated automatically!
+ _tls.subdomains._smtp.TXT = [ (toKV { _00__v = "TLSRPTv1"; rua = "mailto:postmaster+mta-sts@immae.eu"; }) ];
mta-sts = ips servers.eldiron.ips.main;
# DMARC
- _dmarc.TXT = [ (toKV { v = "DMARC1"; p = "none"; adkim = "r"; aspf = "r"; fo = "1"; rua = "mailto:postmaster+rua@immae.eu"; ruf = "mailto:postmaster+ruf@immae.eu"; }) ];
+ # p needs to be the first tag
+ _dmarc.TXT = [ (toKV { _00__v = "DMARC1"; _01__p = if quarantine then "quarantine" else "none"; adkim = "s"; aspf = "s"; fo = "1"; rua = "mailto:postmaster+rua@immae.eu"; ruf = "mailto:postmaster+ruf@immae.eu"; }) ];
};
# SPF
- TXT = [ (toKV { v = "spf1 mx ~all"; }) ];
+ TXT = [ (toKV { _00__v = "spf1 mx ~all"; }) ];
};
};
};
dns-nix.lib.types.zone.getSubModules ++ [
({ name, ... }: {
options = {
+ dnssec = lib.mkOption {
+ default.enable = false;
+ type = lib.types.submodule {
+ options = {
+ enable = lib.mkEnableOption "Configure dnssec for this domain";
+ };
+ };
+ };
hasEmail = lib.mkEnableOption "This domain has e-mails configuration";
emailPolicies = lib.mkOption {
default = {};
zoneHeader
(ips servers.eldiron.ips.main)
{
- ns = [ "immae" ];
+ dnssec.enable = true;
+ ns = [ "immae" "raito" ];
CAA = letsencrypt;
+ extraConfig = ''
+ notify yes;
+ '';
+ slaves = [ "raito" ];
}
];
"immae.dev" = lib.mkMerge [
{
+ dnssec.enable = true;
extraConfig = ''
notify yes;
'';
];
"immae.eu" = lib.mkMerge [
{
+ dnssec.enable = true;
extraConfig = ''
notify yes;
'';
zoneHeader
(ips servers.eldiron.ips.production)
{
- ns = [ "immae" "raito" ];
+ ns = [ "immae" ];
+ # Cannot put ns2.immae.eu as glue record as it takes ages to propagate.
+ # And gandi only accepts NS records with glues in their interface
+ NS = [ "kurisu.dual.lahfa.xyz." ];
CAA = letsencrypt;
# ns1 has glue records in gandi.net
{
# Machines local users
emailPolicies.localhost.receive = false;
- subdomains.localhost = lib.mkMerge [ (mailCommon "immae.eu") mailSend ];
+ subdomains.localhost = lib.mkMerge [ (mailCommon "immae.eu" true) mailSend ];
emailPolicies.eldiron.receive = true;
- subdomains.eldiron = lib.mkMerge [ (mailCommon "immae.eu") mailSend ];
+ subdomains.eldiron = lib.mkMerge [ (mailCommon "immae.eu" true) mailSend ];
}
{
# For each server "server" and each server ip group "ipgroup",
zones =
builtins.mapAttrs (name: v: {
master = true;
- extraConfig = v.extraConfig;
+ extraConfig = v.extraConfig + lib.optionalString v.dnssec.enable ''
+ key-directory "/var/lib/named/dnssec_keys";
+ dnssec-policy default;
+ inline-signing yes;
+ '';
masters = [];
slaves =
lib.flatten (map (n: builtins.attrValues config.myEnv.dns.ns.${n}) v.slaves);
- file = pkgs.runCommand "${name}.zone" {
- text = v;
- passAsFile = [ "text" ];
- # Automatically change the increment when relevant change
- # happened (both serial and mta-sts)
- } ''
- mv "$textPath" $out
- increment=$(( 100*($(date -u +%-H) * 60 + $(date -u +%-M))/1440 ))
- sed -i -e "s/2022121902/$(date -u +%Y%m%d)$increment/g" $out
- sed -i -e "s/20200109150200Z/$(date -u +%Y%m%d%H%M%SZ)/g" $out
- '';
+ file = if v.dnssec.enable then "/var/run/named/dnssec-${name}.zone" else zoneToFile name v;
}) config.myServices.dns.zones;
};
+ systemd.services.bind.serviceConfig.StateDirectory = "named";
+ systemd.services.bind.preStart = lib.mkAfter
+ (builtins.concatStringsSep "\n" (lib.mapAttrsToList (name: v: ''
+ install -m444 ${zoneToFile name v} /var/run/named/dnssec-${name}.zone
+ '') zonesWithDNSSec) + ''
+ install -dm755 -o named /var/lib/named/dnssec_keys
+ '');
myServices.monitoring.fromMasterActivatedPlugins = [ "dns" ];
myServices.monitoring.fromMasterObjects.service = lib.mkMerge (lib.mapAttrsToList (name: z:
lib.optional (builtins.elem "immae" z.ns) {
servicegroups = "webstatus-dns";
_webstatus_name = name;
} ++
- lib.optional (builtins.elem "raito" z.ns) {
- service_description = "raito dns is active and authoritative for ${name}";
+ lib.optionals (builtins.elem "raito" z.ns) [
+ {
+ service_description = "raito dns is active and authoritative for ${name}";
+ host_name = config.hostEnv.fqdn;
+ use = "dns-service";
+ check_command = ["check_external_dns" "kurisu.dual.lahfa.xyz" name "-A"];
+
+ servicegroups = "webstatus-dns";
+ _webstatus_name = "${name} (Secondary DNS Raito)";
+ }
+ {
+ service_description = "raito dns is up to date for ${name}";
+ host_name = config.hostEnv.fqdn;
+ use = "dns-service";
+ check_command = ["check_dns_soa" "kurisu.dual.lahfa.xyz" name config.hostEnv.fqdn];
+
+ servicegroups = "webstatus-dns";
+ _webstatus_name = "${name} (Secondary DNS Raito up to date)";
+ }
+ ] ++
+ lib.optional z.dnssec.enable {
+ service_description = "DNSSEC is active and not expired for ${name}";
host_name = config.hostEnv.fqdn;
use = "dns-service";
- check_command = ["check_external_dns" "kurisu.dual.lahfa.xyz" name "-A"];
+ check_command = ["check_dnssec" name];
servicegroups = "webstatus-dns";
- _webstatus_name = "${name} (Secondary DNS Raito)";
+ _webstatus_name = "${name} (DNSSEC)";
}
) config.myServices.dns.zones);
};
}
zoneHeader
mailMX
- (mailCommon "immae.fr")
+ (mailCommon "immae.fr" true)
(ips servers.eldiron.ips.main)
{
ns = [ "immae" "raito" ];
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
config = lib.mkIf config.myServices.mail.enable {
myServices.dns.zones."immae.eu" = with config.myServices.dns.helpers; lib.mkMerge [
mailMX
- (mailCommon "immae.eu")
+ (mailCommon "immae.eu" true)
mailSend
{
# Virtual forwards and mailboxes for real users
# system virtual mailboxes:
# devnull, printer, testconnect
emailPolicies."".receive = true;
- subdomains.mail = lib.mkMerge [ (mailCommon "immae.eu") mailSend ];
+ subdomains.mail = lib.mkMerge [ (mailCommon "immae.eu" true) mailSend ];
subdomains.smtp = ips servers.eldiron.ips.main;
# DMARC reports
myServices.dns.zones."immae.eu".subdomains.lists =
with config.myServices.dns.helpers; lib.mkMerge [
(ips servers.eldiron.ips.main)
- (mailCommon "immae.eu")
+ (mailCommon "immae.eu" false)
mailSend
];
{
outils = ips servers.eldiron.ips.main;
tools = lib.mkMerge [
- (mailCommon "immae.eu")
+ (mailCommon "immae.eu" true)
mailSend
(ips servers.eldiron.ips.main)
];
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-rybO4c9UB9a34Xgoh+ToYz36Dz2OM1sgYxi3m00+W+E=",
+ "narHash": "sha256-DN3hgnw6hXCrSGXep4mumwksWSggsuyyaKXuKvswXl8=",
"path": "../../flakes/private/monitoring",
"type": "path"
},