blob: dfc24ee3072ab1d76e4ee470f30e4f55dccbf561 (
plain)
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
|
#!/bin/ksh
ZONES_DIR=/var/nsd/zones/master/
DEFAULT_MASTER=fishfinger.buetow.org
DEFAULT_STANDBY=blowfish.buetow.org
determine_master_and_standby () {
local master=$DEFAULT_MASTER
local standby=$DEFAULT_STANDBY
# Weekly auto-failover for Let's Encrypt automation
local -i -r week_of_the_year=$(date +%U)
if [ $(( week_of_the_year % 2 )) -ne 0 ]; then
local tmp=$master
master=$standby
standby=$tmp
fi
local -i health_ok=1
if ! ftp -4 -o - https://$master/index.txt | grep -q "Welcome to $master"; then
echo "https://$master/index.txt IPv4 health check failed"
health_ok=0
elif ! ftp -6 -o - https://$master/index.txt | grep -q "Welcome to $master"; then
echo "https://$master/index.txt IPv6 health check failed"
health_ok=0
fi
if [ $health_ok -eq 0 ]; then
local tmp=$master
master=$standby
standby=$tmp
fi
echo "Master is $master, standby is $standby"
host $master | awk '/has address/ { print $(NF) }' >/var/nsd/run/master_a
host $master | awk '/has IPv6 address/ { print $(NF) }' >/var/nsd/run/master_aaaa
host $standby | awk '/has address/ { print $(NF) }' >/var/nsd/run/standby_a
host $standby | awk '/has IPv6 address/ { print $(NF) }' >/var/nsd/run/standby_aaaa
}
transform () {
sed -E '
/IN A .*; Enable failover/ {
/^standby/! {
s/^(.*) 300 IN A (.*) ; (.*)/\1 300 IN A '$(cat /var/nsd/run/master_a)' ; \3/;
}
/^standby/ {
s/^(.*) 300 IN A (.*) ; (.*)/\1 300 IN A '$(cat /var/nsd/run/standby_a)' ; \3/;
}
}
/IN AAAA .*; Enable failover/ {
/^standby/! {
s/^(.*) 300 IN AAAA (.*) ; (.*)/\1 300 IN AAAA '$(cat /var/nsd/run/master_aaaa)' ; \3/;
}
/^standby/ {
s/^(.*) 300 IN AAAA (.*) ; (.*)/\1 300 IN AAAA '$(cat /var/nsd/run/standby_aaaa)' ; \3/;
}
}
/ ; serial/ {
s/^( +) ([0-9]+) .*; (.*)/\1 '$(date +%s)' ; \3/;
}
'
}
zone_is_ok () {
local -r zone=$1
local -r domain=${zone%.zone}
dig $domain @localhost | grep -q "$domain.*IN.*NS"
}
failover_zone () {
local -r zone_file=$1
local -r zone=$(basename $zone_file)
# Race condition (e.g. script execution abored in the middle previous run)
if [ -f $zone_file.bak ]; then
mv $zone_file.bak $zone_file
fi
cat $zone_file | transform > $zone_file.new.tmp
grep -v ' ; serial' $zone_file.new.tmp > $zone_file.new.noserial.tmp
grep -v ' ; serial' $zone_file > $zone_file.old.noserial.tmp
echo "Has zone $zone_file changed?"
if diff -u $zone_file.old.noserial.tmp $zone_file.new.noserial.tmp; then
echo "The zone $zone_file hasn't changed"
rm $zone_file.*.tmp
return 0
fi
cp $zone_file $zone_file.bak
mv $zone_file.new.tmp $zone_file
rm $zone_file.*.tmp
echo "Reloading nsd"
nsd-control reload
if ! zone_is_ok $zone; then
echo "Rolling back $zone_file changes"
cp $zone_file $zone_file.invalid
mv $zone_file.bak $zone_file
echo "Reloading nsd"
nsd-control reload
zone_is_ok $zone
return 3
fi
for cleanup in invalid bak; do
if [ -f $zone_file.$cleanup ]; then
rm $zone_file.$cleanup
fi
done
echo "Failover of zone $zone to $MASTER completed"
return 1
}
main () {
determine_master_and_standby
local -i ec=0
for zone_file in $ZONES_DIR/*.zone; do
if ! failover_zone $zone_file; then
ec=1
fi
done
# ec other than 0: CRON will send out an E-Mail.
exit $ec
}
main
|