Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
27838c2
Added an environment directory for sourcing secrets
adrianparvino Jul 15, 2018
08f9a80
`source` only sources the first argument
adrianparvino Jul 15, 2018
7e30b08
Remove SSH and Nixops support
adrianparvino Jul 15, 2018
32ea179
Do not list envDir's children
adrianparvino Jul 15, 2018
53be763
Fixed pgpKeyFile typo (gcfg.gpgKeyFile)
adrianparvino Jul 15, 2018
e6fd55e
Change default envDir location
adrianparvino Jul 15, 2018
49405a0
Change from types.path to types.string to avoid security problems
adrianparvino Jul 15, 2018
49cd324
Remove passphraseFile option
adrianparvino Jul 15, 2018
26f7532
Remove use of passphraseFile in the systemd service
adrianparvino Jul 15, 2018
834f9e9
Add pgpDir and split envDir to a rootDir
adrianparvino Jul 15, 2018
1e4df9d
rootDir should be referenced as gcfg.rootDir
adrianparvino Jul 15, 2018
a13d55d
Remove ssh usage in systemd service
adrianparvino Jul 15, 2018
ba828c5
Import and ultimately trust public key
adrianparvino Jul 15, 2018
586b60c
Add duplicity to the environment
adrianparvino Jul 15, 2018
8d2acc7
Remove some directory settings
adrianparvino Jul 15, 2018
75475a4
Add restore scripts
adrianparvino Jul 15, 2018
b28cc6b
Removed public key import
adrianparvino Jul 15, 2018
8c898cd
Added key generation script
adrianparvino Jul 15, 2018
50d9dd7
Exit if directories exist
adrianparvino Jul 15, 2018
f2a1e44
Prettify AWS credentials input
adrianparvino Jul 15, 2018
6e45f47
Use expect to automate GPG key generation
adrianparvino Jul 15, 2018
c8e60d8
Remove the keyId option
adrianparvino Jul 15, 2018
e94683c
Add a simple guide to using the module.
adrianparvino Jul 15, 2018
5842768
Add warning w.r.t. strings vs paths
adrianparvino Jul 15, 2018
2f13640
Add note about the generated directories
adrianparvino Jul 15, 2018
3bf4cc5
Added empty section
adrianparvino Jul 16, 2018
be82392
Clarify the use of the generated directories
adrianparvino Jul 16, 2018
94c0028
Implement multiple directory backup and partially password
adrianparvino Jul 23, 2018
7016af0
Finish passphrase implementation
adrianparvino Jul 23, 2018
5978933
Use baseNameOf as a postfix
adrianparvino Jul 23, 2018
1935947
Add documentation for using passphrase instead of GPG private key
adrianparvino Jul 28, 2018
e681669
Add ~services.duplicity-backup.usePasshprase~ option to the docs.
adrianparvino Jul 28, 2018
6971888
Changed warning to say that ~directories~ may accept multiple paths
adrianparvino Jul 28, 2018
8a9369d
Add the warning on the nix module declaration
adrianparvino Jul 29, 2018
8ea0aaf
Warning should map over all archives
adrianparvino Jul 31, 2018
8214674
Use name/directory instead of name-directory for buckets
adrianparvino Jul 31, 2018
e32ebe0
Added recommended system backup directories
adrianparvino Jul 31, 2018
4d7eebe
Start draft for a LiveCD backup script
adrianparvino Dec 17, 2018
87a623a
Create a more appropriate title for LiveCDBackup
adrianparvino Dec 18, 2018
600b427
Document the challenges of a NixOS LiveCD; Move links to footnotes
adrianparvino Dec 18, 2018
c2107f6
Add documentation and challenges of using nix-duplicity-backup
adrianparvino Dec 18, 2018
9160bf7
Remove duplicity subsection under background
adrianparvino Dec 19, 2018
1c737cd
nix-duplicity-backup is currently unsuitable for system backups
adrianparvino Dec 19, 2018
4109b13
Add subsections under proposed solution as steps
adrianparvino Dec 19, 2018
ab05d26
Add a solution and an alternative for FDE on the LiveCD
adrianparvino Dec 19, 2018
a73d860
Add details for the proposed nix-duplicity-backup-system
adrianparvino Dec 19, 2018
b0bb4eb
Fix wpa_supplicant module -> postgresql module typo
adrianparvino Dec 21, 2018
d4a7fda
Add subsection regarding separation of restoration and backup module
adrianparvino Dec 21, 2018
a5151d0
Scripts for the creation of an encrypted Live CD
adrianparvino Dec 24, 2018
601ff13
Create general structure of the solution proper
adrianparvino Jan 3, 2019
47f3d5d
Create the final draft of solution proper
adrianparvino Jan 3, 2019
0e633d8
Reorder partition sturcture and duplicity GPG keys
adrianparvino Jan 3, 2019
96bfd8a
Factor out options to a separate file
adrianparvino Jan 5, 2019
b0c9e7d
Factor backup implementation into its own file
adrianparvino Jan 5, 2019
c30febe
Factor out genScripts and rename from options to common
adrianparvino Jan 5, 2019
020b4aa
Factor out restoration into its own file
adrianparvino Jan 5, 2019
46b4dc7
Move remaining configuration to -common.nix
adrianparvino Jan 5, 2019
2eb1720
Add option for non-/ restoration to archives
adrianparvino Jan 6, 2019
12c4a01
Nixify luksFormat script
adrianparvino Jan 6, 2019
d85eeec
Remove sh from postMountCommands
adrianparvino Jan 6, 2019
64f16b9
Import duplicity-backup-common from -{backup,restore}
adrianparvino Jan 23, 2019
e645e3f
Move EncryptedCD files to their own repository
adrianparvino Jan 26, 2019
2311447
Change restoration terminology from root to target
adrianparvino Jan 26, 2019
8ab610d
Full backups should be done every month
adrianparvino Mar 13, 2019
7f02b19
Add individual enable option and target option for restoration
adrianparvino Mar 13, 2019
07a8468
cachedir -> cacheDir
adrianparvino Mar 20, 2019
23f8935
Temporarily add option to use full backups and pruning
adrianparvino Jul 10, 2019
abe5895
Add duplicity-system.nix
adrianparvino Jul 14, 2019
de233ac
Filter out <unknown-file> from module imports
adrianparvino Jul 14, 2019
51f41ea
Add option to disable missing_imports checking for the LiveCD
adrianparvino Jul 14, 2019
e8f694d
Invalidate all of USING docs
adrianparvino Jul 15, 2019
ba47d03
Add installation and prerequisites
adrianparvino Jul 15, 2019
645c7c6
Add credentials management to manual
adrianparvino Jul 15, 2019
4815438
Add instructions on enabling backups on manual
adrianparvino Jul 15, 2019
03343de
Clarify AWS credentials
adrianparvino Jul 16, 2019
689ba15
Clarify GPG-enabled nix-duplicity-backup
adrianparvino Jul 16, 2019
d34a197
Add explanation for envDir and pgpDir
adrianparvino Jul 16, 2019
ac307d7
Fix typos in AWS USING.org
adrianparvino Jul 16, 2019
20fc893
Add certian miscellaneous configurations that are often useful
adrianparvino Jul 16, 2019
a85b592
Remove support for multiple directories and push for include/exclude
adrianparvino Jul 17, 2019
252b834
Update documentation to remove directories and add includes
adrianparvino Jul 17, 2019
a0b5852
Fix typo in period
adrianparvino Jul 17, 2019
31a7f13
Revert using instanced services
adrianparvino Jul 17, 2019
f96056f
Clarify a few nuances in duplicity's enable options
adrianparvino Jul 17, 2019
93aae5b
Make --full-if-older-than configurable
adrianparvino Jul 17, 2019
f4a2127
Add option to prune full backups
adrianparvino Jul 17, 2019
baa999c
Add usage instructions for fullIfOlderThan and removeAllButNFull
adrianparvino Jul 17, 2019
b711c5b
Create function for writing an environment variable to a file
adrianparvino Jul 20, 2019
ca37a4d
Enable echo for non-confidential input
adrianparvino Jul 20, 2019
4b0d1c9
Add shebang to duplicity-gen-keys
adrianparvino Jul 20, 2019
4329ed4
Fix duplicity-gen-keys saving an invalid state when cancelled
adrianparvino Jul 20, 2019
8d74a39
Revamp prompting code to be more robust
adrianparvino Jul 20, 2019
5fae2c7
Fix quoting problems in writeVar's target
adrianparvino Jul 20, 2019
efe7589
Push usage of escapeShellArg
adrianparvino Jul 20, 2019
9a9aeb3
Change "''${X}" to "$X" as they are always equivalent
adrianparvino Jul 21, 2019
5620dee
Add auto-detection code for AWS credentials
adrianparvino Jul 21, 2019
7b1b261
Fix sed script to be order independent
adrianparvino Jul 21, 2019
6c8fdb8
Append -r flag to read
adrianparvino Jul 21, 2019
742ac4a
Add command line parsing
adrianparvino Jul 21, 2019
43c1e40
Decrease complexity of AWS extraction code
adrianparvino Jul 22, 2019
a6c32e7
Exclude system-specific.nix and hardware-configuration.nix
adrianparvino Jul 23, 2019
b6142c2
Add --update flag for key versioning
adrianparvino Jul 23, 2019
4c9df14
Report specific conflicting flags
adrianparvino Jul 24, 2019
e2f3c58
Remove usage of SUDO_USER aside from AWS_FILE
adrianparvino Jul 24, 2019
23e7121
Redirect user prompts to stderr
adrianparvino Jul 24, 2019
f6107a4
Simplify AWS detection code branching
adrianparvino Jul 24, 2019
f104ab9
Print available AWS profiles
adrianparvino Jul 24, 2019
3d5867f
Add an option to enable --force
adrianparvino Jul 24, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions LiveCDBackup.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#+TITLE: Creation of a LiveCD for the restoration of a NixOS system backed up with nix-duplicity-backup

* Background
** NixOS LiveCD
NixOS provides a module for the creation of a LiveCD through ~config.system.build.isoImage~ [fn:livecd].
This creates a fully bootable LiveCD with the full build process, starting with GRUB to NixOS proper.
It would probably require a few patches for full disk encryption, alternatively,
it should be possible for the partitition alone to be encrypted instead of FDE.
Furthermore, automating the encryption process might not be fully containable in Nix [fn:nixsecrets] [fn:nixprivate].
Nix relies on reproducibility yet encryption relies on nonces and initialization --
a more comprehensive discussion can be found on the Nix Encryption RFC [fn:nixencryption].

** nix-duplicity-backup
nix-duplicity-backup is a NixOS module initially authored by [[https://github.com/fgaz][fgaz]] and further modified by [[https://github.com/adrianparvino][adrianparvino]].
The patches by adrianparvino allows the usage and automation of the creation of
(a) GPG keys, or
(b) password files,
with a common interface.
The current architecture of nix-duplicity-backup requires the
installation of both restoration and backup scripts;
this poses a few problems;
(a) without FDE on the disk, mounting the flash drive will expose the identification files, and
(b) even with FDE on the disk, having the identication files bundled with the restoration script is somewhat sloppy.
nix-duplicity-backup is also currently unsuitable to be used directly for full system backups;
it requires users to manually specify the files to back up -- this is error-prone and tedious.

* Proposed solution
In addition to the solution proper; this section will also contain solutions to
overcome the shortcomings of the modules specified in the background.

** Full Disk Encryption on NixOS LiveCD
Full Disk Encryption for the LiveCD is achievable by using LVM instead of squashfs.
Alternatively, we can encrypt individual files in the LiveCD using GPG.
The GPG approach has the advantage of compatibility with ~<nixpkgs/nixos/modules/installer/cd-dvd/iso-image.nix>~,
and the identification files can simply be added with ~isoImage.contents~.
However, the LVM approach has the following advantages:
(1) as the ~.service~ file is located in ~/nix/store~, file encryption is not recommended, but possible,
(2) it requires no special treatment of individual files.

** nix-duplicity-backup-system
nix-duplicity-backup-system will be a NixOS module which scans for enabled NixOS options.
Take, for example, the postgresql module [fn:postgresql];
if it is enabled, then backup the file located at ~services.postgresql.dataDir~.

** Separation of restoration and backup
For this subsection, declare/define is used as in C.

nix-duplicity-backup configuration should be separated into 3 files --
(a) ~duplicity-backup-common.nix~
(b) ~duplicity-backup-backup.nix~
(c) ~duplicity-backup-restore.nix~

The ~duplicity-backup-common.nix~ declares the common interface between backup and restoration;
specifically, the backup directories and the backup destination.
Theoretically, this file should not contain any implementation, sans normalization.

The ~duplicity-backup-backup.nix~ is one definition of the declared common interface;
it will define the systemd services and timers for periodically backing up the directories to the directories.

The ~duplicity-backup-restore.nix~ is another definition of the declared common interface;
it will define shell scripts for restoration from backup destinations to their directories.
The restoration script should also allow one to change the target root location,
as the root file system would contain the LiveCD file system rather than the target's file system.

** Solution proper
We define a new NixOS module which
(a) adds ~nix-duplicity-backup-system~ to the system, and
(b) creates a LiveCD containing ~duplicity-backup-restore~.
The output of ~nix-duplicity-backup-system~ shall be passed as
the input of ~duplicity-backup-restore~.

The LiveCD is built and encrypted using ~./EncryptedCD~.
Adding everything to the LiveCD's ~/nix/store~,
and only maintaining symlinks to the system,
allows us to remove the need for multiple stages of decryption.

The LiveCD will be populated with the following files:
(a) The AWS S3 identity files
(b) The partition structure

Optionally, the LiveCD may also contain the following files:
(a) The duplicity GPG keys
By placing the duplicity GPG keys,
we are able to automate the decryption of the backup.
(b) The duplicity backups
It is also possible to store the backups directly to the USB drive,
allowing it to be restored without internet.

The partition structure can be generated using heuristics on ~mount~ and ~hardware-configuration.nix~.

The bootup process will be as follows:
*** Decryption of ~/nix/store~
Upon bootup, the LiveCD will prompt the user for a decryption key.
*** Rebuilding the partition structure
Using the partition structure provided by ~mount~ and ~hardware-configuration.nix~,
we are able to mimic the file structure of the original system.
Another key is then prompted for the decryption key of the restoration root.
*** [OPTIONAL] Input of the duplicity GPG key
If the GPG identification keys are not saved into ~/nix/store~,
the GPG key is prompted using [fn:interactivesystemd].
*** Duplicity restore
From here-on, everything should be automatically handled by ~duplicity-backup-restore.nix~.

[fn:livecd] https://nixos.wiki/wiki/Creating_a_NixOS_live_CD
[fn:nixsecrets] https://github.com/NixOS/nixpkgs/issues/24288
[fn:nixprivate] https://github.com/NixOS/nix/issues/8
[fn:nixencryption] https://github.com/edolstra/rfcs/blob/nix-encryption/rfcs/0005-nix-encryption.md

[fn:postgresql] https://github.com/NixOS/nixpkgs/blob/release-18.09/nixos/modules/services/databases/postgresql.nix

[fn:interactivesystemd] https://alan-mushi.github.io/2014/10/26/execute-an-interactive-script-at-boot-with-systemd.html
[fn:nixosencryptedroot] https://gist.github.com/martijnvermaat/76f2e24d0239470dd71050358b4d5134
124 changes: 124 additions & 0 deletions USING.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#+TITLE: nix-duplicity-backup
* Prerequisites and installation

The canonical installation of nix-duplicity-backup is S3+Password.
An alternative to S3 is rsync/SSH, however this is currently disabled.
An alternative to Password login is the usage of GPG keys.
Any S3 bucket will work provided that ACL permissions are granted to your user.

* Canonical Configuration

As an example, nix-duplicity-backup, the repository will be backed up.
An S3+Password configuration is used by default.
First, create a ~duplicity-backup-config.nix~ beside ~configuration.nix~:
#+BEGIN_src nix
# This serves as a configuration file, no backups will be created,
# and this is a noop, sans assertions checking and duplicity key generation.
{
# This loads the services.duplicity-backup options.
imports = [ <nix-duplicity-backup/duplicity-backup.nix> ];

services.duplicity-backup = {
# This enabled interpretation of the duplicity-backup config,
# specifically assertions checking and duplicity-gen-keys.
enable = true;

# Use passphrase instead of GPG keys
usePassphrase = true;

# Add an archive to duplicity-backup
archives.nix-duplicity-backup = {
# The S3(or SSH) instance to upload the backups to
destination = s3://s3.REGION.amazonaws.com/BUCKETNAME/nix-duplicity-backup;

# A directory or file to back up
directory = <nix-duplicity-backup>;
};
};
}
#+END_src

* Credentials management

Under default configurations,
~${envDir}~ is located in ~/var/keys/duplicity/env~,
~${pgpDir}~ is located in ~/var/keys/duplicity/gnupg~.

With an S3 backend, ~duplicity-gen-keys~ will ask for
~AWS_ACCESS_KEY_ID~ and ~AWS_SECRET_ACCESS_KEY~.
If you have previously used the AWS CLI,
then these can be found under ~$HOME/.aws/credentials~.
Otherwise, you need to check under
IAM > Users > [your user] > Security Credentials
on the Amazon AWS console.
Access key secrets are only shown upon creation,
so if you don't have an existing secret,
you'll have to generate a new access key ID.
These are statefully stored under ~${envDir}/10-aws.sh~

With passphrase enabled, it will prompt for a passphrase,
and store it under ~${envDir}/20-passphrase.sh~.

These are stored as Bash files, allowing you to load it imperatively using:
#+BEGIN_src bash
for i in ${envDir}/*; do
. $i
done
#+END_src

With GPG enabled, ~duplicity-gen-keys~ will generate GPG keys
and store it under in ~${pgpDir}~.
These can be loaded into your environment using
#+BEGIN_src bash
export PGP_HOME_DIR=${pgpDir}
#+END_src bash

* Enabling backups

Backups can finally be enabled by adding the following to your ~configuration.nix~:
#+BEGIN_src nix
{
imports = [ ./duplicity-backup-config.nix ];

# Adds the backup services and timers to systemd for periodic backups.
services.duplicity-backup.enableBackup = true;
}
#+END_src

To verify that everything works, run ~systemctl start duplicity-nix-duplicity-backup~.

* Configuration

More granular configurations are possible:
#+BEGIN_src nix
{
services.duplicity-backup = {
archives.nix-duplicity-backup = {
# Use GPG keys instead of passphrase login
usePassphrase = false;

archives.nix-duplicity-backup = {
# Defaults to "01:15"
# This makes the backups run hourly instead of 01:15 localtime.
# More info in `man 7 systemd.time`, section CALENDAR EVENTS.
period = "hourly";

# Exclude files containing the name "secret" from being uploaded.
excludes = [ "*secret*" ];

# However, allow files containing the name "code_to_handle_secret" to be uploaded.
includes = [ "*code_to_handle_secret*" ];

# Only use a maximum bandwidth of 1 MB/s.
maxbw = 1 * 1000 * 1000;

# Use a full backup every week instead of every month.
fullIfOlderThan = "7D";

# And only keep 2 weeks worth of full backups
removeAllButNFull = 2;
};
};
};
}
#+END_src
82 changes: 82 additions & 0 deletions duplicity-backup-backup.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{ pkgs, config, lib, options, ... }:

with lib;

let
gcfg = config.services.duplicity-backup;
in
{
imports = [ ./duplicity-backup-common.nix ];

options = {
services.duplicity-backup.enableBackup = mkEnableOption "periodic duplicity backups backup tools";
services.duplicity-backup.archives = mkOption {
type = types.attrsOf (types.submodule ({ name, config, ... }: {
options = {
backupService = mkOption {
type = types.attrs;
};
};

config.backupService = {
description = "Duplicity archive ${name}";

requires = [ "network-online.target" ];
after = [ "network-online.target" ];

serviceConfig = {
Type = "oneshot";
IOSchedulingClass = "idle";
NoNewPrivileges = "true";
CapabilityBoundingSet = [ "CAP_DAC_READ_SEARCH" ];
PermissionsStartOnly = "true";
};

script = ''
for i in ${gcfg.envDir}/*; do
source $i
done

mkdir -p ${gcfg.cacheDir}
chmod 0700 ${gcfg.cacheDir}

${pkgs.duplicity}/bin/duplicity \
--archive-dir ${gcfg.cacheDir} \
--name ${name} \
--gpg-options "--homedir=${gcfg.pgpDir}" \
--full-if-older-than ${config.fullIfOlderThan} \
'' + optionalString (config.allowSourceMismatch) ''--allow-source-mismatch \
'' + optionalString (!gcfg.usePassphrase) ''--encrypt-key "Duplicity Backup" \
'' + ''
${concatStringsSep " " (map (v: "--include '${v}'") config.includes)} \
${concatStringsSep " " (map (v: "--exclude '${v}'") config.excludes)} \
${config.directory} \
${config.destination}
'' + optionalString (config.removeAllButNFull != null) ''
${pkgs.duplicity}/bin/duplicity remove-all-but-n-full ${toString config.removeAllButNFull} \
--archive-dir ${gcfg.cacheDir} \
--name ${name} \
--gpg-options "--homedir=${gcfg.pgpDir}" \
--full-if-older-than ${config.fullIfOlderThan} \
--force \
${config.destination}
'';
};
}));
};
};

config = mkIf (gcfg.enable && gcfg.enableBackup) {
systemd.services = mapAttrs' (name: archive:
nameValuePair "duplicity-${name}" archive.backupService
) gcfg.archives;

# Note: the timer must be Persistent=true, so that systemd will start it even
# if e.g. your laptop was asleep while the latest interval occurred.
systemd.timers = mapAttrs' (name: cfg: nameValuePair "duplicity-${name}"
{ timerConfig.OnCalendar = cfg.period;
timerConfig.Persistent = "true";
wantedBy = [ "timers.target" ];
}) gcfg.archives;
};
}
Loading