The security risk of running docker mariadb/mysql with MYSQL_ALLOW_EMPTY_PASSWORD=yes
This is part of a common docker-compose.yml
which is frequently seen on the internet
services:
mariadb:
image: 'mariadb:latest'
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
- MYSQL_DATABASE=redmine
volumes:
- './mariadb_data:/var/lib/mysql'
[...]
Simple and secure, right? A no-root-password MariaDB instance that’s running in a separate container and does not have its port 3306 exposed - so only services from the same docker-compose.yml
can reach it since docker-compose
puts all those services in a separate network.
Wrong.
While the MariaDB instance is not reachable from the internet since no, it can be reached by any process via its internal IP address.
In order to comprehend what’s happening, we shall take a look at docker’s networks. In this case, my docker-compose config is called redmine
.
$ docker network ls | grep redmine
ea7ed38f469b redmine_default bridge local
This is the network that docker-compose
creates without any explicit network configuration. Let’s inspect
the network to show the hosts:
[
// [...]
"Containers": {
"2578fc65b4dab9f204d0a252e421dd4ddd9f41c35642d48350f4e59370581757": {
"Name": "redmine_mariadb_1",
"EndpointID": "1e6d81acc096a12fc740173f4e107090333c42e8a86680ac5c9886c148d578e7",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
},
"7867f71d2a36265c34c133b70aea487b90ea68fcf30ecb42d6e7e9a376cf8e07": {
"Name": "redmine_redmine_1",
"EndpointID": "f5ac7b3325aa9bde12f0c625c4881f9a6fc9957da4965767563ec9a3b76c19c3",
"MacAddress": "02:42:ac:12:00:03",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": ""
}
},
// [...]
]
We can see that the IP address of the redmine_mariadb_1
container is 172.18.0.2
.
Using the internal IP 172.18.0.2
, you can access the MySQL server.
Any process on the host (even from unprivileged users) can connect to the container without any password, e.g.
$ mysqldump -uroot -h172.18.0.2 --all-databases
// This will show the dump of the entire MariaDB database
How to mitigate this security risk?
Mitigation is quite easy since we only need to set a root password for the MariaDB instance.
My recommended best practice is to avoid duplicate passwords. In order to do this, create a .env file in the directory where docker-compose.yml
is located.
MARIADB_ROOT_PASSWORD=aiPaipei6ookaemue4voo0NooC0AeH
Remember to replace the password by a random password or use this shell script to automatically create it:
echo MARIADB_ROOT_PASSWORD=$(pwgen 30) > .env
Now we can use ${MARIADB_ROOT_PASSWORD}
in docker-compose.yml
whereever the MariaDB root password is required, for example:
services:
mariadb:
image: 'mariadb:latest'
environment:
- MYSQL_ROOT_PASSWORD=${MARIADB_ROOT_PASSWORD}
- MYSQL_DATABASE=redmine
volumes:
- './mariadb_data:/var/lib/mysql'
redmine:
image: 'redmine:latest'
environment:
- REDMINE_USERNAME=admin
- REDMINE_PASSWORD=redmineadmin
- [email protected]
- REDMINE_DB_MYSQL=mariadb
- REDMINE_DB_USERNAME=root
- REDMINE_DB_PASSWORD=${MARIADB_ROOT_PASSWORD}
ports:
- '3718:3000'
volumes:
- './redmine_data/conf:/usr/src/redmine/conf'
- './redmine_data/files:/usr/src/redmine/files'
- './redmine_themes:/usr/src/redmine/public/themes'
depends_on:
- mariadb
Note that the mariadb
docker image will not change the root password if the database directory already exists(mariadb_data
in this example).
My recommended best practice for changing the root password is to use mysqldump --all-databases
to export the entire database to a SQL file, then backup and delete the data directory, then re-start the container so the new root password will be set. After that, re-import the dump from the SQL file.