1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.services.prosody; 8 9 sslOpts = { ... }: { 10 11 options = { 12 13 # TODO: require attribute 14 key = mkOption { 15 type = types.str; 16 description = "Path to the key file"; 17 }; 18 19 # TODO: require attribute 20 cert = mkOption { 21 type = types.str; 22 description = "Path to the certificate file"; 23 }; 24 }; 25 }; 26 27 moduleOpts = { 28 29 roster = mkOption { 30 default = true; 31 description = "Allow users to have a roster"; 32 }; 33 34 saslauth = mkOption { 35 default = true; 36 description = "Authentication for clients and servers. Recommended if you want to log in."; 37 }; 38 39 tls = mkOption { 40 default = true; 41 description = "Add support for secure TLS on c2s/s2s connections"; 42 }; 43 44 dialback = mkOption { 45 default = true; 46 description = "s2s dialback support"; 47 }; 48 49 disco = mkOption { 50 default = true; 51 description = "Service discovery"; 52 }; 53 54 legacyauth = mkOption { 55 default = true; 56 description = "Legacy authentication. Only used by some old clients and bots"; 57 }; 58 59 version = mkOption { 60 default = true; 61 description = "Replies to server version requests"; 62 }; 63 64 uptime = mkOption { 65 default = true; 66 description = "Report how long server has been running"; 67 }; 68 69 time = mkOption { 70 default = true; 71 description = "Let others know the time here on this server"; 72 }; 73 74 ping = mkOption { 75 default = true; 76 description = "Replies to XMPP pings with pongs"; 77 }; 78 79 console = mkOption { 80 default = false; 81 description = "telnet to port 5582"; 82 }; 83 84 bosh = mkOption { 85 default = false; 86 description = "Enable BOSH clients, aka 'Jabber over HTTP'"; 87 }; 88 89 httpserver = mkOption { 90 default = false; 91 description = "Serve static files from a directory over HTTP"; 92 }; 93 94 websocket = mkOption { 95 default = false; 96 description = "Enable WebSocket support"; 97 }; 98 99 }; 100 101 createSSLOptsStr = o: 102 if o ? key && o ? cert then 103 ''ssl = { key = "${o.key}"; certificate = "${o.cert}"; };'' 104 else ""; 105 106 vHostOpts = { ... }: { 107 108 options = { 109 110 # TODO: require attribute 111 domain = mkOption { 112 type = types.str; 113 description = "Domain name"; 114 }; 115 116 enabled = mkOption { 117 default = false; 118 description = "Whether to enable the virtual host"; 119 }; 120 121 ssl = mkOption { 122 description = "Paths to SSL files"; 123 default = null; 124 options = [ sslOpts ]; 125 }; 126 127 extraConfig = mkOption { 128 default = ''''; 129 description = "Additional virtual host specific configuration"; 130 }; 131 132 }; 133 134 }; 135 136in 137 138{ 139 140 ###### interface 141 142 options = { 143 144 services.prosody = { 145 146 enable = mkOption { 147 default = false; 148 description = "Whether to enable the prosody server"; 149 }; 150 151 allowRegistration = mkOption { 152 default = false; 153 description = "Allow account creation"; 154 }; 155 156 modules = moduleOpts; 157 158 extraModules = mkOption { 159 description = "Enable custom modules"; 160 default = []; 161 }; 162 163 virtualHosts = mkOption { 164 165 description = "Define the virtual hosts"; 166 167 type = types.loaOf types.optionSet; 168 169 example = { 170 myhost = { 171 domain = "my-xmpp-example-host.org"; 172 enabled = true; 173 }; 174 }; 175 176 default = { 177 localhost = { 178 domain = "localhost"; 179 enabled = true; 180 }; 181 }; 182 183 options = [ vHostOpts ]; 184 }; 185 186 ssl = mkOption { 187 description = "Paths to SSL files"; 188 default = null; 189 options = [ sslOpts ]; 190 }; 191 192 admins = mkOption { 193 description = "List of administrators of the current host"; 194 example = [ "admin1@example.com" "admin2@example.com" ]; 195 default = []; 196 }; 197 198 extraConfig = mkOption { 199 default = ''''; 200 description = "Additional prosody configuration"; 201 }; 202 203 }; 204 }; 205 206 207 ###### implementation 208 209 config = mkIf cfg.enable { 210 211 environment.systemPackages = [ pkgs.prosody ]; 212 213 environment.etc."prosody/prosody.cfg.lua".text = '' 214 215 pidfile = "/var/lib/prosody/prosody.pid" 216 217 218 log = "*syslog" 219 220 data_path = "/var/lib/prosody" 221 222 allow_registration = ${ if cfg.allowRegistration then "true" else "false" }; 223 224 ${ optionalString cfg.modules.console "console_enabled = true;" } 225 226 ${ optionalString (cfg.ssl != null) (createSSLOptsStr cfg.ssl) } 227 228 admins = { ${lib.concatStringsSep ", " (map (n: "\"${n}\"") cfg.admins) } }; 229 230 modules_enabled = { 231 232 ${ lib.concatStringsSep "\n\ \ " (lib.mapAttrsToList 233 (name: val: optionalString val ''"${name}";'') 234 cfg.modules) } 235 236 ${ optionalString cfg.allowRegistration "\"register\"\;" } 237 238 ${ lib.concatStringsSep "\n" (map (x: "\"${x}\";") cfg.extraModules)} 239 240 "posix"; 241 }; 242 243 ${ cfg.extraConfig } 244 245 ${ lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: '' 246 VirtualHost "${v.domain}" 247 enabled = ${if v.enabled then "true" else "false"}; 248 ${ optionalString (v.ssl != null) (createSSLOptsStr v.ssl) } 249 ${ v.extraConfig } 250 '') cfg.virtualHosts) } 251 ''; 252 253 users.extraUsers.prosody = { 254 uid = config.ids.uids.prosody; 255 description = "Prosody user"; 256 createHome = true; 257 group = "prosody"; 258 home = "/var/lib/prosody"; 259 }; 260 261 users.extraGroups.prosody = { 262 gid = config.ids.gids.prosody; 263 }; 264 265 systemd.services.prosody = { 266 267 description = "Prosody XMPP server"; 268 after = [ "network.target" ]; 269 wantedBy = [ "multi-user.target" ]; 270 serviceConfig = { 271 User = "prosody"; 272 PIDFile = "/var/lib/prosody/prosody.pid"; 273 ExecStart = "${pkgs.prosody}/bin/prosodyctl start"; 274 }; 275 276 }; 277 278 }; 279 280}