-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Neil Dunbar
committed
Nov 14, 2013
0 parents
commit 18b026e
Showing
8 changed files
with
1,448 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
from ubuntu:12.04 | ||
|
||
# Configure apt | ||
run echo 'deb http://us.archive.ubuntu.com/ubuntu/ precise universe' >> /etc/apt/sources.list | ||
run apt-get -y update | ||
run apt-get -y install python-software-properties | ||
run apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xcbcb082a1bb943db | ||
run add-apt-repository 'deb http://ftp.osuosl.org/pub/mariadb/repo/5.5/ubuntu precise main' | ||
run apt-key adv --recv-keys --keyserver keys.gnupg.net --recv-keys 1C4CBDCDCD2EFD2A | ||
|
||
# Make apt and MariaDB happy with the docker environment | ||
run echo "#!/bin/sh\nexit 101" >/usr/sbin/policy-rc.d | ||
run chmod +x /usr/sbin/policy-rc.d | ||
run cat /proc/mounts >/etc/mtab | ||
|
||
# Install MariaDB | ||
run apt-get -y update | ||
run apt-get -y install | ||
run LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y iproute mariadb-galera-server galera rsync netcat-openbsd socat pv | ||
|
||
# this is for testing - can be commented out later | ||
run LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y iputils-ping net-tools | ||
|
||
run add-apt-repository 'deb http://repo.percona.com/apt precise main' | ||
run apt-get -y update | ||
run LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y percona-xtrabackup | ||
|
||
# add in extra wsrep scripts | ||
add wsrep_sst_common /usr/bin/wsrep_sst_common | ||
add wsrep_sst_xtrabackup-v2 /usr/bin/wsrep_sst_xtrabackup-v2 | ||
|
||
# Clean up | ||
run rm /usr/sbin/policy-rc.d | ||
run rm -r /var/lib/mysql | ||
|
||
# Add config(s) - standalong and cluster mode | ||
add ./my-cluster.cnf /etc/mysql/my-cluster.cnf | ||
add ./my-init.cnf /etc/mysql/my-init.cnf | ||
|
||
expose 3306 4567 | ||
|
||
add ./mariadb-setrootpassword /usr/bin/mariadb-setrootpassword | ||
add ./mariadb-start /usr/bin/mariadb-start | ||
cmd ["/usr/bin/mariadb-start"] | ||
|
||
# vim:ts=8:noet: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
MariaDB 5.5 Galera | ||
================== | ||
|
||
A reasonably simple Ubuntu 12.04 LTS container with the pieces to form | ||
a MariaDB 5.5 Galera cluster. It's based on Nick Stenning's MariaDB | ||
5.5 container for MariaDB 5.5, and adds in default support for X.509 | ||
based administrative authentication. | ||
|
||
(Repeating the warning from Nick S) | ||
|
||
**NB**: Please be aware that by default docker will make the MariaDB | ||
port accessible from anywhere if the host firewall is unconfigured. It | ||
also exposes the Galera wsrep port (by default 4567) globally. | ||
|
||
The root password for the first node (and therefore the entire | ||
cluster) is randomly generated. This password can only be used to | ||
contact the server along the unix domain socket (ie, the default | ||
localhost connection). | ||
|
||
The container needs to import two volumes (one read/write, the other | ||
read only). The first volume is the data volume, where the actual | ||
MySQL data is stored. | ||
|
||
The second volume (read-only) is the SSL container, which should | ||
contain 3 files: | ||
|
||
* a CA certificate (this can be a real CA certificate, or just a | ||
simple test one, generated via something like TinyCA. This CA | ||
file should be called `ca.pem`. [^1] | ||
* an SSL private key. This should be set to ownership mode 0600 (or | ||
even 0400). The file needs to be called `server-key.pem`. | ||
* an SSL server certificate. The CN component of the subject name on | ||
this certificate should be the DNS name of the host which will be | ||
the end point for the database service. Note that you can have | ||
multiple host names by using the subjectAltName X.509 v3 extension | ||
on the certificate. This file needs to be called `server-cert.pem`. | ||
|
||
The data volume must be mounted at the container directory | ||
`/var/lib/mysql`. | ||
|
||
The SSL volume must be mounted at the container directory | ||
`/etc/ssl/mysql`. | ||
|
||
Within the SSL volume there should also be a directory called `root` | ||
which will contain a set of *client* certificates with names like | ||
`joe-root.pem`, `amy-root.pem`, `superman.pem` and so on. These names | ||
will be registered as root capable users from any network host, with | ||
no passwords, but only available using the SSL client certificates. | ||
[^2] | ||
|
||
The remote root access is derived from a set of X.509 certificates | ||
which will be used via SSL to authenticate the user, so that passwords | ||
are not passed in via environmental variables. The root password is | ||
also encrypted via those keys and stored on the (exported) data volume | ||
which the container needs. | ||
|
||
Decrypting the Root Password | ||
---------------------------- | ||
|
||
If you need to recover the root password, and you are the holder of | ||
one of the root keys in /etc/ssl/mysql/root, then execute | ||
|
||
$ cat _/path/to/mysql-dir/_rootpw.pem | openssl smime -decrypt -inkey | ||
_path/to/private-key.pem_ | ||
|
||
e.g. | ||
|
||
cat /data/mysql-node-1/rootpw.pem | openssl smime -decrypt -inkey | ||
~/ssl-keys/joe-root-key.pem | ||
|
||
|
||
Example Usage | ||
------------- | ||
|
||
This sets up a 3 node cluster on 3 different servers, all using | ||
default ports. | ||
|
||
We assume that /data/mysql holds the (host, not container) directory | ||
where the MySQL database files need to go, and /data/mysql-ssl | ||
contains the relevant keys, certificates and CA certificates. For the | ||
first node standup, /data/mysql should be empty. | ||
|
||
# Boot the first node in standalone mode # | ||
|
||
DBID=$(sudo docker run -d -v /data/mysql:/var/lib/mysql \ | ||
-v /data/mysql-ssl:/etc/ssl/mysql \ | ||
-p 3306:3306 -e CLUSTER=BOOT ndunbar/mariadb55 \ | ||
mariadb-start) | ||
|
||
(Note: no need to expose ports 4567 and 4444 - no-one is talking to | ||
anyone just yet) | ||
|
||
This should fire up a single MySQL server with the appropriate remote | ||
root user permissions (using SSL client certificates only). Check the | ||
contents of `/data/mysql/mysql.error.log` for whether things worked OK | ||
or not. | ||
|
||
Assuming it went well, continue. | ||
|
||
# Stop the standalone mode # | ||
|
||
sudo docker stop $DBID | ||
|
||
This should (gracefully) stop the standalone mode MariaDB node | ||
|
||
# Start the node in Primary Component mode # | ||
|
||
To begin the cluster, you need a single node as a catch-all donor. | ||
|
||
DBID=$(sudo docker run -d /data/mysql:/var/lib/mysql \ | ||
-v /data/mysql-ssl:/etc/ssl/mysql \ | ||
-p 3306:3306 -p 4567:4567 -p 4444:4444 \ | ||
-e CLUSTER=INIT \ | ||
-e NODE_ADDR=my.host.name ndunbar/mariadb55 \ | ||
mariadb-start) | ||
|
||
This should restart the server (with data intact) with the ability to | ||
use it to populate new nodes to fill out the cluster. Node that the | ||
primary component has a node number of 1 (this is fixed by scripting). | ||
|
||
Note that ports 4567 and 4444 are open. 4567 is the Galera clustering | ||
service, and 4444 is the service by which snapshots of the database | ||
are transferred across to bring a new node into sync. The current | ||
container implementation uses Xtrabackup, encrypting the transfers via | ||
SSL between nodes. Xtrabackup has the nice property of not blocking | ||
the donor server (well, it does, but only for a very short time), | ||
which means you can continue to write into the donor node while other | ||
nodes are coming online. | ||
|
||
# Start the other nodes in the cluster | ||
|
||
For each extra node that you need to turn up, set up the `/data/mysql` | ||
and `/data/mysql-ssl` directories as per the primary node, and execute | ||
|
||
DBID=$(sudo docker run -d /data/mysql:/var/lib/mysql \ | ||
-v /data/mysql-ssl:/etc/ssl/mysql \ | ||
-p 3306:3306 -p 4567:4567 -p 4444:4444 \ | ||
-e CLUSTER= my.host.name,his.host.name,her.host.name \ | ||
-e NODE=<node number> \ | ||
-e NODE_ADDR=my.host.name ndunbar/mariadb55 \ | ||
mariadb-start) | ||
|
||
where `<node number>` is some integer above 1, ideally sequential. The | ||
`his.host.name`, `her.host.name` etc. should be the other nodes in the | ||
cluster. You don't need to list all the nodes in the cluster - the | ||
service will discover the other active nodes upon attempting to join | ||
the cluster - if the list includes the host name of the node on which | ||
the server is running, it will be excluded from the list of nodes to | ||
be interrogated for cluster manifest. | ||
|
||
Three nodes is the least number needed to avoid split brain | ||
scenarios. | ||
|
||
# (Optional) Restart the primary node as a normal cluster node # | ||
|
||
Once the minimum number of synced nodes the cluster reaches 3, you can | ||
stop and restart the primary component as just another node in the | ||
cluster. | ||
|
||
(On the server running the primary node) | ||
|
||
sudo docker stop $DBID | ||
|
||
DBID=$(sudo docker run -d /data/mysql:/var/lib/mysql \ | ||
-v /data/mysql-ssl:/etc/ssl/mysql \ | ||
-p 3306:3306 -p 4567:4567 -p 4444:4444 \ | ||
-e CLUSTER= my.host.name,his.host.name,her.host.name \ | ||
-e NODE=1 \ | ||
-e NODE_ADDR=my.host.name ndunbar/mariadb55 \ | ||
mariadb-start) | ||
|
||
[^1]: http://tinyca.sm-zone.net | ||
|
||
[^2]: Note that the root user certificates _must_ be signed by the | ||
same CA as the one signing the server certificate. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
#!/bin/bash | ||
|
||
set -u | ||
|
||
MSA=/usr/bin/mysqladmin | ||
# generate long random password | ||
MARIADB_ROOT_PW=$(openssl rand -base64 32) | ||
|
||
|
||
# make sure ownership of data dir is OK | ||
chown -R mysql:mysql /var/lib/mysql | ||
/usr/bin/mysqld_safe & | ||
|
||
|
||
|
||
sleep 5 # wait for mysqld_safe to rev up, and check for port 3306 | ||
port_open=0 | ||
|
||
while [ "$port_open" -eq 0 ]; do | ||
/bin/nc -z -w 5 127.0.0.1 3306 | ||
if [ $? -ne 0 ]; then | ||
echo "Sleeping waiting for port 3306 to open: result " $? | ||
sleep 1 | ||
else | ||
echo "Port 3306 is open" | ||
port_open=1 | ||
fi | ||
done | ||
|
||
# Secure the installation | ||
done=0 | ||
count=0 | ||
maxtries=10 | ||
while [ $done -eq 0 ]; do | ||
${MSA} -u root password ${MARIADB_ROOT_PW} | ||
if [ $? -ne 0 ]; then | ||
count=$((${count} + 1)) | ||
if [ $count -gt $maxtries ]; then | ||
echo "Maximum tries at setting password exceeded. Giving up" | ||
exit 1 | ||
else | ||
echo "Root password set failed. Sleeping, then retrying" | ||
sleep 1 | ||
fi | ||
else | ||
echo "Root Password set successfully" | ||
done=1 | ||
fi | ||
done | ||
|
||
# this code mimics the secure install script, which was originally | ||
# scripted via expect. I found that unreliable, hence this section | ||
|
||
# drop test database | ||
echo "Dropping test DB" | ||
DROP="DROP DATABASE IF EXISTS test" | ||
echo "$DROP" | mysql -u root --password="$MARIADB_ROOT_PW" mysql | ||
|
||
echo "Cleaning test db privs" | ||
# remove db privs for test | ||
DELETE="DELETE FROM mysql.db Where Db='test' OR Db='test\\_%'" | ||
echo "$DELETE" | mysql -u root --password="$MARIADB_ROOT_PW" mysql | ||
|
||
echo "Deleting anon db users" | ||
# remove anon users | ||
DELETE="DELETE FROM mysql.user WHERE User=''" | ||
echo "$DELETE" | mysql -u root --password="$MARIADB_ROOT_PW" mysql | ||
|
||
echo "create mysql user" | ||
# create mysql@localhost user for xtrabackup | ||
CUSER="CREATE USER 'mysql'@'localhost'" | ||
echo "$CUSER" | mysql -u root --password="$MARIADB_ROOT_PW" mysql | ||
|
||
# now set the root passwords given the certificates available | ||
ROOTCERTS=/etc/ssl/mysql/root | ||
if [ -d ${ROOTCERTS} ]; then | ||
clist=$(find ${ROOTCERTS} -type f -a -name \*.pem -print) | ||
|
||
if [ -z "${clist}" ]; then | ||
echo "No certificates available to encrypt root pw" >&2 | ||
exit 1 | ||
fi | ||
|
||
# dumping encrypted root password to disk - make sure that | ||
# only owner can read/write it | ||
oldu=$(umask) | ||
umask 0077 | ||
echo -n $MARIADB_ROOT_PW | openssl smime -encrypt -aes256 \ | ||
-out /var/lib/mysql/rootpw.pem \ | ||
${clist} | ||
umask ${oldu} | ||
|
||
for c in ${clist}; do | ||
filename=$(basename "${c}") | ||
username=${filename%.*} | ||
# extract subject and issuer | ||
subject=$(openssl x509 -noout -subject -in ${c} | sed -e "s/^subject= //ig") | ||
printf -v qsubject "%q" "${subject}" | ||
issuer=$(openssl x509 -noout -issuer -in ${c} | sed -e "s/^issuer= //ig" -e "") | ||
printf -v qissuer "%q" "${issuer}" | ||
CUSER="CREATE USER '${username}'@'%'" | ||
echo "${CUSER}" | mysql -u root --password="$MARIADB_ROOT_PW" mysql | ||
GRANT="GRANT ALL PRIVILEGES ON *.* TO '${username}'@'%' \ | ||
REQUIRE SUBJECT '${qsubject}' AND \ | ||
ISSUER '${qissuer}' \ | ||
WITH GRANT OPTION" | ||
echo "${GRANT}" | mysql -u root --password="$MARIADB_ROOT_PW" mysql | ||
done | ||
fi | ||
echo "FLUSH PRIVILEGES" | mysql -u root --password="$MARIADB_ROOT_PW" mysql | ||
|
||
echo "Shutting down MySQL server" | ||
${MSA} -uroot -p${MARIADB_ROOT_PW} shutdown | ||
echo "---> MariaDB installation secured with root password" >&2 |
Oops, something went wrong.