mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-07-06 17:13:24 -05:00
nixos/mysql: fix default MySQL/Percona Server insecure authentication
Existing deployments using MySQL or Percona Server with `services.mysql`
do not restrict authentication to the root@localhost user allowing any
user on the system to log in without a password.
With this changes:
* new deployments will use the `auth_socket` authentication by default
to only allow the local root user to log in as the MySQL root user
* existing deployments with a stateVersion > 26.05 will also be moved to
the `auth_socket` authentication
* existing deployments with a stateVersion <= 26.05 have a warning
logged when the local authentication seems to be open
Users can disable this behavior by setting `services.mysql.secureSuperUserByDefault`
to `false`.
(cherry picked from commit 1d6947077e)
This commit is contained in:
parent
2c93fd12df
commit
e36354e1c1
4 changed files with 144 additions and 0 deletions
|
|
@ -293,6 +293,19 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
secureSuperUserByDefault = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to automatically secure the root@localhost user with auth_socket authentication.
|
||||
|
||||
::: {.note}
|
||||
When enabled (default), the module will ensure root@localhost uses socket authentication,
|
||||
preventing any local user from connecting as root without proper credentials.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
||||
replication = {
|
||||
role = lib.mkOption {
|
||||
type = lib.types.enum [
|
||||
|
|
@ -411,6 +424,10 @@ in
|
|||
assertion = !cfg.galeraCluster.enable || isMariaDB;
|
||||
message = "'services.mysql.galeraCluster.enable' expect services.mysql.package to be an mariadb variant";
|
||||
}
|
||||
{
|
||||
assertion = !isMariaDB || cfg.secureSuperUserByDefault == true;
|
||||
message = "'services.mysql.secureSuperUserByDefault' has no effect on MariaDB (which is already secure by default)";
|
||||
}
|
||||
]
|
||||
# galeraCluster options checks
|
||||
++ lib.optionals cfg.galeraCluster.enable [
|
||||
|
|
@ -571,6 +588,7 @@ in
|
|||
let
|
||||
# The super user account to use on *first* run of MySQL server
|
||||
superUser = if isMariaDB then cfg.user else "root";
|
||||
isStateVersion2611Plus = lib.versionAtLeast config.system.stateVersion "26.11";
|
||||
in
|
||||
''
|
||||
${lib.optionalString isMariaDB ''
|
||||
|
|
@ -644,6 +662,11 @@ in
|
|||
) | ${cfg.package}/bin/mysql -u ${superUser} -N
|
||||
''}
|
||||
|
||||
# Secure root@localhost for MySQL/Percona on first initialization
|
||||
${lib.optionalString (cfg.secureSuperUserByDefault && !isMariaDB) ''
|
||||
echo "ALTER USER root@localhost IDENTIFIED WITH auth_socket;" | ${cfg.package}/bin/mysql -u ${superUser} -N
|
||||
''}
|
||||
|
||||
${lib.optionalString (cfg.initialScript != null) ''
|
||||
# Execute initial script
|
||||
# using toString to avoid copying the file to nix store if given as path instead of string,
|
||||
|
|
@ -654,6 +677,27 @@ in
|
|||
rm ${cfg.dataDir}/mysql_init
|
||||
fi
|
||||
|
||||
${lib.optionalString (cfg.secureSuperUserByDefault && !isMariaDB) ''
|
||||
# We try to detect if we are in the default insecure auth mode for MySQL (all users can connect with password)
|
||||
# If the configuration has been moved to the socket-peer credential authentication we do nothing
|
||||
# If we are not able to connect it also means the default setup has been adjusted, so we also skip and do not do any changes
|
||||
if plugin_info=$(${cfg.package}/bin/mysql -u ${superUser} --skip-column-names 2>/dev/null -e "SELECT plugin FROM mysql.user WHERE user = 'root' AND host = 'localhost';"); then
|
||||
case "$plugin_info" in
|
||||
*auth_socket*) ;;
|
||||
*)
|
||||
${lib.optionalString isStateVersion2611Plus ''
|
||||
# Attempt to auto-fix to prevent local authentication without a password
|
||||
echo "Securing root@localhost with auth_socket to local connection without password, see https://github.com/NixOS/nixpkgs/security/advisories/GHSA-6qxx-6rg8-c4p8" >&2
|
||||
echo "ALTER USER root@localhost IDENTIFIED WITH auth_socket;" | ${cfg.package}/bin/mysql -u ${superUser} -N
|
||||
''}
|
||||
${lib.optionalString (!isStateVersion2611Plus) ''
|
||||
echo "Security warning: root@localhost seems to have open authentication, consider adjusting your configuration. See https://github.com/NixOS/nixpkgs/security/advisories/GHSA-6qxx-6rg8-c4p8" >&2
|
||||
''}
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
''}
|
||||
|
||||
${lib.optionalString (cfg.ensureDatabases != [ ]) ''
|
||||
(
|
||||
${lib.concatMapStrings (database: ''
|
||||
|
|
|
|||
|
|
@ -1043,6 +1043,7 @@ in
|
|||
mysql-autobackup = handleTest ./mysql/mysql-autobackup.nix { };
|
||||
mysql-backup = handleTest ./mysql/mysql-backup.nix { };
|
||||
mysql-replication = handleTest ./mysql/mysql-replication.nix { };
|
||||
mysql-secure-root = handleTest ./mysql/mysql-secure-root.nix { };
|
||||
n8n = runTest ./n8n.nix;
|
||||
nagios = runTestOn [ "x86_64-linux" "aarch64-linux" ] ./nagios.nix;
|
||||
nar-serve = runTest ./nar-serve.nix;
|
||||
|
|
|
|||
94
nixos/tests/mysql/mysql-secure-root.nix
Normal file
94
nixos/tests/mysql/mysql-secure-root.nix
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
{
|
||||
system ? builtins.currentSystem,
|
||||
config ? { },
|
||||
pkgs ? import ../../.. { inherit system config; },
|
||||
lib ? pkgs.lib,
|
||||
}:
|
||||
|
||||
let
|
||||
makeTest = import ./../make-test-python.nix;
|
||||
inherit (import ./common.nix { inherit pkgs lib; })
|
||||
mysqlPackages
|
||||
;
|
||||
|
||||
makeSecureRootTest =
|
||||
{
|
||||
package,
|
||||
name ? "mysql_secure_root_" + (builtins.replaceStrings [ "-" "." ] [ "_" "" ] package.pname),
|
||||
}:
|
||||
makeTest {
|
||||
inherit name;
|
||||
|
||||
nodes.${name} = { pkgs, ... }: {
|
||||
services.mysql = {
|
||||
enable = true;
|
||||
package = package;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
machine = ${name}
|
||||
machine.wait_for_unit("mysql")
|
||||
|
||||
# Verify that non-root user cannot connect as root
|
||||
machine.fail("sudo -u nobody mysql -u root -e 'SELECT 1;' 2>&1")
|
||||
|
||||
# Verify that system root can connect as root via socket
|
||||
machine.succeed("mysql -u root -e 'SELECT 1;'")
|
||||
|
||||
# Verify that root@localhost has auth_socket plugin
|
||||
machine.succeed("[ \"$(mysql -u root -N -e \"SELECT plugin FROM mysql.user WHERE user = 'root' AND host = 'localhost';\")\" = \"auth_socket\" ]")
|
||||
|
||||
# Test service restart - verify it still works
|
||||
machine.succeed("systemctl restart mysql")
|
||||
machine.wait_for_unit("mysql")
|
||||
|
||||
# After restart, verify non-root user still cannot connect as root
|
||||
machine.fail("sudo -u nobody mysql -u root -e 'SELECT 1;' 2>&1")
|
||||
|
||||
# After restart, verify system root can still connect
|
||||
machine.succeed("mysql -u root -e 'SELECT 1;'")
|
||||
|
||||
# After restart, verify root@localhost still has auth_socket
|
||||
machine.succeed("[ \"$(mysql -u root -N -e \"SELECT plugin FROM mysql.user WHERE user = 'root' AND host = 'localhost';\")\" = \"auth_socket\" ]")
|
||||
'';
|
||||
};
|
||||
|
||||
makeInsecureRootTest =
|
||||
{
|
||||
package,
|
||||
name ? "mysql_insecure_root_" + (builtins.replaceStrings [ "-" "." ] [ "_" "" ] package.pname),
|
||||
}:
|
||||
makeTest {
|
||||
inherit name;
|
||||
|
||||
nodes.${name} = { pkgs, ... }: {
|
||||
services.mysql = {
|
||||
enable = true;
|
||||
package = package;
|
||||
secureSuperUserByDefault = false;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
machine = ${name}
|
||||
machine.wait_for_unit("mysql")
|
||||
|
||||
# With secureRootByDefault = false, anyone can connect as root (default --initialize-insecure behavior)
|
||||
machine.succeed("sudo -u nobody mysql -u root -e 'SELECT 1;' 2>&1")
|
||||
'';
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
"secure-by-default" = lib.mapAttrs (
|
||||
_: package: makeSecureRootTest { inherit package; }
|
||||
) mysqlPackages;
|
||||
"can-be-insecure" = lib.mapAttrs (
|
||||
_: package: makeInsecureRootTest { inherit package; }
|
||||
) mysqlPackages;
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
libtirpc,
|
||||
rpcsvc-proto,
|
||||
curl,
|
||||
nixosTests,
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
|
|
@ -111,6 +112,10 @@ stdenv.mkDerivation (finalAttrs: {
|
|||
connector-c = finalAttrs.finalPackage;
|
||||
server = finalAttrs.finalPackage;
|
||||
mysqlVersion = lib.versions.majorMinor finalAttrs.version;
|
||||
tests.mysql-secure-root-by-default =
|
||||
nixosTests.mysql-secure-root.secure-by-default."mysql${lib.versions.major finalAttrs.version}${lib.versions.minor finalAttrs.version}";
|
||||
tests.mysql-root-can-be-kept-insecure =
|
||||
nixosTests.mysql-secure-root.can-be-insecure."mysql${lib.versions.major finalAttrs.version}${lib.versions.minor finalAttrs.version}";
|
||||
};
|
||||
|
||||
meta = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue