0
0
Fork 0
mirror of https://github.com/NixOS/nixpkgs.git synced 2025-07-13 13:40:28 +03:00

Merge remote-tracking branch 'origin/master' into staging-next

This commit is contained in:
K900 2025-04-07 21:21:10 +03:00
commit b3146d4446
8183 changed files with 9734 additions and 8649 deletions

View file

@ -1,8 +1,8 @@
{
x86_64-linux = "/nix/store/00a7rdfwhm6avqkgj68grddbzyz3h6ql-nix-2.24.13";
i686-linux = "/nix/store/s6c620v60hfishzi1lbfpryk65lbvg8g-nix-2.24.13";
aarch64-linux = "/nix/store/7yg9is1shh3383iwi6qynz3vh91l1f9d-nix-2.24.13";
riscv64-linux = "/nix/store/fagjkrx5r6p52xp8qb5581bmnlgp01sn-nix-riscv64-unknown-linux-gnu-2.24.13";
x86_64-darwin = "/nix/store/ifby7rrgkkly5pzjnyac90lzvrak3i9y-nix-2.24.13";
aarch64-darwin = "/nix/store/b0rbdp6ba2fprprpgsw1a8pplzg0j324-nix-2.24.13";
x86_64-linux = "/nix/store/kvqnqgjw3k0xmv3ypzajz3c5wf1pxnbs-nix-2.24.14";
i686-linux = "/nix/store/292xy9z1vjmy0888bzadmj9fmq1ccapv-nix-2.24.14";
aarch64-linux = "/nix/store/qsy62z6rk31s8s937nvkcdhn0ds62yax-nix-2.24.14";
riscv64-linux = "/nix/store/zvrzwzv534zcmhw4ai1hbc4iz229hk3p-nix-riscv64-unknown-linux-gnu-2.24.14";
x86_64-darwin = "/nix/store/jc7x6906wyy6csgf6br1gbwkw56nxm4l-nix-2.24.14";
aarch64-darwin = "/nix/store/sfrmn30fijs6qpfi7ckjkv2vdr4z590h-nix-2.24.14";
}

View file

@ -361,6 +361,7 @@
./programs/zsh/zsh.nix
./rename.nix
./security/acme
./security/agnos.nix
./security/apparmor.nix
./security/audit.nix
./security/auditd.nix

View file

@ -10,16 +10,24 @@ in
{
options.programs.amnezia-vpn = {
enable = lib.mkEnableOption "The AmneziaVPN client";
package = lib.mkPackageOption pkgs "amnezia-vpn" { };
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ pkgs.amnezia-vpn ];
services.dbus.packages = [ pkgs.amnezia-vpn ];
environment.systemPackages = [ cfg.package ];
services.dbus.packages = [ cfg.package ];
services.resolved.enable = true;
systemd = {
packages = [ pkgs.amnezia-vpn ];
services."AmneziaVPN".wantedBy = [ "multi-user.target" ];
packages = [ cfg.package ];
services."AmneziaVPN" = {
wantedBy = [ "multi-user.target" ];
path = with pkgs; [
procps
iproute2
sudo
];
};
};
};

View file

@ -0,0 +1,314 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.security.agnos;
format = pkgs.formats.toml { };
name = "agnos";
stateDir = "/var/lib/${name}";
accountType =
let
inherit (lib) types mkOption;
in
types.submodule {
freeformType = format.type;
options = {
email = mkOption {
type = types.str;
description = ''
Email associated with this account.
'';
};
private_key_path = mkOption {
type = types.str;
description = ''
Path of the PEM-encoded private key for this account.
Currently, only RSA keys are supported.
If this path does not exist, then the behavior depends on `generateKeys.enable`.
When this option is `true`,
the key will be automatically generated and saved to this path.
When it is `false`, agnos will fail.
If a relative path is specified,
the key will be looked up (or generated and saved to) under `${stateDir}`.
'';
};
certificates = mkOption {
type = types.listOf certificateType;
description = ''
Certificates for agnos to issue or renew.
'';
};
};
};
certificateType =
let
inherit (lib) types literalExpression mkOption;
in
types.submodule {
freeformType = format.type;
options = {
domains = mkOption {
type = types.listOf types.str;
description = ''
Domains the certificate represents
'';
example = literalExpression ''["a.example.com", "b.example.com", "*b.example.com"]'';
};
fullchain_output_file = mkOption {
type = types.str;
description = ''
Output path for the full chain including the acquired certificate.
If a relative path is specified, the file will be created in `${stateDir}`.
'';
};
key_output_file = mkOption {
type = types.str;
description = ''
Output path for the certificate private key.
If a relative path is specified, the file will be created in `${stateDir}`.
'';
};
};
};
in
{
options.security.agnos =
let
inherit (lib) types mkEnableOption mkOption;
in
{
enable = mkEnableOption name;
settings = mkOption {
description = "Settings";
type = types.submodule {
freeformType = format.type;
options = {
dns_listen_addr = mkOption {
type = types.str;
default = "0.0.0.0:53";
description = ''
Address for agnos to listen on.
Note that this needs to be reachable by the outside world,
and 53 is required in most situations
since `NS` records do not allow specifying the port.
'';
};
accounts = mkOption {
type = types.listOf accountType;
description = ''
A list of ACME accounts.
Each account is associated with an email address
and can be used to obtain an arbitrary amount of certificate
(subject to provider's rate limits,
see e.g. [Let's Encrypt Rate Limits](https://letsencrypt.org/docs/rate-limits/)).
'';
};
};
};
};
generateKeys = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Enable automatic generation of account keys.
When this is `true`, a key will be generated for each account where
the file referred to by the `private_key` path does not exist yet.
Currently, only RSA keys can be generated.
'';
};
keySize = mkOption {
type = types.int;
default = 4096;
description = ''
Key size in bits to use when generating new keys.
'';
};
};
server = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
ACME Directory Resource URI. Defaults to Let's Encrypt's production endpoint,
`https://acme-v02.api.letsencrypt.org/directory`, if unset.
'';
};
serverCa = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
The root certificate (in PEM format) of the ACME server's HTTPS interface.
'';
};
persistent = mkOption {
type = types.bool;
default = true;
description = ''
When `true`, use a persistent systemd timer.
'';
};
startAt = mkOption {
type = types.either types.str (types.listOf types.str);
default = "daily";
example = "02:00";
description = ''
How often or when to run agnos.
The format is described in
{manpage}`systemd.time(7)`.
'';
};
temporarilyOpenFirewall = mkOption {
type = types.bool;
default = false;
description = ''
When `true`, will open the port specified in `settings.dns_listen_addr`
before running the agnos service, and close it when agnos finishes running.
'';
};
group = mkOption {
type = types.str;
default = name;
description = ''
Group to run Agnos as. The acquired certificates will be owned by this group.
'';
};
user = mkOption {
type = types.str;
default = name;
description = ''
User to run Agnos as. The acquired certificates will be owned by this user.
'';
};
};
config =
let
configFile = format.generate "agnos.toml" cfg.settings;
port = lib.toInt (lib.last (builtins.split ":" cfg.settings.dns_listen_addr));
useNftables = config.networking.nftables.enable;
# nftables implementation for temporarilyOpenFirewall
nftablesSetup = pkgs.writeShellScript "agnos-fw-setup" ''
${lib.getExe pkgs.nftables} add element inet nixos-fw temp-ports "{ tcp . ${toString port} }"
${lib.getExe pkgs.nftables} add element inet nixos-fw temp-ports "{ udp . ${toString port} }"
'';
nftablesTeardown = pkgs.writeShellScript "agnos-fw-teardown" ''
${lib.getExe pkgs.nftables} delete element inet nixos-fw temp-ports "{ tcp . ${toString port} }"
${lib.getExe pkgs.nftables} delete element inet nixos-fw temp-ports "{ udp . ${toString port} }"
'';
# iptables implementation for temporarilyOpenFirewall
helpers = ''
function ip46tables() {
${lib.getExe' pkgs.iptables "iptables"} -w "$@"
${lib.getExe' pkgs.iptables "ip6tables"} -w "$@"
}
'';
fwFilter = ''--dport ${toString port} -j ACCEPT -m comment --comment "agnos"'';
iptablesSetup = pkgs.writeShellScript "agnos-fw-setup" ''
${helpers}
ip46tables -I INPUT 1 -p tcp ${fwFilter}
ip46tables -I INPUT 1 -p udp ${fwFilter}
'';
iptablesTeardown = pkgs.writeShellScript "agnos-fw-setup" ''
${helpers}
ip46tables -D INPUT -p tcp ${fwFilter}
ip46tables -D INPUT -p udp ${fwFilter}
'';
in
lib.mkIf cfg.enable {
assertions = [
{
assertion = !cfg.temporarilyOpenFirewall || config.networking.firewall.enable;
message = "temporarilyOpenFirewall is only useful when firewall is enabled";
}
];
systemd.services.agnos = {
serviceConfig = {
ExecStartPre =
lib.optional cfg.generateKeys.enable ''
${pkgs.agnos}/bin/agnos-generate-accounts-keys \
--no-confirm \
--key-size ${toString cfg.generateKeys.keySize} \
${configFile}
''
++ lib.optional cfg.temporarilyOpenFirewall (
"+" + (if useNftables then nftablesSetup else iptablesSetup)
);
ExecStopPost = lib.optional cfg.temporarilyOpenFirewall (
"+" + (if useNftables then nftablesTeardown else iptablesTeardown)
);
ExecStart = ''
${pkgs.agnos}/bin/agnos \
${if cfg.server != null then "--acme-url=${cfg.server}" else "--no-staging"} \
${lib.optionalString (cfg.serverCa != null) "--acme-serv-ca=${cfg.serverCa}"} \
${configFile}
'';
Type = "oneshot";
User = cfg.user;
Group = cfg.group;
StateDirectory = name;
StateDirectoryMode = "0750";
WorkingDirectory = "${stateDir}";
# Allow binding privileged ports if necessary
CapabilityBoundingSet = lib.mkIf (port < 1024) [ "CAP_NET_BIND_SERVICE" ];
AmbientCapabilities = lib.mkIf (port < 1024) [ "CAP_NET_BIND_SERVICE" ];
};
after = [
"firewall.target"
"network-online.target"
"nftables.service"
];
wants = [ "network-online.target" ];
};
systemd.timers.agnos = {
timerConfig = {
OnCalendar = cfg.startAt;
Persistent = cfg.persistent;
Unit = "agnos.service";
};
wantedBy = [ "timers.target" ];
};
users.groups = lib.mkIf (cfg.group == name) {
${cfg.group} = { };
};
users.users = lib.mkIf (cfg.user == name) {
${cfg.user} = {
isSystemUser = true;
description = "Agnos service user";
group = cfg.group;
};
};
};
}

View file

@ -147,34 +147,34 @@ let
else
throw "Invalid database driver: ${cfg.database.driver}";
mattermostPluginDerivations =
with pkgs;
map (
plugin:
stdenv.mkDerivation {
name = "mattermost-plugin";
installPhase = ''
mkdir -p $out/share
cp ${plugin} $out/share/plugin.tar.gz
'';
dontUnpack = true;
dontPatch = true;
dontConfigure = true;
dontBuild = true;
preferLocalBuild = true;
}
) cfg.plugins;
mattermostPluginDerivations = map (
plugin:
pkgs.stdenvNoCC.mkDerivation {
name = "${cfg.package.name}-plugin";
installPhase = ''
runHook preInstall
mkdir -p $out/share
ln -sf ${plugin} $out/share/plugin.tar.gz
runHook postInstall
'';
dontUnpack = true;
dontPatch = true;
dontConfigure = true;
dontBuild = true;
preferLocalBuild = true;
}
) cfg.plugins;
mattermostPlugins =
with pkgs;
if mattermostPluginDerivations == [ ] then
null
else
stdenv.mkDerivation {
pkgs.stdenvNoCC.mkDerivation {
name = "${cfg.package.name}-plugins";
nativeBuildInputs = [ autoPatchelfHook ] ++ mattermostPluginDerivations;
nativeBuildInputs = [ pkgs.autoPatchelfHook ] ++ mattermostPluginDerivations;
buildInputs = [ cfg.package ];
installPhase = ''
runHook preInstall
mkdir -p $out
plugins=(${
escapeShellArgs (map (plugin: "${plugin}/share/plugin.tar.gz") mattermostPluginDerivations)
@ -187,6 +187,7 @@ let
GZIP_OPT=-9 tar -C "$hash" -cvzf "$out/$hash.tar.gz" .
rm -rf "$hash"
done
runHook postInstall
'';
dontUnpack = true;
@ -254,8 +255,8 @@ let
}
);
mattermostConfJSON = pkgs.writeText "mattermost-config.json" (builtins.toJSON mattermostConf);
format = pkgs.formats.json { };
finalConfig = format.generate "mattermost-config.json" mattermostConf;
in
{
imports = [
@ -454,9 +455,9 @@ in
the options specified in services.mattermost will be generated
but won't be overwritten on changes or rebuilds.
If this option is disabled, changes in the system console won't
be possible (default). If an config.json is present, it will be
overwritten!
If this option is disabled, persistent changes in the system
console won't be possible (the default). If a config.json is
present, it will be overwritten at service start!
'';
};
@ -480,7 +481,20 @@ in
description = ''
Plugins to add to the configuration. Overrides any installed if non-null.
This is a list of paths to .tar.gz files or derivations evaluating to
.tar.gz files.
.tar.gz files. You can use `mattermost.buildPlugin` to build plugins;
see the NixOS documentation for more details.
'';
};
pluginsBundle = mkOption {
type = with types; nullOr package;
default = mattermostPlugins;
defaultText = ''
All entries in {config}`services.mattermost.plugins`, repacked
'';
description = ''
Derivation building to a directory of plugin tarballs.
This overrides {option}`services.mattermost.plugins` if provided.
'';
};
@ -508,7 +522,8 @@ in
type = with types; attrsOf (either int str);
default = { };
description = ''
Extra environment variables to export to the Mattermost process, in the systemd unit.
Extra environment variables to export to the Mattermost process
from the systemd unit configuration.
'';
example = {
MM_SERVICESETTINGS_SITEURL = "http://example.com";
@ -524,11 +539,11 @@ in
for mattermost (see [the Mattermost documentation](https://docs.mattermost.com/configure/configuration-settings.html#environment-variables)).
Settings defined in the environment file will overwrite settings
set via nix or via the {option}`services.mattermost.extraConfig`
set via Nix or via the {option}`services.mattermost.extraConfig`
option.
Useful for setting config options without their value ending up in the
(world-readable) nix store, e.g. for a database password.
(world-readable) Nix store, e.g. for a database password.
'';
};
@ -639,13 +654,13 @@ in
if cfg.database.driver == "postgres" then
{
sslmode = "disable";
connect_timeout = 30;
connect_timeout = 60;
}
else if cfg.database.driver == "mysql" then
{
charset = "utf8mb4,utf8";
writeTimeout = "30s";
readTimeout = "30s";
writeTimeout = "60s";
readTimeout = "60s";
}
else
throw "Invalid database driver ${cfg.database.driver}";
@ -653,13 +668,13 @@ in
if config.mattermost.database.driver == "postgres" then
{
sslmode = "disable";
connect_timeout = 30;
connect_timeout = 60;
}
else if config.mattermost.database.driver == "mysql" then
{
charset = "utf8mb4,utf8";
writeTimeout = "30s";
readTimeout = "30s";
writeTimeout = "60s";
readTimeout = "60s";
}
else
throw "Invalid database driver";
@ -687,7 +702,7 @@ in
};
settings = mkOption {
type = types.attrs;
inherit (format) type;
default = { };
description = ''
Additional configuration options as Nix attribute set in config.json schema.
@ -786,7 +801,7 @@ in
"d= ${tempDir} 0750 ${cfg.user} ${cfg.group} - -"
# Ensure that pluginDir is a directory, as it could be a symlink on prior versions.
"r- ${pluginDir} - - - - -"
# Don't remove or clean it out since it should be persistent, as this is where plugins are unpacked.
"d= ${pluginDir} 0750 ${cfg.user} ${cfg.group} - -"
# Ensure that the plugin directories exist.
@ -801,15 +816,14 @@ in
"L+ ${cfg.dataDir}/client - - - - ${cfg.package}/client"
]
++ (
if mattermostPlugins == null then
# Create the plugin tarball directory if it's a symlink.
if cfg.pluginsBundle == null then
# Create the plugin tarball directory to allow plugin uploads.
[
"r- ${cfg.dataDir}/plugins - - - - -"
"d= ${cfg.dataDir}/plugins 0750 ${cfg.user} ${cfg.group} - -"
]
else
# Symlink the plugin tarball directory, removing anything existing.
[ "L+ ${cfg.dataDir}/plugins - - - - ${mattermostPlugins}" ]
# Symlink the plugin tarball directory, removing anything existing, since it's managed by Nix.
[ "L+ ${cfg.dataDir}/plugins - - - - ${cfg.pluginsBundle}" ]
);
systemd.services.mattermost = rec {
@ -836,7 +850,7 @@ in
configDir=${escapeShellArg cfg.configDir}
logDir=${escapeShellArg cfg.logDir}
package=${escapeShellArg cfg.package}
nixConfig=${escapeShellArg mattermostConfJSON}
nixConfig=${escapeShellArg finalConfig}
''
+ optionalString (versionAtLeast config.system.stateVersion "25.05") ''
# Migrate configs in the pre-25.05 directory structure.