165 lines
4.6 KiB
Nix
165 lines
4.6 KiB
Nix
{ config, lib, pkgs, ... }@toplevel:
|
|
|
|
with lib;
|
|
let
|
|
cfg = config.fudo.services.mqtt;
|
|
hostname = config.instance.hostname;
|
|
isMqttServer = cfg.host == hostname;
|
|
|
|
aclOption = with types;
|
|
mkOption {
|
|
type = listOf str;
|
|
description = "Topic filter to which this user has access.";
|
|
example = [ "some/topic/#" "other/specific/topic" ];
|
|
};
|
|
|
|
userOpts = { name, ... }: {
|
|
options = with types; {
|
|
username = mkOption {
|
|
type = str;
|
|
default = name;
|
|
};
|
|
|
|
password-file = mkOption {
|
|
type = str;
|
|
description =
|
|
"Path to file (on the BUILD HOST) containing the user's password.";
|
|
};
|
|
|
|
acl = aclOption;
|
|
};
|
|
};
|
|
|
|
mosquittoUser = config.systemd.services.mosquitto.serviceConfig.User;
|
|
|
|
pwTarget = type: username: "/run/mqtt/${type}-${username}.passwd";
|
|
|
|
mqttDomain = config.fudo.hosts."${cfg.host}".domain;
|
|
|
|
in {
|
|
options.fudo.services.mqtt = with types; {
|
|
enable = mkEnableOption "Enable MQTT server.";
|
|
|
|
host = mkOption {
|
|
type = str;
|
|
description =
|
|
"Hostname of the MQTT server for this site/domain/whatever.";
|
|
};
|
|
|
|
listen-address = mkOption {
|
|
type = str;
|
|
description = "IP address on which to listen.";
|
|
default = "0.0.0.0";
|
|
};
|
|
|
|
private = {
|
|
enable = mkOption {
|
|
type = bool;
|
|
description = "Enable a private (authenticated) MQTT server.";
|
|
default = true;
|
|
};
|
|
|
|
port = mkOption {
|
|
type = port;
|
|
description = "Port at which to listen for incoming MQTT requests.";
|
|
default = 1883;
|
|
};
|
|
|
|
users = mkOption {
|
|
type = attrsOf (submodule userOpts);
|
|
default = { };
|
|
};
|
|
};
|
|
|
|
public = {
|
|
enable = mkEnableOption "Enable a public (anonymous) MQTT server.";
|
|
|
|
port = mkOption {
|
|
type = port;
|
|
description = "Port at which to listen for incoming MQTT requests.";
|
|
default = 1884;
|
|
};
|
|
|
|
users = mkOption {
|
|
type = attrsOf (submodule userOpts);
|
|
default = { };
|
|
};
|
|
|
|
acl = aclOption;
|
|
};
|
|
|
|
state-directory = mkOption {
|
|
type = str;
|
|
description = "Directory where server can store persistent state.";
|
|
};
|
|
|
|
mqtt-hostname = let
|
|
mqtt-host = toplevel.config.fudo.services.mqtt.host;
|
|
mqtt-domain = toplevel.config.fudo.hosts."${mqtt-host}".domain;
|
|
in mkOption {
|
|
type = str;
|
|
description = "Hostname at which the MQTT server can be reached.";
|
|
default = "mqtt.${mqtt-domain}";
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
networking.firewall.allowedTCPPorts =
|
|
(optional cfg.private.enable cfg.private.port)
|
|
++ (optional cfg.public.enable cfg.public.port);
|
|
|
|
systemd = {
|
|
services.mosquitto = {
|
|
after = [ config.fudo.secrets.secret-target ];
|
|
restartIfChanged = true;
|
|
};
|
|
tmpfiles.rules = optional isMqttServer
|
|
"d ${cfg.state-directory} 0700 ${mosquittoUser} - - -";
|
|
};
|
|
|
|
fudo = {
|
|
zones."${mqttDomain}".aliases.mqtt = cfg.host;
|
|
|
|
secrets.host-secrets."${hostname}" = mkIf isMqttServer (let
|
|
publicUsers = mapAttrs' (_: userOpts:
|
|
nameValuePair "mqtt-public-user-${userOpts.username}" {
|
|
source-file = userOpts.password-file;
|
|
target-file = pwTarget "public" userOpts.username;
|
|
user = mosquittoUser;
|
|
}) cfg.public.users;
|
|
privateUsers = mapAttrs' (_: userOpts:
|
|
nameValuePair "mqtt-private-user-${userOpts.username}" {
|
|
source-file = userOpts.password-file;
|
|
target-file = pwTarget "private" userOpts.username;
|
|
user = mosquittoUser;
|
|
}) cfg.private.users;
|
|
in publicUsers // privateUsers);
|
|
};
|
|
|
|
services.mosquitto = mkIf isMqttServer {
|
|
enable = true;
|
|
dataDir = cfg.state-directory;
|
|
listeners = (optional cfg.private.enable {
|
|
settings.allow_anonymous = false;
|
|
port = cfg.private.port;
|
|
address = cfg.listen-address;
|
|
users = mapAttrs' (_: userOpts:
|
|
nameValuePair userOpts.username {
|
|
acl = userOpts.acl;
|
|
passwordFile = pwTarget "private" userOpts.username;
|
|
}) cfg.private.users;
|
|
}) ++ (optional cfg.public.enable {
|
|
settings.allow_anonymous = true;
|
|
acl = map (line: "topic ${line}") cfg.public.acl;
|
|
port = cfg.public.port;
|
|
address = cfg.listen-address;
|
|
users = mapAttrs' (_: userOpts:
|
|
nameValuePair userOpts.username {
|
|
acl = userOpts.acl;
|
|
passwordFile = pwTarget "public" userOpts.username;
|
|
}) cfg.public.users;
|
|
});
|
|
};
|
|
};
|
|
}
|