Featured image of post How to secure MySQL Docker container for Zero Trust

How to secure MySQL Docker container for Zero Trust

Increase the security of your MySQL Docker containers when running in a Zero Trust dev environment

What is a Zero Trust development environment?

Zero Trust is a security model that assumes no trust, even inside the network. Every request is authenticated, authorized, and encrypted in a Zero Trust environment. This approach helps protect against data breaches and insider threats.

In our example use case, we create a development environment in a cloud instance, which includes a MySQL database running in a Docker container. We need to be able to access the MySQL database from our local machine for development purposes. However, the database may contain sensitive data, such as API keys or user passwords. We want to secure the MySQL database to prevent unauthorized access.

We want to make sure that the MySQL database is not easily accessible from the internet. In addition, we want to limit the exposure of database credentials.

Launching MySQL Docker container

We can run a MySQL database in a Docker container using the official MySQL Docker image. We create docker-compose.yml like:

services:
  mysql:
    image: mysql:8.0
    command: [
        "mysqld",
        "--datadir=/tmp/mysqldata",
      ]
    environment:
      MYSQL_ROOT_PASSWORD: toor
      MYSQL_DATABASE: fleet
      MYSQL_USER: fleet
      MYSQL_PASSWORD: insecure
    ports:
      - "3306:3306"

And we run docker-compose up to start the MySQL database.

We can access the MySQL database by using the MySQL client:

mysql -h 127.0.0.1 -P 3306 -uroot -ptoor

As we can see, the passwords are stored in plain text in the docker-compose.yml file. We want to avoid storing sensitive data in plain text.

Securing database secrets with Docker secrets

Docker secrets allow us to store sensitive data, such as passwords, securely. We can create secrets and use them in the docker-compose.yml file.

secrets:
  mysql_root_password:
    file: ./mysql_root_password.txt
  mysql_password:
    environment: MYSQL_PASSWORD
services:
  mysql:
    image: mysql:8.0
    command: [
        "mysqld",
        "--datadir=/tmp/mysqldata",
      ]
    secrets:
      - mysql_root_password
      - mysql_password
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
      MYSQL_DATABASE: fleet
      MYSQL_USER: fleet
      MYSQL_PASSWORD_FILE: /run/secrets/mysql_password
    ports:
      - "3306:3306"

We create a mysql_root_password.txt file and run MYSQL_PASSWORD=insecure docker-compose up to start the MySQL database.

The above example shows that the MySQL root password is stored in a file, and the MySQL password is passed as an environment variable. Although this approach may be an improvement, it is not secure for a Zero Trust environment. A user with access to the file system can read the secrets, and environment variables can be read by anyone who can run the ps command, like: ps eww <docker compose process ID>.

In addition, a user can dump the secrets from the Docker container by running:

docker exec <container ID> cat /run/secrets/mysql_root_password

Securing database secrets with SQL commands

To secure the MySQL database without exposing the secrets on the server, we can use MySQL commands to set the passwords. We spin up MySQL with the following docker-compose.yml:

services:
  mysql:
    image: mysql:8.0
    command: [
        "mysqld",
        "--datadir=/tmp/mysqldata",
      ]
    environment:
      MYSQL_ROOT_PASSWORD: toor
      MYSQL_ONETIME_PASSWORD: true
    ports:
      - "3306:3306"

We set the root password and marked the root user as expired with MYSQL_ONETIME_PASSWORD: true.

Now, as the second step, we can run the following commands to set the passwords:

echo \
"ALTER USER root IDENTIFIED BY '$(op read op://employee/DEMO_SERVER/MYSQL_ROOT_PASSWORD)';" \
"CREATE DATABASE fleet;" \
"CREATE USER 'fleet'@'%' IDENTIFIED BY '$(op read op://employee/DEMO_SERVER/MYSQL_PASSWORD)';" \
"GRANT ALL PRIVILEGES ON fleet.* TO 'fleet'@'%';" \
"FLUSH PRIVILEGES;" \
| mysql -h 127.0.0.1 -P 3306 -uroot -ptoor --connect-expired-password

In the above command, we use 1Password as our secrets manager. We read the secrets from 1Password and pass them to the MySQL client to set the passwords.

Additional security considerations

This article focused on securing the MySQL passwords. However, there are additional security considerations when running MySQL in a Zero Trust environment:

  • Encrypting sensitive data – all sensitive data should be encrypted when stored in the database
  • Limiting access to specific IPs – we can add a server firewall to restrict access to the MySQL port

Further reading

Recently, we wrote how to use STDIN to read your program arguments.

We’ve previously written about MySQL master-slave replication. You can use MySQL replication to create a high-availability setup for your MySQL databases.

Watch how to secure a MySQL Docker container for Zero Trust

Note: If you want to comment on this article, please do so on the YouTube video.