532 lines
20 KiB
Nix
532 lines
20 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
libDir = "/var/lib/burp";
|
|
clientCertDir = "${libDir}/CA-client";
|
|
cfg = config.services.burp;
|
|
|
|
clientConf = pkgs.writeText "burp.conf" ''
|
|
mode = client
|
|
pidfile = /run/burp/burp.pid
|
|
port = ${toString cfg.client.port}
|
|
status_port = ${toString cfg.client.statusPort}
|
|
server = ${cfg.client.server}
|
|
password = ${if (cfg.client.passwordFile != null) then "#PASSWORD#" else cfg.client.password}
|
|
cname = ${cfg.client.clientName}
|
|
ca_burp_ca = ${cfg.package}/bin/burp_ca
|
|
ca_csr_dir = ${clientCertDir}
|
|
ssl_cert_ca = ${libDir}/ssl_cert_ca.pem
|
|
ssl_cert = ${libDir}/ssl_cert-client.pem
|
|
ssl_key = ${libDir}/ssl_cert-client.key
|
|
ssl_key_password = ${if (cfg.client.sslKeyPasswordFile != null) then "#SSL_KEY_PASSWORD#" else cfg.client.sslKeyPassword}
|
|
ssl_peer_cn = burpserver
|
|
${concatMapStringsSep "\n" (x: "include = " + x) cfg.client.includes}
|
|
${concatMapStringsSep "\n" (x: "exclude = " + x) cfg.client.excludes}
|
|
nobackup = .nobackup
|
|
${optionalString (cfg.client.encryptionPasswordFile != null) ''
|
|
encryption_password = #ENCRYPTION_PASSWORD#
|
|
''}
|
|
${cfg.client.extraConfig}
|
|
'';
|
|
|
|
serverHome = "/var/lib/burp";
|
|
serverClientConfDir = serverHome + "/clientconfdir";
|
|
|
|
serverCaConf = pkgs.writeText "CA.cnf" ''
|
|
CA_DIR = ${serverHome}/CA
|
|
|
|
[ ca ]
|
|
dir = $ENV::CA_DIR
|
|
database = $dir/index.txt
|
|
serial = $dir/serial.txt
|
|
certs = $dir/certs
|
|
new_certs_dir = $dir/newcerts
|
|
crlnumber = $dir/crlnumber.txt
|
|
|
|
unique_subject = no
|
|
|
|
default_md = sha256
|
|
default_days = 7300
|
|
default_crl_days = 7300
|
|
|
|
#????
|
|
name_opt = ca_default
|
|
cert_opt = ca_default
|
|
|
|
x509_extensions = usr_cert
|
|
copy_extensions = copy
|
|
policy = policy_anything
|
|
|
|
[ usr_cert ]
|
|
basicConstraints = CA:FALSE
|
|
|
|
[ policy_anything ]
|
|
commonName = supplied
|
|
'';
|
|
|
|
serverConf = pkgs.writeText "burp-server.conf" ''
|
|
mode = server
|
|
pidfile = /run/burp/burp-server.pid
|
|
listen = ${cfg.server.listen}
|
|
max_children = 5;
|
|
listen_status = ${cfg.server.listenStatus}
|
|
max_status_children = 5;
|
|
|
|
user = burp
|
|
group = burp
|
|
umask = 0022
|
|
clientconfdir = ${serverClientConfDir}
|
|
directory = ${cfg.server.dataDirectory}
|
|
|
|
ca_conf = ${serverCaConf}
|
|
ca_name = burpCA
|
|
ca_server_name = burpserver
|
|
ca_burp_ca = ${cfg.package}/bin/burp_ca
|
|
ca_crl_check = 1
|
|
ssl_cert_ca = ${serverHome}/ssl_cert_ca.pem
|
|
ssl_cert = ${serverHome}/ssl_cert-server.pem
|
|
ssl_key = ${serverHome}/ssl_cert-server.key
|
|
ssl_dhfile = ${serverHome}/dhfile.pem
|
|
ssl_key_password = ${if (cfg.server.sslKeyPasswordFile != null) then "#SSL_KEY_PASSWORD#" else cfg.server.sslKeyPassword}
|
|
|
|
${concatMapStringsSep "\n" (x: "keep = " + toString x) cfg.server.keep}
|
|
timer_script = ${cfg.server.timerScript}
|
|
${concatMapStringsSep "\n" (x: "timer_arg = " + x) cfg.server.timerArgs}
|
|
|
|
dedup_group = global
|
|
working_dir_recovery_method = ${cfg.server.workingDirRecoveryMethod}
|
|
max_resume_attempts = ${toString cfg.server.maxResumeAttempts}
|
|
|
|
${concatMapStringsSep "\n" (x: "super_client = " + x) cfg.server.superClients}
|
|
${concatMapStringsSep "\n" (x: "restore_client = " + x) cfg.server.restoreClients}
|
|
|
|
${cfg.server.extraConfig}
|
|
'';
|
|
|
|
clientConfigs = lib.attrsets.mapAttrs (name: config: (pkgs.writeText name ''
|
|
password = ${if (config.passwordFile != null) then "#PASSWORD#" else config.password}
|
|
${config.extraConfig}
|
|
'')) cfg.server.clients;
|
|
|
|
in {
|
|
options = {
|
|
services.burp.package = mkOption {
|
|
type = types.package;
|
|
default = pkgs.burp;
|
|
description = ''
|
|
The package which is used as a burp server/client.
|
|
'';
|
|
};
|
|
|
|
services.burp.server = {
|
|
enable = mkEnableOption "Burp server";
|
|
|
|
listen = mkOption {
|
|
type = types.str;
|
|
default = ":::4971";
|
|
description = ''
|
|
Listen address used for backup communication.
|
|
'';
|
|
};
|
|
|
|
listenStatus = mkOption {
|
|
type = types.str;
|
|
default = ":::4972";
|
|
description = ''
|
|
Listen address used for querying backup and server status.
|
|
'';
|
|
};
|
|
|
|
dataDirectory = mkOption {
|
|
type = types.str;
|
|
default = "/var/spool/burp";
|
|
description = ''
|
|
Where the backup data is stored.
|
|
'';
|
|
};
|
|
|
|
sslKeyPassword = mkOption {
|
|
type = types.str;
|
|
default = "change-this-password";
|
|
description = ''
|
|
SSL key password for loading a certificate with encryption.
|
|
'';
|
|
};
|
|
sslKeyPasswordFile = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = mdDoc ''
|
|
File to load an SSL key password for loading a certificate with encryption from.
|
|
Takes preference over `sslKeyPassword`.
|
|
'';
|
|
};
|
|
|
|
keep = mkOption {
|
|
type = types.listOf types.int;
|
|
default = [ 7 ];
|
|
description = ''
|
|
How many backups to keep.
|
|
'';
|
|
};
|
|
|
|
timerScript = mkOption {
|
|
type = types.str;
|
|
default = "${cfg.package}/share/burp/scripts/timer_script";
|
|
description = ''
|
|
Timer script used to evaluate if it's backup time.
|
|
'';
|
|
};
|
|
|
|
timerArgs = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [
|
|
"20h"
|
|
"Mon,Tue,Wed,Thu,Fri,00,01,02,03,04,05,19,20,21,22,23"
|
|
"Sat,Sun,00,01,02,03,04,05,06,07,08,17,18,19,20,21,22,23"
|
|
];
|
|
description = ''
|
|
How often should backups be triggered.
|
|
'';
|
|
};
|
|
|
|
superClients = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
description = ''
|
|
Clients that are permitted to list, verify, restore, delete, and diff files belonging to any other client, according to the super_client's client_can permissions (eg, 'client_can_list'). If this is too permissive, you may set a super_client for individual original clients in the individual clientconfdir files, or look at the 'restoreClients' option.
|
|
'';
|
|
};
|
|
|
|
restoreClients = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
description = ''
|
|
Clients that are permitted to list, verify, restore, delete, and diff files belonging to any other client, according to the client_can permissions on both the restore_client and the original_client (eg, 'client_can_list'). If this is too permissive, you may set a restoreClient for individual original clients in the individual clientconfdir files.
|
|
'';
|
|
};
|
|
|
|
workingDirRecoveryMethod = mkOption {
|
|
type = types.str;
|
|
default = "delete";
|
|
description = ''
|
|
This option tells the server what to do when it finds the working directory of an interrupted backup (perhaps somebody pulled the plug on the server, or something). This can be overridden by the client configurations files in clientconfdir on the server. Options are...
|
|
|
|
delete: Just delete the old working directory.
|
|
|
|
resume: Continue the previous backup from the point at which it left off. NOTE: If the client has changed its include/exclude configuration since the backup was interrupted, the recovery method will automatically switch to 'delete'. See also the 'resume attempts' option.
|
|
'';
|
|
};
|
|
|
|
maxResumeAttempts = mkOption {
|
|
type = types.int;
|
|
default = 0;
|
|
description = ''
|
|
If workingDirRecoveryMethod is 'resume', this option tells the server how many times to attempt to resume before giving up and deleting the working directory. The default is 0, which means to never give up.
|
|
'';
|
|
};
|
|
|
|
clients = mkOption {
|
|
type = types.attrsOf (types.submodule ({ ... }: {
|
|
options = {
|
|
password = mkOption {
|
|
type = types.str;
|
|
default = "change-this-password";
|
|
description = ''
|
|
Password used by the client for first contact with the server.
|
|
'';
|
|
};
|
|
passwordFile = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = mdDoc ''
|
|
File to load a password for the first contact from client to server from.
|
|
Takes preference over `password`.
|
|
'';
|
|
};
|
|
|
|
extraConfig = mkOption {
|
|
type = types.lines;
|
|
default = "";
|
|
description = ''
|
|
Extra configuration for burp client. Contents will be added verbatim to the
|
|
configuration file.
|
|
'';
|
|
};
|
|
};
|
|
}));
|
|
};
|
|
|
|
extraConfig = mkOption {
|
|
type = types.lines;
|
|
default = "";
|
|
description = ''
|
|
Extra configuration for burp client. Contents will be added verbatim to the
|
|
configuration file.
|
|
'';
|
|
};
|
|
};
|
|
|
|
services.burp.client = {
|
|
enable = mkEnableOption "Burp client";
|
|
|
|
frequency = mkOption {
|
|
type = types.str;
|
|
default = "hourly";
|
|
description = ''
|
|
Controls how frequently the systemd timer is triggered and a backup
|
|
is attempted.
|
|
Backup frequency is still determined by the server, this
|
|
controls only how often the client tries.
|
|
'';
|
|
};
|
|
|
|
server = mkOption {
|
|
type = types.str;
|
|
default = "127.0.0.1";
|
|
description = ''
|
|
Address of burp server the client should contact.
|
|
'';
|
|
};
|
|
|
|
port = mkOption {
|
|
type = types.port;
|
|
default = 4971;
|
|
description = ''
|
|
Port used for backup communication.
|
|
'';
|
|
};
|
|
|
|
statusPort = mkOption {
|
|
type = types.port;
|
|
default = 4972;
|
|
description = ''
|
|
Port used for querying backup and server status.
|
|
'';
|
|
};
|
|
|
|
clientName = mkOption {
|
|
type = types.str;
|
|
default = "${config.networking.hostName}";
|
|
description = ''
|
|
Name the client should use to identify itself to the server.
|
|
'';
|
|
};
|
|
|
|
password = mkOption {
|
|
type = types.str;
|
|
default = "change-this-password";
|
|
description = ''
|
|
Password used by the client for first contact with the server.
|
|
'';
|
|
};
|
|
passwordFile = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = mdDoc ''
|
|
File to load a password for the first contact from client to server from.
|
|
Takes preference over `password`.
|
|
'';
|
|
};
|
|
|
|
sslKeyPassword = mkOption {
|
|
type = types.str;
|
|
default = "change-this-password";
|
|
description = ''
|
|
SSL key password for loading a certificate with encryption.
|
|
'';
|
|
};
|
|
sslKeyPasswordFile = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = mdDoc ''
|
|
File to load an SSL key password for loading a certificate with encryption from.
|
|
Takes preference over `sslKeyPassword`.
|
|
'';
|
|
};
|
|
|
|
includes = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [ "/etc" "/root" "/var" "/home" ];
|
|
description = ''
|
|
List of locations to include in backups.
|
|
'';
|
|
};
|
|
|
|
excludes = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [ libDir ];
|
|
description = ''
|
|
List of locations to exclude from the backup.
|
|
'';
|
|
};
|
|
|
|
encryptionPasswordFile = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = ''
|
|
File with a password for encrypted backups.
|
|
'';
|
|
};
|
|
|
|
extraConfig = mkOption {
|
|
type = types.lines;
|
|
default = "";
|
|
description = ''
|
|
Extra configuration for burp client. Contents will be added verbatim to the
|
|
configuration file.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
config = {
|
|
environment.systemPackages = mkIf cfg.client.enable [ cfg.package ];
|
|
|
|
systemd.timers.burp-client = mkIf cfg.client.enable {
|
|
description = "Timer for triggering Burp client and start a backup";
|
|
wantedBy = [ "timers.target" ];
|
|
timerConfig = {
|
|
OnCalendar = cfg.client.frequency;
|
|
Unit = "burp-client.service";
|
|
};
|
|
};
|
|
|
|
systemd.services.burp-client = mkIf cfg.client.enable {
|
|
description = "Burp client";
|
|
after = [ "network.target" ];
|
|
path = [ cfg.package pkgs.nettools pkgs.openssl ];
|
|
|
|
preStart = let
|
|
configFile = "${libDir}/burp.conf";
|
|
replaceSecret = "${pkgs.replace-secret}/bin/replace-secret";
|
|
in ''
|
|
prepare_config()
|
|
{
|
|
umask 027
|
|
install -Dm640 ${clientConf} '${configFile}'
|
|
|
|
${optionalString (cfg.client.passwordFile != null) ''
|
|
${replaceSecret} '#PASSWORD#' '${cfg.client.passwordFile}' '${configFile}'
|
|
''}
|
|
${optionalString (cfg.client.sslKeyPasswordFile != null) ''
|
|
${replaceSecret} '#SSL_KEY_PASSWORD#' '${cfg.client.sslKeyPasswordFile}' '${configFile}'
|
|
''}
|
|
${optionalString (cfg.client.encryptionPasswordFile != null) ''
|
|
${replaceSecret} '#ENCRYPTION_PASSWORD#' '${cfg.client.encryptionPasswordFile}' '${configFile}'
|
|
''}
|
|
}
|
|
|
|
if [ ! -d "${libDir}" ]; then
|
|
mkdir -m 0750 -p ${libDir}
|
|
mkdir -m 0700 -p ${clientCertDir}
|
|
prepare_config
|
|
${cfg.package}/bin/burp -c '${configFile}' -g
|
|
else
|
|
prepare_config
|
|
fi
|
|
'';
|
|
|
|
serviceConfig = {
|
|
Type = "simple";
|
|
ExecStart = "${cfg.package}/bin/burp -c ${libDir}/burp.conf -a t";
|
|
SuccessExitStatus = "3";
|
|
};
|
|
};
|
|
|
|
users = mkIf cfg.server.enable {
|
|
users.burp = {
|
|
group = "burp";
|
|
uid = 497;
|
|
home = "${serverHome}";
|
|
createHome = true;
|
|
description = "Burp Server user";
|
|
shell = "${pkgs.bash}/bin/bash";
|
|
isSystemUser = true;
|
|
};
|
|
groups.burp = {
|
|
gid = 497;
|
|
};
|
|
};
|
|
|
|
systemd.services.burp-server = mkIf cfg.server.enable {
|
|
description = "Burp Server";
|
|
after = [ "network.target" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
path = [ cfg.package pkgs.nettools pkgs.openssl ];
|
|
|
|
serviceConfig = let
|
|
configFile = "${serverHome}/burp-server.conf";
|
|
replaceSecret = "${pkgs.replace-secret}/bin/replace-secret";
|
|
preStartScript = pkgs.writeScript "burp-server-prestart" ''
|
|
#!/${pkgs.bash}/bin/bash
|
|
|
|
prepare_data_directory()
|
|
{
|
|
if ! [ -d "${cfg.server.dataDirectory}" ] ; then
|
|
mkdir -p "${cfg.server.dataDirectory}"
|
|
fi
|
|
chown burp:burp "${cfg.server.dataDirectory}" "${serverClientConfDir}"
|
|
chmod 700 "${cfg.server.dataDirectory}"
|
|
}
|
|
|
|
prepare_config()
|
|
{
|
|
install -Dm640 -o burp -g burp '${serverConf}' '${configFile}'
|
|
|
|
${optionalString (cfg.server.sslKeyPasswordFile != null) ''
|
|
${replaceSecret} '#SSL_KEY_PASSWORD#' '${cfg.server.sslKeyPasswordFile}' '${configFile}'
|
|
''}
|
|
}
|
|
|
|
prepare_client_configs()
|
|
{
|
|
umask 077
|
|
|
|
if ! [ -d "${serverClientConfDir}" ] ; then
|
|
mkdir -p "${serverClientConfDir}"
|
|
fi
|
|
|
|
rm -f "${serverClientConfDir}"/*
|
|
|
|
${concatStringsSep "\n" (mapAttrsToList(name: file: ''
|
|
install -Dm640 -o burp -g burp '${file}' '${serverClientConfDir}/${name}'
|
|
${optionalString (cfg.server.clients.${name}.passwordFile != null) ''
|
|
${replaceSecret} '#PASSWORD#' '${cfg.server.clients.${name}.passwordFile}' '${serverClientConfDir}/${name}'
|
|
''}
|
|
'') clientConfigs)}
|
|
}
|
|
|
|
prepare_data_directory
|
|
prepare_config
|
|
prepare_client_configs
|
|
'';
|
|
in {
|
|
ExecStart = "${cfg.package}/bin/burp -F -v -c ${configFile}";
|
|
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
|
|
|
User = "burp";
|
|
Group = "burp";
|
|
UMask = "0077";
|
|
RuntimeDirectory = "burp";
|
|
PrivateTmp = true;
|
|
PrivateDevices = true;
|
|
ProtectSystem = true;
|
|
ProtectHome = true;
|
|
NoNewPrivileges = true;
|
|
ReadWriteDirectories = [
|
|
cfg.server.dataDirectory
|
|
serverHome
|
|
];
|
|
|
|
ExecStartPre = "+${preStartScript}";
|
|
|
|
Nice = 19;
|
|
IOSchedulingClass = "idle";
|
|
CPUSchedulingPolicy = "idle";
|
|
};
|
|
};
|
|
};
|
|
}
|