in DevOps

Encrypted MySQL connections (for client and replication)

There are four basic things you need to do when attempting to set up encrypted MySQL connections.

  1. Make sure your MySQL installation is configured with SSL.
  2. Create a set of certificates for your master, your slave, and your client(s).
  3. Configure your master and slave my.cnf with the correct ssl-* options.
  4. Configure the replication with the SSL options to CHANGE MASTER.

First, let’s check to make sure our installation supports SSL.

master [localhost] {msandbox} ((none)) >  show variables like '%ssl%';
+---------------+----------+
| Variable_name | Value    |
+---------------+----------+
| have_openssl  | DISABLED |
| have_ssl      | DISABLED |
| ssl_ca        |          |
| ssl_capath    |          |
| ssl_cert      |          |
| ssl_cipher    |          |
| ssl_key       |          |
+---------------+----------+
7 rows in set (0.00 sec)

So we see here that SSL is disabled. Bummer. This likely happened because you don’t have the ssl option in your my.cnf. Add it to your configuration and restart your mysqld instance. (There is a configuration file below that you can use as an example.)

master [localhost] {msandbox} ((none)) >  show variables like '%ssl%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| have_openssl  | YES   |
| have_ssl      | YES   |
| ssl_ca        |       |
| ssl_capath    |       |
| ssl_cert      |       |
| ssl_cipher    |       |
| ssl_key       |       |
+---------------+-------+
7 rows in set (0.00 sec)

Good, SSL is enabled and available. Now we need to set up our keys. We’ll need a certificate authority, a server key (multiple in the case of master-slave replicas) and some client keys.
I used the following to generate my own certificate authority and self-signed certificates.

#!/bin/sh
# Generate test SSL keys

mkdir ../certs

cd ../certs

openssl genrsa 2048 > ca-key.pem

openssl req -new -x509 -nodes -md5 -days 1000 -key ca-key.pem \
        -subj "/C=US/ST=Texas/O=My Org/OU=Test/CN=CA" > ca-cert.pem

for target in client server
do
  openssl req -newkey rsa:1024 -md5 -days 1000 -nodes -keyout $target-key.pem \
          -subj "/C=US/ST=Texas/O=My Org/OU=Test/CN=$target" > $target-req.pem

  openssl x509 -req -in $target-req.pem -days 1000 -md5 -CA ca-cert.pem \
          -CAkey ca-key.pem -set_serial 01 > $target-cert.pem
done

cd ..
chgrp -R mysql certs/

So what this gets you is:

:;  ls -l certs/
total 32
-rw-r----- 1 travis staff 1598 May 14 16:31 ca-cert.pem
-rw-r----- 1 travis staff 1675 May 14 16:31 ca-key.pem
-rw-r----- 1 travis staff 1086 May 14 16:31 client-cert.pem
-rw-r----- 1 travis staff  891 May 14 16:31 client-key.pem
-rw-r----- 1 travis staff  692 May 14 16:31 client-req.pem
-rw-r----- 1 travis staff 1086 May 14 16:31 server-cert.pem
-rw-r----- 1 travis staff  887 May 14 16:31 server-key.pem
-rw-r----- 1 travis staff  692 May 14 16:31 server-req.pem

Next, configure the master’s my.cnf. I’m using MySQL Sandbox for this test, so the config should be appropriate for that. You might have to modify accordingly to run outside of a Sandbox.

client]
user            = msandbox
password        = msandbox
port            = 31281
socket          = /tmp/mysql_sandbox31281.sock
ssl-capath                      = /home/travis/sandboxes/cat1_test/certs
ssl-ca                          = /home/travis/sandboxes/cat1_test/certs/ca-cert.pem
ssl-cert                        = /home/travis/sandboxes/cat1_test/certs/client-cert.pem
ssl-key                         = /home/travis/sandboxes/cat1_test/certs/client-key.pem

[mysqld]
user                            = travis
port                            = 31281
socket                          = /tmp/mysql_sandbox31281.sock
basedir                         = /home/travis/opt/mysql/5.0.76
datadir                         = /home/travis/sandboxes/cat1_test/master/data
pid-file                        = /home/travis/sandboxes/cat1_test/master/data/mysql_sandbox31281.pid
#log-slow-queries               = /home/travis/sandboxes/cat1_test/master/data/msandbox-slow.log
#log                            = /home/travis/sandboxes/cat1_test/master/data/msandbox.log
#
# additional options passed through 'my_clause'
#
log-bin=mysql-bin
server-id=1
log-error                       = /home/travis/sandboxes/cat1_test/master/data/msandbox.err
ssl
ssl-capath                      = /home/travis/sandboxes/cat1_test/certs
ssl-ca                          = /home/travis/sandboxes/cat1_test/certs/ca-cert.pem
ssl-cert                        = /home/travis/sandboxes/cat1_test/certs/server-cert.pem
ssl-key                         = /home/travis/sandboxes/cat1_test/certs/server-key.pem

And the corresponding slave configuration:

[client]
user            = msandbox
password        = msandbox
port            = 31282
socket          = /tmp/mysql_sandbox31282.sock
ssl
ssl-capath                      = /home/travis/sandboxes/cat1_test/certs
ssl-ca                          = /home/travis/sandboxes/cat1_test/certs/ca-cert.pem
ssl-cert                        = /home/travis/sandboxes/cat1_test/certs/client-cert.pem
ssl-key                         = /home/travis/sandboxes/cat1_test/certs/client-key.pem

[mysqld]
user                            = travis
port                            = 31282
socket                          = /tmp/mysql_sandbox31282.sock
basedir                         = /home/travis/opt/mysql/5.0.76
datadir                         = /home/travis/sandboxes/cat1_test/node1/data
pid-file                        = /home/travis/sandboxes/cat1_test/node1/data/mysql_sandbox31282.pid
#log-slow-queries               = /home/travis/sandboxes/cat1_test/node1/data/msandbox-slow.log
#log                            = /home/travis/sandboxes/cat1_test/node1/data/msandbox.log
#
# additional options passed through 'my_clause'
#
server-id=101
report-host=SBslave1
report-port=31281
log-bin=mysql-bin
log-error                       = /home/travis/sandboxes/cat1_test/node1/data/msandbox.err
ssl
ssl-capath                      = /home/travis/sandboxes/cat1_test/certs
ssl-ca                          = /home/travis/sandboxes/cat1_test/certs/ca-cert.pem
ssl-cert                        = /home/travis/sandboxes/cat1_test/certs/server-cert.pem
ssl-key                         = /home/travis/sandboxes/cat1_test/certs/server-key.pem

Since I’m being lazy for this test, note that the server key is used in both the master and slave [mysqld] sections. Ordinarily you would have a server key for each mysqld instance.

Now, when you start up your mysql client you either need to specify all the ssl-* options on the command line or be lazy and refer to one of the above my.cnf files. Because we defined a [client] section, it should just work.

Again, I’m using a Sandbox, so we start the client with the generated script. It looks like this:

export LD_LIBRARY_PATH=/home/travis/opt/mysql/5.0.76/lib:/home/travis/opt/mysql/5.0.76/lib/mysql:$LD_LIBRARY_PATH
export DYLD_LIBRARY_PATH=/home/travis/opt/mysql/5.0.76/lib:/home/travis/opt/mysql/5.0.76/lib/mysql:$DYLD_LIBRARY_PATH
SBDIR="/home/travis/sandboxes/cat1_test/master"
BASEDIR=/home/travis/opt/mysql/5.0.76
MYSQL="$BASEDIR/bin/mysql"
PIDFILE="$SBDIR/data/mysql_sandbox31281.pid"
if [ -f $PIDFILE ]
then
    $MYSQL --defaults-file=$SBDIR/my.sandbox.cnf $MYCLIENT_OPTIONS "$@"
fi

When I run this I get logged into my Sandbox master and I can check that SSL is working on my connection.

:;  ./m
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 89228
Server version: 5.0.76-enterprise-gpl-log MySQL Enterprise Server (GPL)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

master [localhost] {msandbox} ((none)) >  show variables like '%ssl%';
+---------------+--------------------------------------------------------+
| Variable_name | Value                                                  |
+---------------+--------------------------------------------------------+
| have_openssl  | YES                                                    |
| have_ssl      | YES                                                    |
| ssl_ca        | /home/travis/sandboxes/cat1_test/certs/ca-cert.pem     |
| ssl_capath    | /home/travis/sandboxes/cat1_test/certs                 |
| ssl_cert      | /home/travis/sandboxes/cat1_test/certs/server-cert.pem |
| ssl_cipher    |                                                        |
| ssl_key       | /home/travis/sandboxes/cat1_test/certs/server-key.pem  |
+---------------+--------------------------------------------------------+
7 rows in set (0.00 sec)

Alternatively, you can start the command line client with the following options:

--ssl-capath=/usr/local/mysql/certs --ssl-cert=client-cert.pem --ssl-key=client-key.pem

Finally, we need to configure the replication to use SSL. I will assume you understand how to set up replication properly and you have it already working on your master-slave pairs.

Ok, so by now you should have your master and slave my.cnf configured with all the SSL variables. On your master, configure the replication user to require SSL.

GRANT REPLICATION SLAVE
ON *.* TO 'msandbox'@'%'
IDENTIFIED BY 'msandbox' REQUIRE SSL;

On your slave, issue the CHANGE MASTER command.

STOP SLAVE;
CHANGE MASTER TO MASTER_HOST = '127.0.0.1',
MASTER_USER = 'msandbox',
MASTER_PASSWORD = 'msandbox',
MASTER_PORT = 31281,
MASTER_LOG_FILE = 'mysql-bin.000001',
MASTER_LOG_POS = 98,
MASTER_SSL = 1,
MASTER_SSL_CA = '/home/travis/sandboxes/cat1_test/certs/ca-cert.pem',
MASTER_SSL_CERT = '/home/travis/sandboxes/cat1_test/certs/client-cert.pem',
MASTER_SSL_KEY = '/home/travis/sandboxes/cat1_test/certs/client-key.pem';
START SLAVE;

And hopefully, if things went well, you’ll see happiness when you issue a SHOW SLAVE STATUS on the slave. Mine looks like:

slave1 [localhost] {msandbox} ((none)) > show slave status \G
*************************** 1. row ***************************
             Slave_IO_State: Waiting for master to send event
                Master_Host: 127.0.0.1
                Master_User: msandbox
                Master_Port: 31281
              Connect_Retry: 60
            Master_Log_File: mysql-bin.000016
        Read_Master_Log_Pos: 98
             Relay_Log_File: mysql_sandbox31282-relay-bin.001746
              Relay_Log_Pos: 235
      Relay_Master_Log_File: mysql-bin.000016
           Slave_IO_Running: Yes
          Slave_SQL_Running: Yes
            Replicate_Do_DB:
        Replicate_Ignore_DB:
         Replicate_Do_Table:
     Replicate_Ignore_Table:
    Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
                 Last_Errno: 0
                 Last_Error:
               Skip_Counter: 0
        Exec_Master_Log_Pos: 98
            Relay_Log_Space: 235
            Until_Condition: None
             Until_Log_File:
              Until_Log_Pos: 0
         Master_SSL_Allowed: Yes
         Master_SSL_CA_File: /home/travis/sandboxes/cat1_test/certs/ca-cert.pem
         Master_SSL_CA_Path:
            Master_SSL_Cert: /home/travis/sandboxes/cat1_test/certs/client-cert.pem
          Master_SSL_Cipher:
             Master_SSL_Key: /home/travis/sandboxes/cat1_test/certs/client-key.pem
      Seconds_Behind_Master: 0
1 row in set (0.00 sec)
Travis Campbell
Staff Systems Engineer at ghostar
Travis Campbell is a seasoned Linux Systems Engineer with nearly two decades of experience, ranging from dozens to tens of thousands of systems in the semiconductor industry, higher education, and high volume sites on the web. His current focus is on High Performance Computing, Big Data environments, and large scale web architectures.
  1. Does encrypted SQL connections stop sql injection when it happens or is that not possibly related – the two issues?

  2. @Faisal: this does not prevent sql injection. This is strictly for the encryption of the transport layer between the MySQL server and the MySQL client to prevent sniffing of the data over the wire.

Comments are closed.