]> git.immae.eu Git - perso/Immae/Config/Nix.git/blob - modules/private/databases/mariadb.nix
Add mysql replication
[perso/Immae/Config/Nix.git] / modules / private / databases / mariadb.nix
1 { lib, pkgs, config, ... }:
2 let
3 cfg = config.myServices.databases.mariadb;
4 in {
5 options.myServices.databases = {
6 mariadb = {
7 enable = lib.mkOption {
8 default = false;
9 example = true;
10 description = "Whether to enable mariadb database";
11 type = lib.types.bool;
12 };
13 package = lib.mkOption {
14 type = lib.types.package;
15 default = pkgs.mariadb;
16 description = ''
17 Mariadb package to use.
18 '';
19 };
20 credentials = lib.mkOption {
21 default = {};
22 description = "Credentials";
23 type = lib.types.attrsOf lib.types.str;
24 };
25 ldapConfig = lib.mkOption {
26 description = "LDAP configuration to allow PAM identification via LDAP";
27 type = lib.types.submodule {
28 options = {
29 host = lib.mkOption { type = lib.types.str; };
30 base = lib.mkOption { type = lib.types.str; };
31 dn = lib.mkOption { type = lib.types.str; };
32 password = lib.mkOption { type = lib.types.str; };
33 filter = lib.mkOption { type = lib.types.str; };
34 };
35 };
36 };
37 replicationLdapConfig = lib.mkOption {
38 description = "LDAP configuration to allow replication";
39 type = lib.types.submodule {
40 options = {
41 host = lib.mkOption { type = lib.types.str; };
42 base = lib.mkOption { type = lib.types.str; };
43 dn = lib.mkOption { type = lib.types.str; };
44 password = lib.mkOption { type = lib.types.str; };
45 };
46 };
47 };
48 dataDir = lib.mkOption {
49 type = lib.types.path;
50 default = "/var/lib/mysql";
51 description = ''
52 The directory where Mariadb stores its data.
53 '';
54 };
55 # Output variables
56 socketsDir = lib.mkOption {
57 type = lib.types.path;
58 default = "/run/mysqld";
59 description = ''
60 The directory where Mariadb puts sockets.
61 '';
62 };
63 sockets = lib.mkOption {
64 type = lib.types.attrsOf lib.types.path;
65 default = {
66 mysqld = "${cfg.socketsDir}/mysqld.sock";
67 };
68 readOnly = true;
69 description = ''
70 Mariadb sockets
71 '';
72 };
73 };
74 };
75
76 config = lib.mkIf cfg.enable {
77 networking.firewall.allowedTCPPorts = [ 3306 ];
78
79 # for adminer, ssl is implemented with mysqli only, which is
80 # currently disabled because it’s not compatible with pam.
81 # Thus we need to generate two users for each 'remote': one remote
82 # with SSL, and one localhost without SSL.
83 # User identified by LDAP:
84 # CREATE USER foo@% IDENTIFIED VIA pam USING 'mysql' REQUIRE SSL;
85 # CREATE USER foo@localhost IDENTIFIED VIA pam USING 'mysql';
86
87 # To create a user (host) for replication:
88 # CREATE USER 'host'@'%' IDENTIFIED VIA pam USING 'mysql_replication' REQUIRE SSL;
89 # GRANT REPLICATION SLAVE, REPLICATION CLIENT, RELOAD, LOCK TABLES, SELECT, SHOW VIEW ON *.* TO 'host'@'%';
90 # (the lock/select grant permits to let the replication host handle
91 # the initial fetch of the database)
92 # % should be valid for both localhost (for cron dumps) and the origin host.
93 services.mysql = {
94 enable = true;
95 package = cfg.package;
96 dataDir = cfg.dataDir;
97 extraOptions = ''
98 ssl_ca = ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
99 ssl_key = ${config.security.acme.directory}/mysql/key.pem
100 ssl_cert = ${config.security.acme.directory}/mysql/fullchain.pem
101
102 # for replication
103 log-bin=mariadb-bin
104 server-id=1
105 '';
106 };
107
108 users.users.mysql.extraGroups = [ "keys" ];
109 security.acme.certs."mysql" = config.myServices.databasesCerts // {
110 user = "mysql";
111 group = "mysql";
112 plugins = [ "fullchain.pem" "key.pem" "account_key.json" ];
113 domain = "db-1.immae.eu";
114 postRun = ''
115 systemctl restart mysql.service
116 '';
117 };
118
119 secrets.keys = [
120 {
121 dest = "mysql/mysqldump";
122 permissions = "0400";
123 user = "root";
124 group = "root";
125 text = ''
126 [mysqldump]
127 user = root
128 password = ${cfg.credentials.root}
129 '';
130 }
131 {
132 dest = "mysql/pam";
133 permissions = "0400";
134 user = "mysql";
135 group = "mysql";
136 text = with cfg.ldapConfig; ''
137 host ${host}
138 base ${base}
139 binddn ${dn}
140 bindpw ${password}
141 pam_filter ${filter}
142 ssl start_tls
143 '';
144 }
145 {
146 dest = "mysql/pam_replication";
147 permissions = "0400";
148 user = "mysql";
149 group = "mysql";
150 text = with cfg.replicationLdapConfig; ''
151 host ${host}
152 base ${base}
153 binddn ${dn}
154 bindpw ${password}
155 pam_login_attribute cn
156 ssl start_tls
157 '';
158 }
159 ];
160
161 security.pam.services = let
162 pam_ldap = "${pkgs.pam_ldap}/lib/security/pam_ldap.so";
163 in [
164 {
165 name = "mysql";
166 text = ''
167 # https://mariadb.com/kb/en/mariadb/pam-authentication-plugin/
168 auth required ${pam_ldap} config=${config.secrets.location}/mysql/pam
169 account required ${pam_ldap} config=${config.secrets.location}/mysql/pam
170 '';
171 }
172 {
173 name = "mysql_replication";
174 text = ''
175 auth required ${pam_ldap} config=${config.secrets.location}/mysql/pam_replication
176 account required ${pam_ldap} config=${config.secrets.location}/mysql/pam_replication
177 '';
178 }
179 ];
180
181 };
182 }