3 # Copyright (C) 2009 Google Inc.
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 # This is an example ganeti hook that sets up an IPsec ESP link between all the
22 # nodes of a cluster for a given list of protocols.
24 # When run on cluster initialization it will create the shared key to be used
25 # for all the links. When run on node add/removal it will reconfigure IPsec
26 # on each node of the cluster.
30 LOCALSTATEDIR=@LOCALSTATEDIR@
31 SYSCONFDIR=@SYSCONFDIR@
33 GNTDATA=${LOCALSTATEDIR}/lib/ganeti
35 LOCKFILE=${LOCALSTATEDIR}/lock/ganeti_ipsec
36 CRYPTALGO=rijndael-cbc
37 KEYPATH=${GNTDATA}/ipsec.key
39 PROTOSTOSEC="icmp tcp"
41 # On debian/ubuntu this file is automatically reloaded on boot
42 SETKEYCONF=${SYSCONFDIR}/ipsec-tools.conf
43 SETKEYCUSTOMCONF=${SYSCONFDIR}/ipsec-tools-custom.conf
44 AUTOMATIC_MARKER="# Automatically generated rules"
47 NODES=${GNTDATA}/ssconf_node_secondary_ips
48 MASTERNAME_FILE=${GNTDATA}/ssconf_master_node
49 MASTERIP_FILE=${GNTDATA}/ssconf_master_ip
51 SSHOPTS="-q -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no \
52 -oGlobalKnownHostsFile=${GNTDATA}/known_hosts"
58 # Perform all registered cleanup operation
60 for (( i=${#CLEANUP[@]}; i >= 0 ; --i )); do
66 # Acquire the lockfile associated with system ipsec configuration.
67 lockfile-create "$LOCKFILE" || exit 1
68 CLEANUP+=("lockfile-remove $LOCKFILE")
71 update_system_ipsec() {
72 # Update system ipsec configuration.
73 # $1 : temporary location of a working configuration
76 mv "$TMPCONF" "$SETKEYCONF"
77 setkey -f "$SETKEYCONF"
81 # Obtain the IPsec keyfile from the master.
82 local MASTERIP=$(< "$MASTERIP_FILE")
83 scp $SCPOPTS "$MASTERIP":"$KEYPATH" "$KEYPATH"
87 # Output IPsec key, if no key is present on the node
88 # obtain it from master.
89 if [[ ! -f "$KEYPATH" ]]; then
92 cut -d ' ' -f2 "$KEYPATH"
96 # Output IPsec key sequence number, if no key is present
97 # on the node exit with error.
98 if [[ ! -f "$KEYPATH" ]]; then
99 echo 'Cannot obtain key timestamp, no key file.' >&2
102 cut -d ' ' -f1 "$KEYPATH"
105 update_ipsec_conf() {
106 # Generate a new IPsec configuration and update the system.
107 local TMPCONF=$(mktemp)
108 CLEANUP+=("rm -f $TMPCONF")
109 ESCAPED_HOSTNAME=$(sed 's/\./\\./g' <<< "$HOSTNAME")
110 local MYADDR=$(grep -E "^$ESCAPED_HOSTNAME\\>" "$NODES" | cut -d ' ' -f2)
111 local KEY=$(gather_key)
112 local SETKEYPATH=$(which setkey)
115 echo "#!$SETKEYPATH -f"
117 echo "# Configuration for $MYADDR"
119 echo '# This file has been automatically generated. Do not modify by hand,'
120 echo "# add your own rules to $SETKEYCUSTOMCONF instead."
122 echo '# Flush SAD and SPD'
126 if [[ -f "$SETKEYCUSTOMCONF" ]]; then
127 echo "# Begin custom rules from $SETKEYCUSTOMCONF"
128 cat "$SETKEYCUSTOMCONF"
129 echo "# End custom rules from $SETKEYCUSTOMCONF"
132 echo "$AUTOMATIC_MARKER"
133 for node in $(cut -d ' ' -f2 "$NODES") ; do
134 if [[ "$node" != "$MYADDR" ]]; then
136 for port in $TCPTOIGNORE ; do
137 echo "spdadd $MYADDR[$port] $node tcp -P out none;"
138 echo "spdadd $node $MYADDR[$port] tcp -P in none;"
139 echo "spdadd $MYADDR $node[$port] tcp -P out none;"
140 echo "spdadd $node[$port] $MYADDR tcp -P in none;"
143 echo "add $MYADDR $node esp 0x201 -E $CRYPTALGO $KEY;"
144 echo "add $node $MYADDR esp 0x201 -E $CRYPTALGO $KEY;"
145 for proto in $PROTOSTOSEC ; do
146 echo "spdadd $MYADDR $node $proto -P out ipsec esp/transport//require;"
147 echo "spdadd $node $MYADDR $proto -P in ipsec esp/transport//require;"
155 update_system_ipsec "$TMPCONF"
159 # Reconfigure IPsec on the system when a new key is generated
160 # on the master (assuming the current configuration is working
161 # and a new key is about to be generated on the master).
162 if [[ ! -f "$KEYPATH" ]]; then
163 echo 'Asking to regenerate with new key, but no old key.' >&2
166 local CURSEQNO=$(gather_key_seqno)
168 local NEWSEQNO=$(gather_key_seqno)
169 while [[ $NEWSEQNO -le $CURSEQNO ]]; do
170 # Master did not update yet, wait..
171 sleep $REGEN_KEY_WAIT
173 NEWSEQNO=$(gather_key_seqno)
179 # Unconfigure IPsec on the system, removing the key and
180 # the rules previously generated.
183 local TMPCONF=$(mktemp)
184 CLEANUP+=("rm -f $TMPCONF")
185 # Remove all auto-generated rules
186 sed "/$AUTOMATIC_MARKER/q" "$SETKEYCONF" > "$TMPCONF"
188 update_system_ipsec "$TMPCONF"
192 # Generate a random HEX string (length specified by global variable KEYSIZE)
193 python -c "from ganeti import utils; print utils.GenerateSecret($KEYSIZE)"
197 # Generate a new random key to be used for IPsec, the key is associated with
199 local KEY=$(generate_secret)
200 if [[ ! -f "$KEYPATH" ]]; then
201 # New environment/cluster, let's start from scratch
204 local SEQNO=$(( $(gather_key_seqno) + 1 ))
206 local TMPKEYPATH=$(mktemp)
207 CLEANUP+=("rm -f $TMPKEYPATH")
208 echo -n "$SEQNO 0x$KEY" > "$TMPKEYPATH"
209 chmod 400 "$TMPKEYPATH"
210 mv "$TMPKEYPATH" "$KEYPATH"
215 hooks_path="$GANETI_HOOKS_PATH"
216 if [[ ! -n "$hooks_path" ]]; then
217 echo '\$GANETI_HOOKS_PATH not specified.' >&2
220 hooks_phase="$GANETI_HOOKS_PHASE"
221 if [[ ! -n "$hooks_phase" ]]; then
222 echo '\$GANETI_HOOKS_PHASE not specified.' >&2
226 if [[ "$hooks_phase" = post ]]; then
227 case "$hooks_path" in
235 # This hook path is not yet implemented in Ganeti, here we suppose it
236 # runs on all the nodes.
237 MASTERNAME=$(< "$MASTERNAME_FILE")
238 if [[ "$MASTERNAME" = "$HOSTNAME" ]]; then
249 node_name="$GANETI_NODE_NAME"
250 if [[ ! -n "$node_name" ]]; then
251 echo '\$GANETI_NODE_NAME not specified.' >&2
254 if [[ "$node_name" = "$HOSTNAME" ]]; then
261 echo "Hooks path $hooks_path is not for us." >&2
265 echo "Hooks phase $hooks_phase is not for us." >&2