uboot: (firmwareOdroidC2/C4) don't invoke patch tool, use patches = [] instead
https://github.com/NixOS/nixpkgs/blob/master/pkgs/stdenv/generic/setup.sh#L948 this can do it nicely. Signed-off-by: Anton Arapov <anton@deadbeef.mx>
This commit is contained in:
commit
56de2bcd43
30691 changed files with 3076956 additions and 0 deletions
675
nixos/modules/services/networking/mosquitto.nix
Normal file
675
nixos/modules/services/networking/mosquitto.nix
Normal file
|
|
@ -0,0 +1,675 @@
|
|||
{ config, lib, pkgs, ...}:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.mosquitto;
|
||||
|
||||
# note that mosquitto config parsing is very simplistic as of may 2021.
|
||||
# often times they'll e.g. strtok() a line, check the first two tokens, and ignore the rest.
|
||||
# there's no escaping available either, so we have to prevent any being necessary.
|
||||
str = types.strMatching "[^\r\n]*" // {
|
||||
description = "single-line string";
|
||||
};
|
||||
path = types.addCheck types.path (p: str.check "${p}");
|
||||
configKey = types.strMatching "[^\r\n\t ]+";
|
||||
optionType = with types; oneOf [ str path bool int ] // {
|
||||
description = "string, path, bool, or integer";
|
||||
};
|
||||
optionToString = v:
|
||||
if isBool v then boolToString v
|
||||
else if path.check v then "${v}"
|
||||
else toString v;
|
||||
|
||||
assertKeysValid = prefix: valid: config:
|
||||
mapAttrsToList
|
||||
(n: _: {
|
||||
assertion = valid ? ${n};
|
||||
message = "Invalid config key ${prefix}.${n}.";
|
||||
})
|
||||
config;
|
||||
|
||||
formatFreeform = { prefix ? "" }: mapAttrsToList (n: v: "${prefix}${n} ${optionToString v}");
|
||||
|
||||
userOptions = with types; submodule {
|
||||
options = {
|
||||
password = mkOption {
|
||||
type = uniq (nullOr str);
|
||||
default = null;
|
||||
description = ''
|
||||
Specifies the (clear text) password for the MQTT User.
|
||||
'';
|
||||
};
|
||||
|
||||
passwordFile = mkOption {
|
||||
type = uniq (nullOr types.path);
|
||||
example = "/path/to/file";
|
||||
default = null;
|
||||
description = ''
|
||||
Specifies the path to a file containing the
|
||||
clear text password for the MQTT user.
|
||||
'';
|
||||
};
|
||||
|
||||
hashedPassword = mkOption {
|
||||
type = uniq (nullOr str);
|
||||
default = null;
|
||||
description = ''
|
||||
Specifies the hashed password for the MQTT User.
|
||||
To generate hashed password install <literal>mosquitto</literal>
|
||||
package and use <literal>mosquitto_passwd</literal>.
|
||||
'';
|
||||
};
|
||||
|
||||
hashedPasswordFile = mkOption {
|
||||
type = uniq (nullOr types.path);
|
||||
example = "/path/to/file";
|
||||
default = null;
|
||||
description = ''
|
||||
Specifies the path to a file containing the
|
||||
hashed password for the MQTT user.
|
||||
To generate hashed password install <literal>mosquitto</literal>
|
||||
package and use <literal>mosquitto_passwd</literal>.
|
||||
'';
|
||||
};
|
||||
|
||||
acl = mkOption {
|
||||
type = listOf str;
|
||||
example = [ "read A/B" "readwrite A/#" ];
|
||||
default = [];
|
||||
description = ''
|
||||
Control client access to topics on the broker.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
userAsserts = prefix: users:
|
||||
mapAttrsToList
|
||||
(n: _: {
|
||||
assertion = builtins.match "[^:\r\n]+" n != null;
|
||||
message = "Invalid user name ${n} in ${prefix}";
|
||||
})
|
||||
users
|
||||
++ mapAttrsToList
|
||||
(n: u: {
|
||||
assertion = count (s: s != null) [
|
||||
u.password u.passwordFile u.hashedPassword u.hashedPasswordFile
|
||||
] <= 1;
|
||||
message = "Cannot set more than one password option for user ${n} in ${prefix}";
|
||||
}) users;
|
||||
|
||||
makePasswordFile = users: path:
|
||||
let
|
||||
makeLines = store: file:
|
||||
mapAttrsToList
|
||||
(n: u: "addLine ${escapeShellArg n} ${escapeShellArg u.${store}}")
|
||||
(filterAttrs (_: u: u.${store} != null) users)
|
||||
++ mapAttrsToList
|
||||
(n: u: "addFile ${escapeShellArg n} ${escapeShellArg "${u.${file}}"}")
|
||||
(filterAttrs (_: u: u.${file} != null) users);
|
||||
plainLines = makeLines "password" "passwordFile";
|
||||
hashedLines = makeLines "hashedPassword" "hashedPasswordFile";
|
||||
in
|
||||
pkgs.writeScript "make-mosquitto-passwd"
|
||||
(''
|
||||
#! ${pkgs.runtimeShell}
|
||||
|
||||
set -eu
|
||||
|
||||
file=${escapeShellArg path}
|
||||
|
||||
rm -f "$file"
|
||||
touch "$file"
|
||||
|
||||
addLine() {
|
||||
echo "$1:$2" >> "$file"
|
||||
}
|
||||
addFile() {
|
||||
if [ $(wc -l <"$2") -gt 1 ]; then
|
||||
echo "invalid mosquitto password file $2" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "$1:$(cat "$2")" >> "$file"
|
||||
}
|
||||
''
|
||||
+ concatStringsSep "\n"
|
||||
(plainLines
|
||||
++ optional (plainLines != []) ''
|
||||
${cfg.package}/bin/mosquitto_passwd -U "$file"
|
||||
''
|
||||
++ hashedLines));
|
||||
|
||||
makeACLFile = idx: users: supplement:
|
||||
pkgs.writeText "mosquitto-acl-${toString idx}.conf"
|
||||
(concatStringsSep
|
||||
"\n"
|
||||
(flatten [
|
||||
supplement
|
||||
(mapAttrsToList
|
||||
(n: u: [ "user ${n}" ] ++ map (t: "topic ${t}") u.acl)
|
||||
users)
|
||||
]));
|
||||
|
||||
authPluginOptions = with types; submodule {
|
||||
options = {
|
||||
plugin = mkOption {
|
||||
type = path;
|
||||
description = ''
|
||||
Plugin path to load, should be a <literal>.so</literal> file.
|
||||
'';
|
||||
};
|
||||
|
||||
denySpecialChars = mkOption {
|
||||
type = bool;
|
||||
description = ''
|
||||
Automatically disallow all clients using <literal>#</literal>
|
||||
or <literal>+</literal> in their name/id.
|
||||
'';
|
||||
default = true;
|
||||
};
|
||||
|
||||
options = mkOption {
|
||||
type = attrsOf optionType;
|
||||
description = ''
|
||||
Options for the auth plugin. Each key turns into a <literal>auth_opt_*</literal>
|
||||
line in the config.
|
||||
'';
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
authAsserts = prefix: auth:
|
||||
mapAttrsToList
|
||||
(n: _: {
|
||||
assertion = configKey.check n;
|
||||
message = "Invalid auth plugin key ${prefix}.${n}";
|
||||
})
|
||||
auth;
|
||||
|
||||
formatAuthPlugin = plugin:
|
||||
[
|
||||
"auth_plugin ${plugin.plugin}"
|
||||
"auth_plugin_deny_special_chars ${optionToString plugin.denySpecialChars}"
|
||||
]
|
||||
++ formatFreeform { prefix = "auth_opt_"; } plugin.options;
|
||||
|
||||
freeformListenerKeys = {
|
||||
allow_anonymous = 1;
|
||||
allow_zero_length_clientid = 1;
|
||||
auto_id_prefix = 1;
|
||||
bind_interface = 1;
|
||||
cafile = 1;
|
||||
capath = 1;
|
||||
certfile = 1;
|
||||
ciphers = 1;
|
||||
"ciphers_tls1.3" = 1;
|
||||
crlfile = 1;
|
||||
dhparamfile = 1;
|
||||
http_dir = 1;
|
||||
keyfile = 1;
|
||||
max_connections = 1;
|
||||
max_qos = 1;
|
||||
max_topic_alias = 1;
|
||||
mount_point = 1;
|
||||
protocol = 1;
|
||||
psk_file = 1;
|
||||
psk_hint = 1;
|
||||
require_certificate = 1;
|
||||
socket_domain = 1;
|
||||
tls_engine = 1;
|
||||
tls_engine_kpass_sha1 = 1;
|
||||
tls_keyform = 1;
|
||||
tls_version = 1;
|
||||
use_identity_as_username = 1;
|
||||
use_subject_as_username = 1;
|
||||
use_username_as_clientid = 1;
|
||||
};
|
||||
|
||||
listenerOptions = with types; submodule {
|
||||
options = {
|
||||
port = mkOption {
|
||||
type = port;
|
||||
description = ''
|
||||
Port to listen on. Must be set to 0 to listen on a unix domain socket.
|
||||
'';
|
||||
default = 1883;
|
||||
};
|
||||
|
||||
address = mkOption {
|
||||
type = nullOr str;
|
||||
description = ''
|
||||
Address to listen on. Listen on <literal>0.0.0.0</literal>/<literal>::</literal>
|
||||
when unset.
|
||||
'';
|
||||
default = null;
|
||||
};
|
||||
|
||||
authPlugins = mkOption {
|
||||
type = listOf authPluginOptions;
|
||||
description = ''
|
||||
Authentication plugin to attach to this listener.
|
||||
Refer to the <link xlink:href="https://mosquitto.org/man/mosquitto-conf-5.html">
|
||||
mosquitto.conf documentation</link> for details on authentication plugins.
|
||||
'';
|
||||
default = [];
|
||||
};
|
||||
|
||||
users = mkOption {
|
||||
type = attrsOf userOptions;
|
||||
example = { john = { password = "123456"; acl = [ "readwrite john/#" ]; }; };
|
||||
description = ''
|
||||
A set of users and their passwords and ACLs.
|
||||
'';
|
||||
default = {};
|
||||
};
|
||||
|
||||
omitPasswordAuth = mkOption {
|
||||
type = bool;
|
||||
description = ''
|
||||
Omits password checking, allowing anyone to log in with any user name unless
|
||||
other mandatory authentication methods (eg TLS client certificates) are configured.
|
||||
'';
|
||||
default = false;
|
||||
};
|
||||
|
||||
acl = mkOption {
|
||||
type = listOf str;
|
||||
description = ''
|
||||
Additional ACL items to prepend to the generated ACL file.
|
||||
'';
|
||||
example = [ "pattern read #" "topic readwrite anon/report/#" ];
|
||||
default = [];
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
type = submodule {
|
||||
freeformType = attrsOf optionType;
|
||||
};
|
||||
description = ''
|
||||
Additional settings for this listener.
|
||||
'';
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
listenerAsserts = prefix: listener:
|
||||
assertKeysValid "${prefix}.settings" freeformListenerKeys listener.settings
|
||||
++ userAsserts prefix listener.users
|
||||
++ imap0
|
||||
(i: v: authAsserts "${prefix}.authPlugins.${toString i}" v)
|
||||
listener.authPlugins;
|
||||
|
||||
formatListener = idx: listener:
|
||||
[
|
||||
"listener ${toString listener.port} ${toString listener.address}"
|
||||
"acl_file ${makeACLFile idx listener.users listener.acl}"
|
||||
]
|
||||
++ optional (! listener.omitPasswordAuth) "password_file ${cfg.dataDir}/passwd-${toString idx}"
|
||||
++ formatFreeform {} listener.settings
|
||||
++ concatMap formatAuthPlugin listener.authPlugins;
|
||||
|
||||
freeformBridgeKeys = {
|
||||
bridge_alpn = 1;
|
||||
bridge_attempt_unsubscribe = 1;
|
||||
bridge_bind_address = 1;
|
||||
bridge_cafile = 1;
|
||||
bridge_capath = 1;
|
||||
bridge_certfile = 1;
|
||||
bridge_identity = 1;
|
||||
bridge_insecure = 1;
|
||||
bridge_keyfile = 1;
|
||||
bridge_max_packet_size = 1;
|
||||
bridge_outgoing_retain = 1;
|
||||
bridge_protocol_version = 1;
|
||||
bridge_psk = 1;
|
||||
bridge_require_ocsp = 1;
|
||||
bridge_tls_version = 1;
|
||||
cleansession = 1;
|
||||
idle_timeout = 1;
|
||||
keepalive_interval = 1;
|
||||
local_cleansession = 1;
|
||||
local_clientid = 1;
|
||||
local_password = 1;
|
||||
local_username = 1;
|
||||
notification_topic = 1;
|
||||
notifications = 1;
|
||||
notifications_local_only = 1;
|
||||
remote_clientid = 1;
|
||||
remote_password = 1;
|
||||
remote_username = 1;
|
||||
restart_timeout = 1;
|
||||
round_robin = 1;
|
||||
start_type = 1;
|
||||
threshold = 1;
|
||||
try_private = 1;
|
||||
};
|
||||
|
||||
bridgeOptions = with types; submodule {
|
||||
options = {
|
||||
addresses = mkOption {
|
||||
type = listOf (submodule {
|
||||
options = {
|
||||
address = mkOption {
|
||||
type = str;
|
||||
description = ''
|
||||
Address of the remote MQTT broker.
|
||||
'';
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = port;
|
||||
description = ''
|
||||
Port of the remote MQTT broker.
|
||||
'';
|
||||
default = 1883;
|
||||
};
|
||||
};
|
||||
});
|
||||
default = [];
|
||||
description = ''
|
||||
Remote endpoints for the bridge.
|
||||
'';
|
||||
};
|
||||
|
||||
topics = mkOption {
|
||||
type = listOf str;
|
||||
description = ''
|
||||
Topic patterns to be shared between the two brokers.
|
||||
Refer to the <link xlink:href="https://mosquitto.org/man/mosquitto-conf-5.html">
|
||||
mosquitto.conf documentation</link> for details on the format.
|
||||
'';
|
||||
default = [];
|
||||
example = [ "# both 2 local/topic/ remote/topic/" ];
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
type = submodule {
|
||||
freeformType = attrsOf optionType;
|
||||
};
|
||||
description = ''
|
||||
Additional settings for this bridge.
|
||||
'';
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
bridgeAsserts = prefix: bridge:
|
||||
assertKeysValid "${prefix}.settings" freeformBridgeKeys bridge.settings
|
||||
++ [ {
|
||||
assertion = length bridge.addresses > 0;
|
||||
message = "Bridge ${prefix} needs remote broker addresses";
|
||||
} ];
|
||||
|
||||
formatBridge = name: bridge:
|
||||
[
|
||||
"connection ${name}"
|
||||
"addresses ${concatMapStringsSep " " (a: "${a.address}:${toString a.port}") bridge.addresses}"
|
||||
]
|
||||
++ map (t: "topic ${t}") bridge.topics
|
||||
++ formatFreeform {} bridge.settings;
|
||||
|
||||
freeformGlobalKeys = {
|
||||
allow_duplicate_messages = 1;
|
||||
autosave_interval = 1;
|
||||
autosave_on_changes = 1;
|
||||
check_retain_source = 1;
|
||||
connection_messages = 1;
|
||||
log_facility = 1;
|
||||
log_timestamp = 1;
|
||||
log_timestamp_format = 1;
|
||||
max_inflight_bytes = 1;
|
||||
max_inflight_messages = 1;
|
||||
max_keepalive = 1;
|
||||
max_packet_size = 1;
|
||||
max_queued_bytes = 1;
|
||||
max_queued_messages = 1;
|
||||
memory_limit = 1;
|
||||
message_size_limit = 1;
|
||||
persistence_file = 1;
|
||||
persistence_location = 1;
|
||||
persistent_client_expiration = 1;
|
||||
pid_file = 1;
|
||||
queue_qos0_messages = 1;
|
||||
retain_available = 1;
|
||||
set_tcp_nodelay = 1;
|
||||
sys_interval = 1;
|
||||
upgrade_outgoing_qos = 1;
|
||||
websockets_headers_size = 1;
|
||||
websockets_log_level = 1;
|
||||
};
|
||||
|
||||
globalOptions = with types; {
|
||||
enable = mkEnableOption "the MQTT Mosquitto broker";
|
||||
|
||||
package = mkOption {
|
||||
type = package;
|
||||
default = pkgs.mosquitto;
|
||||
defaultText = literalExpression "pkgs.mosquitto";
|
||||
description = ''
|
||||
Mosquitto package to use.
|
||||
'';
|
||||
};
|
||||
|
||||
bridges = mkOption {
|
||||
type = attrsOf bridgeOptions;
|
||||
default = {};
|
||||
description = ''
|
||||
Bridges to build to other MQTT brokers.
|
||||
'';
|
||||
};
|
||||
|
||||
listeners = mkOption {
|
||||
type = listOf listenerOptions;
|
||||
default = {};
|
||||
description = ''
|
||||
Listeners to configure on this broker.
|
||||
'';
|
||||
};
|
||||
|
||||
includeDirs = mkOption {
|
||||
type = listOf path;
|
||||
description = ''
|
||||
Directories to be scanned for further config files to include.
|
||||
Directories will processed in the order given,
|
||||
<literal>*.conf</literal> files in the directory will be
|
||||
read in case-sensistive alphabetical order.
|
||||
'';
|
||||
default = [];
|
||||
};
|
||||
|
||||
logDest = mkOption {
|
||||
type = listOf (either path (enum [ "stdout" "stderr" "syslog" "topic" "dlt" ]));
|
||||
description = ''
|
||||
Destinations to send log messages to.
|
||||
'';
|
||||
default = [ "stderr" ];
|
||||
};
|
||||
|
||||
logType = mkOption {
|
||||
type = listOf (enum [ "debug" "error" "warning" "notice" "information"
|
||||
"subscribe" "unsubscribe" "websockets" "none" "all" ]);
|
||||
description = ''
|
||||
Types of messages to log.
|
||||
'';
|
||||
default = [];
|
||||
};
|
||||
|
||||
persistence = mkOption {
|
||||
type = bool;
|
||||
description = ''
|
||||
Enable persistent storage of subscriptions and messages.
|
||||
'';
|
||||
default = true;
|
||||
};
|
||||
|
||||
dataDir = mkOption {
|
||||
default = "/var/lib/mosquitto";
|
||||
type = types.path;
|
||||
description = ''
|
||||
The data directory.
|
||||
'';
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
type = submodule {
|
||||
freeformType = attrsOf optionType;
|
||||
};
|
||||
description = ''
|
||||
Global configuration options for the mosquitto broker.
|
||||
'';
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
|
||||
globalAsserts = prefix: cfg:
|
||||
flatten [
|
||||
(assertKeysValid "${prefix}.settings" freeformGlobalKeys cfg.settings)
|
||||
(imap0 (n: l: listenerAsserts "${prefix}.listener.${toString n}" l) cfg.listeners)
|
||||
(mapAttrsToList (n: b: bridgeAsserts "${prefix}.bridge.${n}" b) cfg.bridges)
|
||||
];
|
||||
|
||||
formatGlobal = cfg:
|
||||
[
|
||||
"per_listener_settings true"
|
||||
"persistence ${optionToString cfg.persistence}"
|
||||
]
|
||||
++ map
|
||||
(d: if path.check d then "log_dest file ${d}" else "log_dest ${d}")
|
||||
cfg.logDest
|
||||
++ map (t: "log_type ${t}") cfg.logType
|
||||
++ formatFreeform {} cfg.settings
|
||||
++ concatLists (imap0 formatListener cfg.listeners)
|
||||
++ concatLists (mapAttrsToList formatBridge cfg.bridges)
|
||||
++ map (d: "include_dir ${d}") cfg.includeDirs;
|
||||
|
||||
configFile = pkgs.writeText "mosquitto.conf"
|
||||
(concatStringsSep "\n" (formatGlobal cfg));
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
###### Interface
|
||||
|
||||
options.services.mosquitto = globalOptions;
|
||||
|
||||
###### Implementation
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
assertions = globalAsserts "services.mosquitto" cfg;
|
||||
|
||||
systemd.services.mosquitto = {
|
||||
description = "Mosquitto MQTT Broker Daemon";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
serviceConfig = {
|
||||
Type = "notify";
|
||||
NotifyAccess = "main";
|
||||
User = "mosquitto";
|
||||
Group = "mosquitto";
|
||||
RuntimeDirectory = "mosquitto";
|
||||
WorkingDirectory = cfg.dataDir;
|
||||
Restart = "on-failure";
|
||||
ExecStart = "${cfg.package}/bin/mosquitto -c ${configFile}";
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||
|
||||
# Hardening
|
||||
CapabilityBoundingSet = "";
|
||||
DevicePolicy = "closed";
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
NoNewPrivileges = true;
|
||||
PrivateDevices = true;
|
||||
PrivateTmp = true;
|
||||
PrivateUsers = true;
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectProc = "invisible";
|
||||
ProcSubset = "pid";
|
||||
ProtectSystem = "strict";
|
||||
ReadWritePaths = [
|
||||
cfg.dataDir
|
||||
"/tmp" # mosquitto_passwd creates files in /tmp before moving them
|
||||
] ++ filter path.check cfg.logDest;
|
||||
ReadOnlyPaths =
|
||||
map (p: "${p}")
|
||||
(cfg.includeDirs
|
||||
++ filter
|
||||
(v: v != null)
|
||||
(flatten [
|
||||
(map
|
||||
(l: [
|
||||
(l.settings.psk_file or null)
|
||||
(l.settings.http_dir or null)
|
||||
(l.settings.cafile or null)
|
||||
(l.settings.capath or null)
|
||||
(l.settings.certfile or null)
|
||||
(l.settings.crlfile or null)
|
||||
(l.settings.dhparamfile or null)
|
||||
(l.settings.keyfile or null)
|
||||
])
|
||||
cfg.listeners)
|
||||
(mapAttrsToList
|
||||
(_: b: [
|
||||
(b.settings.bridge_cafile or null)
|
||||
(b.settings.bridge_capath or null)
|
||||
(b.settings.bridge_certfile or null)
|
||||
(b.settings.bridge_keyfile or null)
|
||||
])
|
||||
cfg.bridges)
|
||||
]));
|
||||
RemoveIPC = true;
|
||||
RestrictAddressFamilies = [
|
||||
"AF_UNIX"
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
"AF_NETLINK"
|
||||
];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [
|
||||
"@system-service"
|
||||
"~@privileged"
|
||||
"~@resources"
|
||||
];
|
||||
UMask = "0077";
|
||||
};
|
||||
preStart =
|
||||
concatStringsSep
|
||||
"\n"
|
||||
(imap0
|
||||
(idx: listener: makePasswordFile listener.users "${cfg.dataDir}/passwd-${toString idx}")
|
||||
cfg.listeners);
|
||||
};
|
||||
|
||||
users.users.mosquitto = {
|
||||
description = "Mosquitto MQTT Broker Daemon owner";
|
||||
group = "mosquitto";
|
||||
uid = config.ids.uids.mosquitto;
|
||||
home = cfg.dataDir;
|
||||
createHome = true;
|
||||
};
|
||||
|
||||
users.groups.mosquitto.gid = config.ids.gids.mosquitto;
|
||||
|
||||
};
|
||||
|
||||
meta = {
|
||||
maintainers = with lib.maintainers; [ pennae ];
|
||||
# Don't edit the docbook xml directly, edit the md and generate it:
|
||||
# `pandoc mosquitto.md -t docbook --top-level-division=chapter --extract-media=media -f markdown+smart > mosquitto.xml`
|
||||
doc = ./mosquitto.xml;
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue