Rootserver-Serie VII: MySQL Master/Slave mit SSH-Tunnel

Aus Backup-, Sicherheits- und Performancegründen soll ein MySQL-Server-Verbund im Master/Slave-Betrieb aufgebaut werden.

Da der Master auf dem Rootserver läuft und der Slave auf einem LAN-Server an einem Dial-In-Zugang ist dabei etwas mehr Aufwand zu betreiben.
Primär soll der Master nicht weltweit über seine öffentlichen Geräte erreichbar sein. Ein komplette VPN-Lösung (OpenVPN o.Ä.) ist hingegen viel zu überdimensioniert. Die Lösung des Problems ist ein kleiner SSH-Tunnel, der mit PPPd zum Mini-VPN mutiert.
Wie üblich beziehen sich die Angaben auf CentOS 5.1, sollten aber mit anderen Distributionen fast gleich ablaufen.

Den Master vorbereiten

Zuerst wird ein passender User benötigt, der die Daten vom Master beziehen kann. In weiser Voraussicht wird der Zugriff auf die IP-Adresse den Clients im VPN-Verbund beschränkt (s.u.)

mysql> GRANT REPLICATION SLAVE ON *.* TO slaveuser@'192.168.28.2' IDENTIFIED BY 'meinpasswort';Als nächstes muss der MySQL-Server dazu gebracht werden, dass er ein Binary-Log schreibt[mysqld]
#Skip warnings with given hostname
log-bin=static-bin
server-id=1

Der Suffix bei log-bin sorgt dafür, dass das Logfile nicht vom Hostnamen abhängig ist und somit MySQLd uns nicht das Syslog vollmüllt 😉

Danach wird der MySQLd neugestartet und in den Read-Only-Modus gebracht. Datenänderungen können wir jetzt nicht gebrauchen. Diese Konsolensitzung auf jeden Fall geöffnet lassen, damit der LOCK erhalten bleibt!

mysql> FLUSH TABLES WITH READ LOCK;
Danach wird noch das komplette Datenverzeichnis per tar eingepackt und auf dem Slave wieder entpackt:

cd /var/lib/mysql
tar -cvf /tmp/master-snapshot.tar .
scp ...
cd /var/lib/mysql
rm -rf *
tar -xvf /tmp/master-snapshot.tar .

Zu guter letzt muss auf dem Master noch ermittelt werden, an welcher Stelle im Binarylog er steht:

mysql> SHOW MASTER STATUS;

und der Zugriff auf die Datenbank kann wieder gestattet werden:
mysql> UNLOCK TABLES;

VPN erstellen

Um eine entsprechende Sicherheit zu gewährleisten wird dein SSH-Tunnel zwischen den beiden Maschinen aufgebaut.
Dazu kommt das Script vpn-pppssh zum Einsatz. Dieses kann an geeignet Stelle wie z.B. /usr/local/bin abgelegt werden:
[code]#!/bin/sh
# /usr/local/bin/vpn-pppssh
#
# This script initiates a ppp-ssh vpn connection.
# see the VPN PPP-SSH HOWTO on http://www.linuxdoc.org for more information.
#
# revision history:
# 1.6 11-Nov-1996 miquels@cistron.nl
# 1.7 20-Dec-1999 bart@jukie.net
# 2.0 16-May-2001 bronson@trestle.com

#
# You will need to change these variables…
#

# The host name or IP address of the SSH server that we are
# sending the connection request to:
SERVER_HOSTNAME=domail.tld

# The username on the VPN server that will run the tunnel.
# For security reasons, this should NOT be root. (Any user
# that can use PPP can intitiate the connection on the client)
SERVER_USERNAME=srvuser

# The VPN network interface on the server should use this address:
SERVER_IFIPADDR=192.168.28.1

# …and on the client, this address:
CLIENT_IFIPADDR=192.168.28.2

# This tells ssh to use unprivileged high ports, even though it’s
# running as root. This way, you don’t have to punch custom holes
# through your firewall.
LOCAL_SSH_OPTS=„-P -o port=2022″

#
# The rest of this file should not need to be changed.
#

PATH=/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/bin/X11/:

#
# required commands…
#

PPPD=/usr/sbin/pppd
SSH=/usr/bin/ssh

if ! test -f $PPPD ; then echo „can’t find $PPPD„; exit 3; fi
if ! test -f $SSH ; then echo „can’t find $SSH„; exit 4; fi

case „$1„ in
start)
# echo -n „Starting vpn to $SERVER_HOSTNAME: „
${PPPD} updetach noauth passive pty „${SSH} ${LOCAL_SSH_OPTS} ${SERVER_HOSTNAME} -l${SERVER_USERNAME} -o Batchmode=yes sudo ${PPPD} nodetach notty noauth„ ipparam vpn ${CLIENT_IFIPADDR}:${SERVER_IFIPADDR}
# echo „connected.„
;;

stop)
# echo -n „Stopping vpn to $SERVER_HOSTNAME: „
PID=`ps ax | grep „${SSH} ${LOCAL_SSH_OPTS} ${SERVER_HOSTNAME} -l${SERVER_USERNAME} -o„ | grep -v ‚ passive ‚ | grep -v ‚grep ‚ | awk ‚{print $1}’`
if [ „${PID}„ != „„ ]; then
kill $PID
echo „disconnected.„
else
echo „Failed to find PID for the connection„
fi
;;

config)
echo „SERVER_HOSTNAME=$SERVER_HOSTNAME„
echo „SERVER_USERNAME=$SERVER_USERNAME„
echo „SERVER_IFIPADDR=$SERVER_IFIPADDR„
echo „CLIENT_IFIPADDR=$CLIENT_IFIPADDR„
;;

*)
echo „Usage: vpn {start|stop|config}„
exit 1
;;
esac

exit 0[/code]
Die oberen Variablen sollten ggf. angepasst werden. Wenn die IPs geändert werden, so muss daran gedacht werden, dies auch in dem MySQL-User zu reflektieren!
Ansonsten habe ich hier noch exemplarisch den SSH-Port umgelegt und dies mit angegeben.

Dieses Script sollte jetzt noch ausführbar gemacht werden
chmod a+x /usr/local/bin/vpn-pppssh
und in die Startroutine eingebunden werden. (Möglichst bevor der Slave startet 😉 )

Auf dem Server müssen dann die sudo-Rechte noch etwas aufgebohrt werden:
Einerseits muss tty erlaubt werden. Also muss die Zeile
#Defaults requiretty
auskommentiert werden.

Zusätzlich benötigt der User, welcher den VPN-Kanal aufbaut auch die passenden Rechte um pppd ohne Passwort starten zu dürfen:
srvuser ALL=/usr/sbin/pppd, NOPASSWD: ALL

Außerdem sollte auf dem Client noch ein Public-Key eingerichtet werden damit der Tunnel automatisch gestartet werden kann.

Zu guter letzt muss die Firewall auf dem Server etwas aufgebohrt werden. Da ich keinen Nerv habe extra ein neues Firewall-Frontend zu installieren kommt der passende Befehl einfach unter /etc/rc.local
iptables -I INPUT -i ppp0 -j ACCEPT

Es ist aber daran zu denken, dass beim nächsten Rumspielen mit der Firewall der Befehl erneut wieder ausgeführt werden muss. Andernfalls steht der Slave vor verschlossenen Türen 😉

Slave einbinden

Der Slave benötigt ebenso wie der Master eine server-id

[mysqld]
server-id=2

Nun muss nur noch der Slave mit dem Master verbunden werden:

mysql> CHANGE MASTER TO
-> MASTER_HOST='192.168.28.1',
-> MASTER_USER='slave_user',
-> MASTER_PASSWORD='meinpasswort'
-> MASTER_LOG_FILE=static-bin.000002'',
-> MASTER_LOG_POS='152595'

(Die Werte bei LOG_FILE und LOG_POS bitte aus den vorherigen Befehlen entnehmen 😉 )

und der Slave-Modus gestartet werden:

mysql> START SLAVE;
Wenn der Server einmal im Slave-Modus gestartet wurde merkt er sich dies und fährt beim nächsten Mal auch wieder so hoch.

Quellen:

MySQL verteilen und sichern: Master und Slave
VPN PPP-SSH Mini-HOWTO

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *