MariaDB en MySQL beveiligen: de stappen na de installatie

Gepubliceerd op 16 min leestijd

Na de pakketinstallatie is een database nog niet veilig. Deze gids loopt mysql_secure_installation door, beantwoordt de unix_socket-vraag en toont een eigen gebruiker per applicatie.

Een vers geïnstalleerde database op een rootserver is zelden zo onveilig als oudere handleidingen beweren, en zelden zo veilig als het pakketsysteem suggereert. Tussen die twee uitersten liggen precies de handelingen die dit artikel beschrijft: wat mysql_secure_installation werkelijk doet, waarom de unix_socket-vraag op moderne systemen anders beantwoord moet worden dan in 2015, hoe een applicatiegebruiker met minimale rechten eruitziet en hoe u wachtwoorden uit de proceslijst houdt.

Alle commando's draaien als root. Werkt u met een gewone gebruiker, zet er dan sudo voor. De uitvoer komt van Debian 13 en Debian 12 en van Ubuntu 24.04 en Ubuntu 22.04.

Uitgangspunt: welk pakket op welke distributie draait

Het eerste struikelblok ligt nog vóór de eerste beveiligingsstap. Debian levert al jaren geen eigen pakket met de naam mysql-server meer. Die naam bestaat daar alleen nog als virtueel pakket zonder eigen versie, uitsluitend zichtbaar via de afhankelijkheden van mariadb-server:

apt-cache policy mysql-server
mysql-server:
  Installed: (none)
  Candidate: (none)
  Version table:

Deze versievergelijking levert dus alleen onder Ubuntu een resultaat op, daar 8.0.46 op zowel 24.04 als 22.04. Op Debian gebruikt u in plaats daarvan apt-cache showpkg mysql-server, dat het virtuele karakter van de naam zichtbaar maakt.

Op Debian is de database daarmee altijd MariaDB. Op Ubuntu zijn beide beschikbaar en moet u kiezen. De versies in de actuele distributies:

Distributiemariadb-servermysql-server
Debian 1311.8alleen virtuele naam, geen versie
Debian 1210.11alleen virtuele naam, geen versie
Ubuntu 24.0410.118.0.46
Ubuntu 22.0410.68.0.46

Installeren gaat zoals gebruikelijk:

apt update
apt install -y mariadb-server

Of u werkelijk de verwachte versie te pakken heeft, blijkt niet uit de pakketversie alleen, maar uit de draaiende server:

mariadb -e "SELECT @@version, @@version_comment;"

Het tweede struikelblok is de programmanaam. Vanaf MariaDB 11.0 zitten de MySQL-compatibele namen niet meer in het hoofdpakket, maar in mariadb-client-compat en mariadb-server-compat. Op Debian 13 kan een mysql --version daarom ofwel met een waarschuwing antwoorden, ofwel helemaal niet bestaan:

mysql: Deprecated program name. It will be removed in a future release, use '/usr/bin/mariadb' instead
bash: mysql: command not found

Schrijft u scripts die op alle vier de systemen moeten draaien, gebruik onder MariaDB dan consequent mariadb, mariadb-dump en mariadb-secure-installation. Die namen bestaan sinds MariaDB 10.5, dus ook op Ubuntu 22.04.

mysql_secure_installation stap voor stap

Het gereedschap heet afhankelijk van de server anders. Onder MariaDB is de canonieke naam mariadb-secure-installation, onder MySQL 8.0 blijft het mysql_secure_installation. De oude naam bestaat onder MariaDB op Debian 12 en op Ubuntu 24.04 en 22.04 nog steeds als symlink, op Debian 13 daarentegen niet meer: daar bestaat uitsluitend mariadb-secure-installation.

Een blik op de opties loont, want het programma accepteert onder meer --defaults-file, --socket en --protocol. Dat overzicht staat echter alleen in de handleiding:

man mariadb-secure-installation

Een --help bestaat namelijk niet. Het script verwerkt argumenten helemaal niet, het slikt de onbekende optie stilzwijgend in en start meteen de interactieve procedure. Ontbreekt daarbij een echte console, bijvoorbeeld in een pipeline of in een container zonder terminal, dan herhaalt het de vraag om het wachtwoord eindeloos en keert het nooit uit zichzelf terug. Roep het daarom zonder argument aan, en in een interactieve shell:

mariadb-secure-installation

Op Debian 13 zet het script een duidelijke opmerking vooraf:

NOTE: MariaDB is secure by default in Debian. Running this script is useless at best,
and misleading at worst. This script will be removed in a future MariaDB release in Debian.

Debian vindt de doorloop daar dus overbodig en zal het script in een toekomstige versie verwijderen, na te lezen in /usr/share/doc/mariadb-server/README.Debian.gz. De volgende stappen lonen toch, want ze laten zien wat er gecontroleerd wordt en waarom er op een actueel systeem nauwelijks nog iets te veranderen valt.

De procedure begint met de vraag naar het wachtwoord voor root. De formulering hangt af van de versie van het script:

Enter current password for root (enter for none):

Nieuwere versies vragen in plaats daarvan:

Enter root user password or leave blank:

Beide keren wordt hetzelfde bedoeld. Bij een verse installatie bestaat er geen wachtwoord, druk hier dus gewoon op Enter.

Daarna volgen de eigenlijke beslissingen. Volgorde en formulering verschillen tussen MariaDB en MySQL, inhoudelijk gaat het om dezelfde vijf punten.

De unix_socket-vraag

Bij MariaDB verschijnt vervolgens:

Switch to unix_socket authentication [Y/n]

Die vraag zorgt voor verwarring, omdat hij op alle hier behandelde systemen allang beantwoord is. Sinds MariaDB 10.4 is root@localhost standaard beveiligd via de plug-in unix_socket, en dat geldt voor 10.6 op Ubuntu 22.04 net zo goed als voor 11.8 op Debian 13. Bij MySQL 8.0 heet de tegenhanger auth_socket, en de assistent zegt dat ook openlijk:

Skipping password set for root as authentication with auth_socket is used by default.

In de praktijk betekent dat: wie als systeemgebruiker root is ingelogd, komt met mariadb zonder wachtwoord in de database. Wie dat niet is, komt er helemaal niet in, ook niet met het juiste wachtwoord. Dat is geen tekortkoming, maar de sterkere variant. Er bestaat geen wachtwoord dat via een back-up, een configuratiebestand of een schermafbeelding kan weglekken. Het antwoord op de vraag luidt dus ja, behouden, en een extra rootwachtwoord is op een enkele applicatieserver overbodig.

De werkelijke toestand is alleen in de echte tabel te controleren. De voor de hand liggende query SELECT user, host, plugin FROM mysql.user brengt u hier namelijk op een dwaalspoor: bij root toont die de waarde mysql_native_password, terwijl in werkelijkheid unix_socket geldt. Bij MariaDB vanaf 10.4 is mysql.user nog slechts een view op mysql.global_priv, en die view kent per account maar één authenticatiemethode. Wie daarop vertrouwt, houdt de server ten onrechte voor wachtwoordgeauthenticeerd. De volledige regel staat in het JSON-veld Priv van de echte tabel:

mariadb -e "SELECT User, Host, JSON_DETAILED(Priv) FROM mysql.global_priv;"

Voor root op localhost staat daar onder MariaDB in essentie dit:

{"plugin":"mysql_native_password","authentication_string":"invalid","auth_or":[{},{"plugin":"unix_socket"}]}

De eerste vermelding is de wachtwoordmethode tegen de onbruikbare hash van de tekenreeks invalid, die niemand kan raden. De tweede vermelding onder auth_or is de socketauthenticatie die werkelijk geldt. Compacter vraagt u beide zo op:

mariadb -e "SELECT User, Host, JSON_VALUE(Priv,'$.plugin') AS plugin, JSON_QUERY(Priv,'$.auth_or') AS auth_or FROM mysql.global_priv;"

Verwacht wordt bij root en localhost een unix_socket, zo niet in de kolom plugin, dan onder auth_or. Staat daar uitsluitend een wachtwoordmethode en niets onder auth_or, dan werkt het account puur met een wachtwoord. Onder MySQL 8.0 bestaat mysql.global_priv niet, daar is mysql.user een echte tabel en moet de kolom plugin de waarde auth_socket tonen. Uit de MariaDB-view lezen kan overigens nog steeds, erin schrijven niet meer.

Alleen wanneer een tool dwingend een wachtwoord nodig heeft, bijvoorbeeld een monitoring die niet als root draait, schakelt u om. Zinvoller is het dan echter om een tweede beheeraccount aan te maken in plaats van root een andere rol te geven. In MariaDB vanaf 11.6, dus ook in 11.8 op Debian 13, kunt u een account bovendien aan een bepaalde systeemgebruiker koppelen:

CREATE USER 'dbadmin'@'localhost' IDENTIFIED VIA unix_socket AS 'deploy';

Daarmee mag de systeemgebruiker deploy inloggen als databasegebruiker dbadmin, zonder dat de namen gelijk hoeven te zijn. Op Debian 12 en de Ubuntu-versies wordt de tekenreeks na AS nog genegeerd, daar moeten systeemgebruiker en databasegebruiker dezelfde naam hebben.

De vier resterende vragen

De rest is onomstreden en wordt overal met ja beantwoord: anonieme gebruikers verwijderen, inloggen als root op afstand verbieden, de database test inclusief rechten verwijderen, rechtentabellen opnieuw laden. Op een actuele Debian of Ubuntu zijn anonieme gebruikers en de testdatabase meestal toch al niet aangemaakt, het script meldt dan simpelweg dat er niets te doen was.

Bij MySQL 8.0 komt er een vraag bij die MariaDB niet kent:

Would you like to setup VALIDATE PASSWORD component?

Deze component dwingt minimumeisen af voor alle wachtwoorden die voortaan worden ingesteld. Ze is nuttig wanneer meerdere personen gebruikers aanmaken. Ze is lastig wanneer een provisioningscript willekeurige wachtwoorden genereert die per ongeluk geen speciaal teken bevatten. Dan breekt het script af met:

ERROR 1819 (HY000): Your password does not satisfy the current policy requirements

Wie ze inschakelt, moet de wachtwoordgenerator daar vooraf op instellen. Het standaardniveau is MEDIUM en vereist minstens acht tekens, hoofdletters en kleine letters, een cijfer en een speciaal teken.

Als het misgaat: terug in de database

De meest voorkomende manier om uzelf buiten te sluiten, is de goedbedoelde overstap van unix_socket naar een wachtwoord dat vervolgens zoekraakt. Of andersom: een oud script maakt /etc/mysql/debian.cnf opnieuw aan, en ineens klopt er niets meer. De foutmeldingen waarop dan gezocht wordt, luiden:

ERROR 1698 (28000): Access denied for user 'root'@'localhost'
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
ERROR 1524 (HY000): Plugin 'unix_socket' is not loaded

Fout 1698 betekent: het account verwacht unix_socket, maar u bent niet de bijbehorende systeemgebruiker. Een sudo ervoor lost dat vaak al op. Fout 1045 betekent: er wordt een wachtwoord verwacht en het uwe klopt niet.

Komt u er helemaal niet meer in, start de server dan zonder rechtencontrole. Bij MariaDB gaat dat netjes via de omgevingsvariabele die de meegeleverde systemd-unit uitleest, geheel zonder pakketbestanden te bewerken:

systemctl stop mariadb
systemctl set-environment MYSQLD_OPTS="--skip-grant-tables --skip-networking"
systemctl start mariadb

Verbind nu met mariadb -u root en zet de toestand terug. Belangrijk is de FLUSH PRIVILEGES vooraf, want zonder geladen rechtentabellen mislukt ALTER USER:

FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED VIA unix_socket;

Draai dit daarna beslist weer terug, anders start de server na elke reboot onbeschermd:

systemctl stop mariadb
systemctl unset-environment MYSQLD_OPTS
systemctl start mariadb

Bij MySQL 8.0 op Ubuntu werkt deze weg niet, omdat de unit zo'n variabele niet uitleest en omdat ALTER USER onder --skip-grant-tables extra valkuilen kent. Daar gebruikt u een startbestand. Dat moet in een map staan die AppArmor het serverproces toestaat, anders mislukt de start met Can't open file. /var/lib/mysql-files is toegestaan, /tmp niet:

systemctl stop mysql
echo "ALTER USER 'root'@'localhost' IDENTIFIED WITH auth_socket;" > /var/lib/mysql-files/reset.sql
chown mysql:mysql /var/lib/mysql-files/reset.sql
systemctl edit mysql

In de editor komt een overschrijving van het startcommando, de lege eerste regel is nodig om de oorspronkelijke waarde te wissen:

[Service]
ExecStart=
ExecStart=/usr/sbin/mysqld --init-file=/var/lib/mysql-files/reset.sql

Na systemctl daemon-reload en een start is het account gereset. Verwijder daarna de overschrijving met systemctl revert mysql en wis het bestand. Draait u databases op gescheiden systemen, dan vindt u aanvullende aanwijzingen voor het beveiligen van de toegang in het artikel SSH-server beveiligen.

Een eigen gebruiker per applicatie in plaats van root

De meest effectieve stap komt in geen enkele assistent voor. Applicaties mogen niet als root verbinden, en ze hebben ook geen GRANT ALL nodig. Een typische webapplicatie leest en schrijft rijen, ze maakt geen databases aan en ze leest geen bestanden van de server.

CREATE DATABASE shopdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'shopapp'@'localhost' IDENTIFIED BY 'HierEenLangWillekeurigWachtwoord';
GRANT SELECT, INSERT, UPDATE, DELETE ON shopdb.* TO 'shopapp'@'localhost';

Daaraan zijn drie dingen belangrijk. Ten eerste de punt in shopdb.* in plaats van *.*: rechten op *.* zijn globale rechten en gelden ook voor mysql en information_schema. Ten tweede de vermelding @'localhost' in plaats van @'%': daarmee is het account uitsluitend lokaal bruikbaar, zelfs als de poort later een keer openstaat. Ten derde ontbreekt WITH GRANT OPTION, want een account dat rechten kan doorgeven, is feitelijk een beheerder.

Schemawijzigingen lopen dan via een tweede account dat alleen bij het deployment wordt gebruikt:

CREATE USER 'shopmigrate'@'localhost' IDENTIFIED BY 'EenAnderLangWillekeurigWachtwoord';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, DROP, INDEX, REFERENCES ON shopdb.* TO 'shopmigrate'@'localhost';

Dat klinkt als extra werk en is precies het punt waarop een SQL-injectie in de applicatie een vervelend datalek wordt in plaats van een totaalverlies. Een account zonder DROP kan geen tabel verwijderen.

Controleren doet u niet aan de formulering van de GRANT, maar aan het resultaat:

SHOW GRANTS FOR 'shopapp'@'localhost';

Verwacht worden precies twee regels: een GRANT USAGE ON *.*, dat enkel het inlogrecht vertegenwoordigt en geen enkele toegang tot gegevens betekent, en de regel met de vier rechten op shopdb.*. Duikt daar ALL PRIVILEGES ON *.* op, dan stond de punt op de verkeerde plek. De tweede, veelzeggendere proef is een keer inloggen met het nieuwe account en een SHOW DATABASES;. Zichtbaar mogen alleen information_schema en shopdb zijn.

Een veelvoorkomende fout bij het aanmaken:

ERROR 1396 (HY000): Operation CREATE USER failed for 'shopapp'@'localhost'

Dat betekent bijna altijd dat het account al bestaat, vaak als restant van een eerdere poging. DROP USER 'shopapp'@'localhost'; en dan opnieuw van voren af aan.

bind-address: waar de database eigenlijk luistert

Op alle vier de distributies luistert de database na de pakketinstallatie alleen op 127.0.0.1. Die regel staat bij MariaDB in /etc/mysql/mariadb.conf.d/50-server.cnf en bij MySQL 8.0 in /etc/mysql/mysql.conf.d/mysqld.cnf. Kijken in plaats van aannemen:

grep -R "bind-address" /etc/mysql/

Bij MySQL 8.0 is er een tweede regel die vaak over het hoofd wordt gezien: mysqlx-bind-address stuurt het X-protocol op poort 33060 aan. Wie alleen bind-address wijzigt, opent onder omstandigheden maar de halve toegang of laat de tweede poort openstaan.

De betrouwbaarste controle is niet het configuratiebestand, maar de kernel:

ss -lntp
LISTEN 0 80 127.0.0.1:3306 0.0.0.0:* users:(("mariadbd",pid=712,fd=22))

Staat daar 0.0.0.0:3306 of *:3306, dan is de dienst via het netwerk bereikbaar. Aanvullend levert de server zijn eigen kijk op de zaak:

mariadb -e "SELECT @@bind_address, @@port, @@skip_networking;"

Twee valkuilen komen hier veel voor. De eerste: de bestanden in mariadb.conf.d worden in alfabetische volgorde gelezen, en de als laatste gelezen instelling wint. Schrijft u uw wijziging in een eigen bestand, noem het dan 99-eigen.cnf en niet 10-eigen.cnf. Precies daarop stranden de meeste meldingen waarin MariaDB bind-address zogenaamd negeert. Het voordeel van een eigen bestand: pakketupdates vragen niet om conflictoplossing, omdat het meegeleverde bestand ongemoeid blijft.

De tweede valkuil: wie de netwerktoegang helemaal niet nodig heeft, gaat een stap verder dan bind-address en zet skip-networking. Dan bestaat er geen TCP-poort meer, alleen nog de Unix-socket. Dat is de juiste instelling voor het standaardgeval van webapplicatie en database op dezelfde server, maar het kost zenuwen wanneer een applicatie 127.0.0.1 in de configuratie heeft staan in plaats van localhost: bij 127.0.0.1 dwingen de clientbibliotheken TCP af.

Toegang van buitenaf alleen wanneer het echt nodig is

Een databasepoort die open op het internet staat, wordt binnen enkele uren gevonden en daarna onafgebroken bestookt met inlogpogingen. Het beste antwoord op de vraag naar externe toegang is daarom om die te vermijden. Voor incidenteel onderhoud volstaat een SSH-tunnel die op uw eigen computer een lokale poort op de databasesocket van de externe server legt. Het databasegereedschap verbindt dan met 127.0.0.1, zonder dat de server iets op het netwerk openzet.

Voor permanente verbindingen tussen meerdere servers is een WireGuard-tunnel de nette oplossing. De database bindt dan uitsluitend aan het tunneladres, niet aan het publieke IP-adres.

Moet het toch een open poort zijn, dan grijpen vier maatregelen in elkaar. De server bindt aan precies één intern adres. De firewall laat alleen het bekende bronadres door. Het databaseaccount is aan datzelfde adres gebonden, dus 'shopapp'@'10.0.0.5' en nooit 'shopapp'@'%'. En de verbinding wordt versleuteld afgedwongen:

ALTER USER 'shopapp'@'10.0.0.5' REQUIRE SSL;

Bij het testen van buitenaf komt u twee fouten tegen die vaak verward worden. De eerste betekent dat niemand antwoordt, dus firewall of bind-address:

ERROR 2003 (HY000): Can't connect to MySQL server on '203.0.113.10:3306' (110)

De tweede betekent dat de server wel antwoordt en de verbinding bewust weigert, er ontbreekt dus een passend account voor deze herkomst:

ERROR 1130 (HY000): Host '203.0.113.55' is not allowed to connect to this MariaDB server

Lokaal is de klassieker weer dat de dienst simpelweg niet draait:

ERROR 2002 (HY000): Can't connect to local server through socket '/run/mysqld/mysqld.sock' (2)

Wachtwoorden niet op de commandoregel

De aanroep mariadb -u shopapp -pGeheim123 werkt en is toch een fout. De server zegt het zelf:

Warning: Using a password on the command line interface can be insecure.

Daar zitten twee redenen achter. Ten eerste belandt de regel in de shell-historie. Ten tweede is de commandoregel van een proces op een standaardsysteem voor elke ingelogde gebruiker zichtbaar via ps. Op een server met meerdere klanten of meerdere diensten is dat een moeiteloze wachtwoorddiefstal.

De juiste weg voor mensen is -p zonder toevoeging. Dan wordt er interactief gevraagd en staat er niets in de historie:

mariadb -u shopapp -p shopdb

De juiste weg voor scripts en cronjobs is een optiebestand met strakke rechten. Aanmaken met de juiste modus in één stap:

install -m 600 /dev/null /root/.my.cnf

Inhoud:

[client]
user=backup
password=HierEenLangWillekeurigWachtwoord

Daarna vindt elke client de inloggegevens zelf. Voor gescheiden taken maakt u meerdere bestanden aan en verwijst u er gericht naar. Daarbij geldt een regel waarop velen stranden: --defaults-extra-file en --defaults-file moeten de eerste optie van de aanroep zijn, anders worden ze zonder commentaar genegeerd.

mariadb-dump --defaults-extra-file=/root/.my-backup.cnf --single-transaction shopdb

De omgevingsvariabele MYSQL_PWD is geen oplossing. Die staat in /proc en is daarmee net zo zichtbaar als de commandoregel. Onder MySQL 8.0 bestaat daarnaast mysql_config_editor, dat een bestand ~/.mylogin.cnf schrijft. De inhoud is versluierd, maar niet versleuteld, en MariaDB kent dat gereedschap niet. In gemengde omgevingen is het eenvoudige optiebestand met modus 600 de betrouwbaardere keuze.

Een laatste punt betreft back-ups. Een dump bevat alles wat de applicatie ziet, en een back-upaccount heeft daarvoor geen schrijfrechten nodig. Voor mariadb-dump met --single-transaction volstaat doorgaans dit:

GRANT SELECT, SHOW VIEW, TRIGGER, LOCK TABLES ON shopdb.* TO 'backup'@'localhost';
GRANT PROCESS ON *.* TO 'backup'@'localhost';

Oplevering: waaraan u ziet dat het klopt

Een commando dat zonder fout doorloopt, bewijst niets. Deze zes proeven bewijzen wel iets:

  1. Het rootaccount gebruikt socketauthenticatie: mariadb -e "SELECT User, Host, JSON_VALUE(Priv,'$.plugin') AS plugin, JSON_QUERY(Priv,'$.auth_or') AS auth_or FROM mysql.global_priv;" toont bij root een unix_socket, ofwel in de kolom plugin, ofwel onder auth_or. De view mysql.user deugt daar niet voor, die geeft alleen de eerste methode weer. Onder MySQL 8.0 is omgekeerd de kolom plugin in mysql.user maatgevend, daar moet auth_socket staan.
  2. Er zijn geen anonieme accounts: mariadb -e "SELECT user, host FROM mysql.user WHERE user = '';" levert een lege resultaatverzameling op.
  3. De testdatabase is weg: mariadb -e "SHOW DATABASES LIKE 'test';" levert niets op.
  4. De poort is dicht: ss -lntp toont voor 3306 ofwel helemaal niets, ofwel uitsluitend 127.0.0.1.
  5. Het applicatieaccount is beperkt: inloggen daarmee toont bij SHOW DATABASES; alleen de eigen database, en een DROP TABLE mislukt.
  6. Nergens een wachtwoord in platte tekst: grep -rs "password" /etc/cron.d/ /etc/cron.daily/ /etc/cron.hourly/ /etc/cron.weekly/ /etc/cron.monthly/ vindt niets, en evenmin crontab -l voor root en een blik in /var/spool/cron/crontabs/, en de optiebestanden hebben modus 600. De optie -s hoort erbij, omdat een ontbrekende map de aanroep anders met exitcode 2 afbreekt: op Debian 13 bestaat /etc/cron.d na een pure database-installatie niet, daar is nog helemaal geen cron-pakket geïnstalleerd.

Wie deze zes punten op een nieuwe server afwerkt, heeft de overgrote meerderheid van de aanvallen op databases al uitgesloten, zonder ook maar één extra stuk software te installeren. De rest is updatediscipline en een werkende back-up waarvan het terugzetten minstens één keer is geoefend.

Veelgestelde vragen

Heb ik onder MariaDB eigenlijk nog wel een rootwachtwoord nodig?
Op Debian 12 en 13 en op Ubuntu 22.04 en 24.04 is dat niet nodig. Het account root@localhost gebruikt daar standaard de plug-in unix_socket, het inloggen verloopt dus via de identiteit van de systeemgebruiker. Een extra wachtwoord verhoogt de veiligheid niet, maar creëert nog een geheim dat verloren kan gaan of kan weglekken. Alleen wanneer een tool dwingend met een wachtwoord wil inloggen, maakt u daarvoor een eigen account aan in plaats van root om te zetten.
Waarom zegt mijn Debian dat het pakket mysql-server niet bestaat?
Debian levert al sinds meerdere uitgaven geen eigen pakket mysql-server meer. Noch op Debian 12, noch op Debian 13 bestaat daarvan een installeerbare versie: apt-cache policy mysql-server meldt daar Candidate: (none) en een lege Version table, omdat de naam alleen nog virtueel via mariadb-server bestaat. Zichtbaar wordt dat met apt-cache showpkg mysql-server. Het commando voor de installatie luidt apt install -y mariadb-server. Op Ubuntu 22.04 en 24.04 zijn beide beschikbaar, MySQL in versie 8.0.46 en MariaDB in respectievelijk 10.6 en 10.11.
Hoe kom ik weer in de database als ik mezelf heb buitengesloten?
Bij MariaDB stopt u de dienst, zet u met systemctl set-environment de variabele MYSQLD_OPTS op --skip-grant-tables --skip-networking en start u opnieuw. Na het inloggen is eerst FLUSH PRIVILEGES nodig, daarna laat het account zich met ALTER USER resetten. Verwijder de variabele vervolgens weer met systemctl unset-environment. Bij MySQL 8.0 werkt dat niet, daar gebruikt u een startbestand via --init-file, dat vanwege AppArmor in /var/lib/mysql-files moet staan.
Wat betekent ERROR 1698 tegenover ERROR 1045?
ERROR 1698 (28000) betekent dat het account socketauthenticatie verwacht en dat de aanroepende systeemgebruiker niet past. Hier helpt meestal sudo vóór het commando. ERROR 1045 (28000) met de toevoeging (using password: YES) betekent daarentegen dat er met een wachtwoord is geprobeerd in te loggen en dat dat wachtwoord niet klopt. De twee fouten hebben dus volledig verschillende oorzaken en oplossingen.
Is bind-address = 127.0.0.1 genoeg om de database te beveiligen?
Het is de belangrijkste stap, maar niet de enige. Bij MySQL 8.0 bestaat daarnaast mysqlx-bind-address voor poort 33060, dat apart ingesteld moet worden. Bovendien wint in /etc/mysql/mariadb.conf.d het als laatste gelezen bestand, eigen wijzigingen horen daarom in een bestand met een hoog nummer zoals 99-eigen.cnf. Het resultaat controleert u altijd met ss -lntp, niet in het configuratiebestand.
Waarom mag de applicatie de database niet als root benaderen?
Omdat elk lek in de applicatie dan tot volledige toegang tot alle databases leidt, inclusief de rechtentabellen. Een account met GRANT SELECT, INSERT, UPDATE, DELETE ON mijndb.* kan geen tabel verwijderen, geen vreemde database lezen en geen rechten uitdelen. Schemawijzigingen lopen via een apart account dat alleen bij het deployment wordt gebruikt.
Hoe geef ik een cronjob veilig een databasewachtwoord mee?
Via een optiebestand met modus 600, bijvoorbeeld aangemaakt met install -m 600 /dev/null /root/.my.cnf en een sectie [client] met user en password. De aanroep verwijst daar dan met --defaults-extra-file naar, en wel dwingend als eerste optie, anders wordt de schakelaar genegeerd. Wachtwoorden direct achter -p op de commandoregel zijn voor elke gebruiker zichtbaar via ps, en MYSQL_PWD is via /proc net zo goed in te zien.

MariaDB MySQL Database Serverbeveiliging Debian Ubuntu Linux-beheer