at 25.11-pre 6.0 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 9 cfg = config.services.mongodb; 10 11 mongodb = cfg.package; 12 13 mongoshExe = lib.getExe cfg.mongoshPackage; 14 15 mongoCnf = 16 cfg: 17 pkgs.writeText "mongodb.conf" '' 18 net.bindIp: ${cfg.bind_ip} 19 ${lib.optionalString cfg.quiet "systemLog.quiet: true"} 20 systemLog.destination: syslog 21 storage.dbPath: ${cfg.dbpath} 22 ${lib.optionalString cfg.enableAuth "security.authorization: enabled"} 23 ${lib.optionalString (cfg.replSetName != "") "replication.replSetName: ${cfg.replSetName}"} 24 ${cfg.extraConfig} 25 ''; 26 27in 28 29{ 30 imports = [ 31 (lib.mkRemovedOptionModule [ 32 "services" 33 "mongodb" 34 "initialRootPassword" 35 ] "Use services.mongodb.initialRootPasswordFile to securely provide the initial root password.") 36 ]; 37 38 ###### interface 39 40 options = { 41 42 services.mongodb = { 43 44 enable = lib.mkEnableOption "the MongoDB server"; 45 46 package = lib.mkPackageOption pkgs "mongodb" { 47 example = "pkgs.mongodb-ce"; 48 }; 49 50 mongoshPackage = lib.mkPackageOption pkgs "mongosh" { }; 51 52 user = lib.mkOption { 53 type = lib.types.str; 54 default = "mongodb"; 55 description = "User account under which MongoDB runs"; 56 }; 57 58 bind_ip = lib.mkOption { 59 type = lib.types.str; 60 default = "127.0.0.1"; 61 description = "IP to bind to"; 62 }; 63 64 quiet = lib.mkOption { 65 type = lib.types.bool; 66 default = false; 67 description = "quieter output"; 68 }; 69 70 enableAuth = lib.mkOption { 71 type = lib.types.bool; 72 default = false; 73 description = "Enable client authentication. Creates a default superuser with username root!"; 74 }; 75 76 initialRootPasswordFile = lib.mkOption { 77 type = lib.types.nullOr lib.types.path; 78 default = null; 79 description = "Path to the file containing the password for the root user if auth is enabled."; 80 }; 81 82 dbpath = lib.mkOption { 83 type = lib.types.str; 84 default = "/var/db/mongodb"; 85 description = "Location where MongoDB stores its files"; 86 }; 87 88 pidFile = lib.mkOption { 89 type = lib.types.str; 90 default = "/run/mongodb.pid"; 91 description = "Location of MongoDB pid file"; 92 }; 93 94 replSetName = lib.mkOption { 95 type = lib.types.str; 96 default = ""; 97 description = '' 98 If this instance is part of a replica set, set its name here. 99 Otherwise, leave empty to run as single node. 100 ''; 101 }; 102 103 extraConfig = lib.mkOption { 104 type = lib.types.lines; 105 default = ""; 106 example = '' 107 storage.journal.enabled: false 108 ''; 109 description = "MongoDB extra configuration in YAML format"; 110 }; 111 112 initialScript = lib.mkOption { 113 type = lib.types.nullOr lib.types.path; 114 default = null; 115 description = '' 116 A file containing MongoDB statements to execute on first startup. 117 ''; 118 }; 119 }; 120 121 }; 122 123 ###### implementation 124 125 config = lib.mkIf config.services.mongodb.enable { 126 assertions = [ 127 { 128 assertion = !cfg.enableAuth || cfg.initialRootPasswordFile != null; 129 message = "`enableAuth` requires `initialRootPasswordFile` to be set."; 130 } 131 ]; 132 133 users.users.mongodb = lib.mkIf (cfg.user == "mongodb") { 134 name = "mongodb"; 135 isSystemUser = true; 136 group = "mongodb"; 137 description = "MongoDB server user"; 138 }; 139 users.groups.mongodb = lib.mkIf (cfg.user == "mongodb") { }; 140 141 systemd.services.mongodb = { 142 description = "MongoDB server"; 143 144 wantedBy = [ "multi-user.target" ]; 145 after = [ "network.target" ]; 146 147 serviceConfig = { 148 ExecStart = "${mongodb}/bin/mongod --config ${mongoCnf cfg} --fork --pidfilepath ${cfg.pidFile}"; 149 User = cfg.user; 150 PIDFile = cfg.pidFile; 151 Type = "forking"; 152 TimeoutStartSec = 120; # initial creating of journal can take some time 153 PermissionsStartOnly = true; 154 }; 155 156 preStart = 157 let 158 cfg_ = cfg // { 159 enableAuth = false; 160 bind_ip = "127.0.0.1"; 161 }; 162 in 163 '' 164 rm ${cfg.dbpath}/mongod.lock || true 165 if ! test -e ${cfg.dbpath}; then 166 install -d -m0700 -o ${cfg.user} ${cfg.dbpath} 167 # See postStart! 168 touch ${cfg.dbpath}/.first_startup 169 fi 170 if ! test -e ${cfg.pidFile}; then 171 install -D -o ${cfg.user} /dev/null ${cfg.pidFile} 172 fi '' 173 + lib.optionalString cfg.enableAuth '' 174 175 if ! test -e "${cfg.dbpath}/.auth_setup_complete"; then 176 systemd-run --unit=mongodb-for-setup --uid=${cfg.user} ${mongodb}/bin/mongod --config ${mongoCnf cfg_} 177 # wait for mongodb 178 while ! ${mongoshExe} --eval "db.version()" > /dev/null 2>&1; do sleep 0.1; done 179 180 initialRootPassword=$(<${cfg.initialRootPasswordFile}) 181 ${mongoshExe} <<EOF 182 use admin; 183 db.createUser( 184 { 185 user: "root", 186 pwd: "$initialRootPassword", 187 roles: [ 188 { role: "userAdminAnyDatabase", db: "admin" }, 189 { role: "dbAdminAnyDatabase", db: "admin" }, 190 { role: "readWriteAnyDatabase", db: "admin" } 191 ] 192 } 193 ) 194 EOF 195 touch "${cfg.dbpath}/.auth_setup_complete" 196 systemctl stop mongodb-for-setup 197 fi 198 ''; 199 postStart = '' 200 if test -e "${cfg.dbpath}/.first_startup"; then 201 ${lib.optionalString (cfg.initialScript != null) '' 202 initialRootPassword=$(<${cfg.initialRootPasswordFile}) 203 ${mongoshExe} ${lib.optionalString (cfg.enableAuth) "-u root -p $initialRootPassword"} admin "${cfg.initialScript}" 204 ''} 205 rm -f "${cfg.dbpath}/.first_startup" 206 fi 207 ''; 208 }; 209 210 }; 211 212}