Merge remote-tracking branch 'origin/master' into staging-next
Conflicts: pkgs/shells/ion/default.nix pkgs/tools/misc/cicero-tui/default.nix
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
{ config, lib, pkgs, ...}:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.duplicity;
|
||||
|
||||
stateDirectory = "/var/lib/duplicity";
|
||||
|
||||
localTarget = if hasPrefix "file://" cfg.targetUrl
|
||||
localTarget =
|
||||
if hasPrefix "file://" cfg.targetUrl
|
||||
then removePrefix "file://" cfg.targetUrl else null;
|
||||
|
||||
in {
|
||||
in
|
||||
{
|
||||
options.services.duplicity = {
|
||||
enable = mkEnableOption "backups with duplicity";
|
||||
|
||||
@@ -24,7 +25,7 @@ in {
|
||||
|
||||
include = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
default = [ ];
|
||||
example = [ "/home" ];
|
||||
description = ''
|
||||
List of paths to include into the backups. See the FILE SELECTION
|
||||
@@ -35,7 +36,7 @@ in {
|
||||
|
||||
exclude = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
default = [ ];
|
||||
description = ''
|
||||
List of paths to exclude from backups. See the FILE SELECTION section in
|
||||
<citerefentry><refentrytitle>duplicity</refentrytitle>
|
||||
@@ -82,14 +83,60 @@ in {
|
||||
|
||||
extraFlags = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
example = [ "--full-if-older-than" "1M" ];
|
||||
default = [ ];
|
||||
example = [ "--backend-retry-delay" "100" ];
|
||||
description = ''
|
||||
Extra command-line flags passed to duplicity. See
|
||||
<citerefentry><refentrytitle>duplicity</refentrytitle>
|
||||
<manvolnum>1</manvolnum></citerefentry>.
|
||||
'';
|
||||
};
|
||||
|
||||
fullIfOlderThan = mkOption {
|
||||
type = types.str;
|
||||
default = "never";
|
||||
example = "1M";
|
||||
description = ''
|
||||
If <literal>"never"</literal> (the default) always do incremental
|
||||
backups (the first backup will be a full backup, of course). If
|
||||
<literal>"always"</literal> always do full backups. Otherwise, this
|
||||
must be a string representing a duration. Full backups will be made
|
||||
when the latest full backup is older than this duration. If this is not
|
||||
the case, an incremental backup is performed.
|
||||
'';
|
||||
};
|
||||
|
||||
cleanup = {
|
||||
maxAge = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "6M";
|
||||
description = ''
|
||||
If non-null, delete all backup sets older than the given time. Old backup sets
|
||||
will not be deleted if backup sets newer than time depend on them.
|
||||
'';
|
||||
};
|
||||
maxFull = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
example = 2;
|
||||
description = ''
|
||||
If non-null, delete all backups sets that are older than the count:th last full
|
||||
backup (in other words, keep the last count full backups and
|
||||
associated incremental sets).
|
||||
'';
|
||||
};
|
||||
maxIncr = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
example = 1;
|
||||
description = ''
|
||||
If non-null, delete incremental sets of all backups sets that are
|
||||
older than the count:th last full backup (in other words, keep only
|
||||
old full backups and not their increments).
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
@@ -99,18 +146,26 @@ in {
|
||||
|
||||
environment.HOME = stateDirectory;
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = ''
|
||||
${pkgs.duplicity}/bin/duplicity ${escapeShellArgs (
|
||||
[
|
||||
cfg.root
|
||||
cfg.targetUrl
|
||||
"--archive-dir" stateDirectory
|
||||
]
|
||||
script =
|
||||
let
|
||||
target = escapeShellArg cfg.targetUrl;
|
||||
extra = escapeShellArgs ([ "--archive-dir" stateDirectory ] ++ cfg.extraFlags);
|
||||
dup = "${pkgs.duplicity}/bin/duplicity";
|
||||
in
|
||||
''
|
||||
set -x
|
||||
${dup} cleanup ${target} --force ${extra}
|
||||
${lib.optionalString (cfg.cleanup.maxAge != null) "${dup} remove-older-than ${lib.escapeShellArg cfg.cleanup.maxAge} ${target} --force ${extra}"}
|
||||
${lib.optionalString (cfg.cleanup.maxFull != null) "${dup} remove-all-but-n-full ${toString cfg.cleanup.maxFull} ${target} --force ${extra}"}
|
||||
${lib.optionalString (cfg.cleanup.maxIncr != null) "${dup} remove-all-incr-but-n-full ${toString cfg.cleanup.maxIncr} ${target} --force ${extra}"}
|
||||
exec ${dup} ${if cfg.fullIfOlderThan == "always" then "full" else "incr"} ${lib.escapeShellArgs (
|
||||
[ cfg.root cfg.targetUrl ]
|
||||
++ concatMap (p: [ "--include" p ]) cfg.include
|
||||
++ concatMap (p: [ "--exclude" p ]) cfg.exclude
|
||||
++ cfg.extraFlags)}
|
||||
++ (lib.optionals (cfg.fullIfOlderThan != "never" && cfg.fullIfOlderThan != "always") [ "--full-if-older-than" cfg.fullIfOlderThan ])
|
||||
)} ${extra}
|
||||
'';
|
||||
serviceConfig = {
|
||||
PrivateTmp = true;
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = "read-only";
|
||||
@@ -130,7 +185,7 @@ in {
|
||||
assertions = singleton {
|
||||
# Duplicity will fail if the last file selection option is an include. It
|
||||
# is not always possible to detect but this simple case can be caught.
|
||||
assertion = cfg.include != [] -> cfg.exclude != [] || cfg.extraFlags != [];
|
||||
assertion = cfg.include != [ ] -> cfg.exclude != [ ] || cfg.extraFlags != [ ];
|
||||
message = ''
|
||||
Duplicity will fail if you only specify included paths ("Because the
|
||||
default is to include all files, the expression is redundant. Exiting
|
||||
|
||||
@@ -111,6 +111,13 @@ in
|
||||
default = false;
|
||||
description = "Disables automatic Universal Plug and Play.";
|
||||
};
|
||||
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Wheter to open ports in the firewall";
|
||||
};
|
||||
|
||||
dataDir = mkOption {
|
||||
type = types.str;
|
||||
default = "/var/lib/terraria";
|
||||
@@ -151,5 +158,11 @@ in
|
||||
${pkgs.coreutils}/bin/chgrp terraria ${cfg.dataDir}/terraria.sock
|
||||
'';
|
||||
};
|
||||
|
||||
networking.firewall = mkIf cfg.openFirewall {
|
||||
allowedTCPPorts = [ cfg.port ];
|
||||
allowedUDPPorts = [ cfg.port ];
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
attrValues
|
||||
concatMap
|
||||
concatStringsSep
|
||||
escapeShellArg
|
||||
literalExample
|
||||
mapAttrs'
|
||||
mkDefault
|
||||
mkEnableOption
|
||||
mkIf
|
||||
mkOption
|
||||
nameValuePair
|
||||
optional
|
||||
types
|
||||
;
|
||||
|
||||
mainCfg = config.services.ghostunnel;
|
||||
|
||||
module = { config, name, ... }:
|
||||
{
|
||||
options = {
|
||||
|
||||
listen = mkOption {
|
||||
description = ''
|
||||
Address and port to listen on (can be HOST:PORT, unix:PATH).
|
||||
'';
|
||||
type = types.str;
|
||||
};
|
||||
|
||||
target = mkOption {
|
||||
description = ''
|
||||
Address to forward connections to (can be HOST:PORT or unix:PATH).
|
||||
'';
|
||||
type = types.str;
|
||||
};
|
||||
|
||||
keystore = mkOption {
|
||||
description = ''
|
||||
Path to keystore (combined PEM with cert/key, or PKCS12 keystore).
|
||||
|
||||
NB: storepass is not supported because it would expose credentials via <code>/proc/*/cmdline</code>.
|
||||
|
||||
Specify this or <code>cert</code> and <code>key</code>.
|
||||
'';
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
|
||||
cert = mkOption {
|
||||
description = ''
|
||||
Path to certificate (PEM with certificate chain).
|
||||
|
||||
Not required if <code>keystore</code> is set.
|
||||
'';
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
|
||||
key = mkOption {
|
||||
description = ''
|
||||
Path to certificate private key (PEM with private key).
|
||||
|
||||
Not required if <code>keystore</code> is set.
|
||||
'';
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
|
||||
cacert = mkOption {
|
||||
description = ''
|
||||
Path to CA bundle file (PEM/X509). Uses system trust store if <code>null</code>.
|
||||
'';
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
|
||||
disableAuthentication = mkOption {
|
||||
description = ''
|
||||
Disable client authentication, no client certificate will be required.
|
||||
'';
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
|
||||
allowAll = mkOption {
|
||||
description = ''
|
||||
If true, allow all clients, do not check client cert subject.
|
||||
'';
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
|
||||
allowCN = mkOption {
|
||||
description = ''
|
||||
Allow client if common name appears in the list.
|
||||
'';
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
};
|
||||
|
||||
allowOU = mkOption {
|
||||
description = ''
|
||||
Allow client if organizational unit name appears in the list.
|
||||
'';
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
};
|
||||
|
||||
allowDNS = mkOption {
|
||||
description = ''
|
||||
Allow client if DNS subject alternative name appears in the list.
|
||||
'';
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
};
|
||||
|
||||
allowURI = mkOption {
|
||||
description = ''
|
||||
Allow client if URI subject alternative name appears in the list.
|
||||
'';
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
};
|
||||
|
||||
extraArguments = mkOption {
|
||||
description = "Extra arguments to pass to <code>ghostunnel server</code>";
|
||||
type = types.separatedString " ";
|
||||
default = "";
|
||||
};
|
||||
|
||||
unsafeTarget = mkOption {
|
||||
description = ''
|
||||
If set, does not limit target to localhost, 127.0.0.1, [::1], or UNIX sockets.
|
||||
|
||||
This is meant to protect against accidental unencrypted traffic on
|
||||
untrusted networks.
|
||||
'';
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
|
||||
# Definitions to apply at the root of the NixOS configuration.
|
||||
atRoot = mkOption {
|
||||
internal = true;
|
||||
};
|
||||
};
|
||||
|
||||
# Clients should not be authenticated with the public root certificates
|
||||
# (afaict, it doesn't make sense), so we only provide that default when
|
||||
# client cert auth is disabled.
|
||||
config.cacert = mkIf config.disableAuthentication (mkDefault null);
|
||||
|
||||
config.atRoot = {
|
||||
assertions = [
|
||||
{ message = ''
|
||||
services.ghostunnel.servers.${name}: At least one access control flag is required.
|
||||
Set at least one of:
|
||||
- services.ghostunnel.servers.${name}.disableAuthentication
|
||||
- services.ghostunnel.servers.${name}.allowAll
|
||||
- services.ghostunnel.servers.${name}.allowCN
|
||||
- services.ghostunnel.servers.${name}.allowOU
|
||||
- services.ghostunnel.servers.${name}.allowDNS
|
||||
- services.ghostunnel.servers.${name}.allowURI
|
||||
'';
|
||||
assertion = config.disableAuthentication
|
||||
|| config.allowAll
|
||||
|| config.allowCN != []
|
||||
|| config.allowOU != []
|
||||
|| config.allowDNS != []
|
||||
|| config.allowURI != []
|
||||
;
|
||||
}
|
||||
];
|
||||
|
||||
systemd.services."ghostunnel-server-${name}" = {
|
||||
after = [ "network.target" ];
|
||||
wants = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Restart = "always";
|
||||
AmbientCapabilities = ["CAP_NET_BIND_SERVICE"];
|
||||
DynamicUser = true;
|
||||
LoadCredential = optional (config.keystore != null) "keystore:${config.keystore}"
|
||||
++ optional (config.cert != null) "cert:${config.cert}"
|
||||
++ optional (config.key != null) "key:${config.key}"
|
||||
++ optional (config.cacert != null) "cacert:${config.cacert}";
|
||||
};
|
||||
script = concatStringsSep " " (
|
||||
[ "${mainCfg.package}/bin/ghostunnel" ]
|
||||
++ optional (config.keystore != null) "--keystore=$CREDENTIALS_DIRECTORY/keystore"
|
||||
++ optional (config.cert != null) "--cert=$CREDENTIALS_DIRECTORY/cert"
|
||||
++ optional (config.key != null) "--key=$CREDENTIALS_DIRECTORY/key"
|
||||
++ optional (config.cacert != null) "--cacert=$CREDENTIALS_DIRECTORY/cacert"
|
||||
++ [
|
||||
"server"
|
||||
"--listen ${config.listen}"
|
||||
"--target ${config.target}"
|
||||
] ++ optional config.allowAll "--allow-all"
|
||||
++ map (v: "--allow-cn=${escapeShellArg v}") config.allowCN
|
||||
++ map (v: "--allow-ou=${escapeShellArg v}") config.allowOU
|
||||
++ map (v: "--allow-dns=${escapeShellArg v}") config.allowDNS
|
||||
++ map (v: "--allow-uri=${escapeShellArg v}") config.allowURI
|
||||
++ optional config.disableAuthentication "--disable-authentication"
|
||||
++ optional config.unsafeTarget "--unsafe-target"
|
||||
++ [ config.extraArguments ]
|
||||
);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
|
||||
options = {
|
||||
services.ghostunnel.enable = mkEnableOption "ghostunnel";
|
||||
|
||||
services.ghostunnel.package = mkOption {
|
||||
description = "The ghostunnel package to use.";
|
||||
type = types.package;
|
||||
default = pkgs.ghostunnel;
|
||||
defaultText = literalExample ''pkgs.ghostunnel'';
|
||||
};
|
||||
|
||||
services.ghostunnel.servers = mkOption {
|
||||
description = ''
|
||||
Server mode ghostunnels (TLS listener -> plain TCP/UNIX target)
|
||||
'';
|
||||
type = types.attrsOf (types.submodule module);
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf mainCfg.enable {
|
||||
assertions = lib.mkMerge (map (v: v.atRoot.assertions) (attrValues mainCfg.servers));
|
||||
systemd = lib.mkMerge (map (v: v.atRoot.systemd) (attrValues mainCfg.servers));
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [
|
||||
roberth
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user