]>
Commit | Line | Data |
---|---|---|
1c90c0dd IB |
1 | { lib, pkgs, config, name, ... }: |
2 | ||
3 | let | |
4 | cfg = config.myEnv.borg_backup; | |
5 | varDir = "/var/lib/borgbackup"; | |
6 | borg_args = "--encryption repokey --make-parent-dirs init create prune compact check"; | |
7 | borg_backup_full_with_ignored = pkgs.writeScriptBin "borg_full_with_ignored" '' | |
8 | #!${pkgs.stdenv.shell} | |
9 | ||
10 | if [ -z "$1" -o "$1" = "-h" -o "$1" = "--help" ]; then | |
11 | echo "borg_full_with_ignored /path/to/borgmatic.yaml" | |
12 | echo "Does a full backup including directories with .duplicity-ignore" | |
13 | exit 1 | |
14 | fi | |
15 | ${pkgs.borgmatic}/bin/borgmatic -c "$1" --override 'storage.archive_name_format="{hostname}-with-ignored-{now:%Y-%m-%dT%H:%M:%S.%f}"' --override 'location.exclude_if_present=[]' ${borg_args} | |
16 | ''; | |
17 | borg_backup = pkgs.writeScriptBin "borg_backup" '' | |
18 | #!${pkgs.stdenv.shell} | |
19 | ||
20 | declare -a profiles | |
21 | profiles=() | |
22 | ${builtins.concatStringsSep "\n" (lib.flatten (lib.mapAttrsToList (k: v: map (remote: [ | |
23 | ''profiles+=("${remote}_${k}")'' | |
24 | ]) v.remotes) config.services.borgBackup.profiles))} | |
25 | ||
26 | if [ -f "${varDir}/last_backup_profile" ]; then | |
27 | last_backup=$(cat ${varDir}/last_backup_profile) | |
28 | for i in "''${!profiles[@]}"; do | |
29 | if [[ "''${profiles[$i]}" = "$last_backup" ]]; then | |
30 | break | |
31 | fi | |
32 | done | |
33 | ((i+=1)) | |
34 | profiles=("''${profiles[@]:$i}" "''${profiles[@]:0:$i}") | |
35 | fi | |
36 | ||
37 | # timeout in minutes | |
38 | timeout="''${1:-180}" | |
39 | timeout_timestamp=$(date +%s -d "$timeout minutes") | |
40 | for profile in "''${profiles[@]}"; do | |
41 | if [ $(date +%s -d "now") -ge "$timeout_timestamp" ]; then | |
42 | break | |
43 | fi | |
44 | ||
45 | touch "${varDir}/$profile.log" | |
46 | ${pkgs.borgmatic}/bin/borgmatic -c "${config.secrets.location}/borg_backup/$profile/borgmatic.yaml" ${borg_args} >> ${varDir}/$profile.log | |
47 | [[ $? = 0 ]] || echo -e "Error when doing backup for $profile, see above or logs in ${varDir}/$profile.log\n---------------------------------------" >&2 | |
48 | echo "$profile" > ${varDir}/last_backup_profile | |
49 | done | |
50 | ''; | |
51 | ||
52 | check_backups = pkgs.writeScriptBin "borg_list_not_backuped" '' | |
53 | #!${pkgs.stdenv.shell} | |
54 | ||
55 | do_check() { | |
56 | local dir="$1" path ignored_path | |
57 | find "$dir" -mindepth 1 -maxdepth 1 | while IFS= read -r path; do | |
58 | if ${pkgs.gnugrep}/bin/grep -qFx "$path" ${config.secrets.fullPaths."borg_backup/backuped_list"}; then | |
59 | continue | |
60 | elif ${pkgs.gnugrep}/bin/grep -q "^$path/" ${config.secrets.fullPaths."borg_backup/backuped_list"}; then | |
61 | do_check "$path" | |
62 | else | |
63 | while IFS= read -r ignored_path; do | |
64 | if [[ "$path" =~ ^$ignored_path$ ]]; then | |
65 | continue 2 | |
66 | fi | |
67 | done < ${config.secrets.fullPaths."borg_backup/ignored_list"} | |
68 | printf '%s\n' "$path" | |
69 | fi | |
70 | done | |
71 | } | |
72 | ||
73 | do_check /var/lib | |
74 | ''; | |
75 | borgProfile = profile: remote: bucket: builtins.toJSON { | |
76 | location = { | |
77 | source_directories = map (p: "${profile.rootDir}/${p}") profile.includedPaths; | |
78 | repositories = [ | |
79 | { path = cfg.remotes.${remote}.remote bucket; label = "backupserver"; } | |
80 | ]; | |
81 | one_file_system = false; | |
82 | exclude_if_present = [".duplicity-ignore"]; | |
83 | source_directories_must_exist = profile.directoriesMustExist; | |
84 | borgmatic_source_directory = "${varDir}/${profile.bucket}/.borgmatic"; | |
85 | }; | |
86 | storage = { | |
87 | encryption_passphrase = profile.password; | |
88 | ssh_command = "ssh -i ${config.secrets.fullPaths."borg_backup/identity"}"; | |
89 | compression = "zlib"; | |
90 | borg_base_directory = "${varDir}/${profile.bucket}"; | |
91 | }; | |
92 | retention = { | |
93 | keep_within = "10d"; | |
94 | keep_daily = 30; | |
95 | }; | |
96 | }; | |
97 | in | |
98 | { | |
99 | options = { | |
100 | services.borgBackup.enable = lib.mkOption { | |
101 | type = lib.types.bool; | |
102 | default = false; | |
103 | description = '' | |
104 | Whether to enable remote backups. | |
105 | ''; | |
106 | }; | |
107 | services.borgBackup.profiles = lib.mkOption { | |
108 | type = lib.types.attrsOf (lib.types.submodule { | |
109 | options = { | |
110 | hash = lib.mkOption { | |
111 | type = lib.types.bool; | |
112 | default = true; | |
113 | description = '' | |
114 | Hash bucket and directory names | |
115 | ''; | |
116 | }; | |
117 | rootDir = lib.mkOption { | |
118 | type = lib.types.path; | |
119 | default = "/var/lib"; | |
120 | description = '' | |
121 | Path to backup | |
122 | ''; | |
123 | }; | |
124 | password = lib.mkOption { | |
125 | type = lib.types.str; | |
126 | default = cfg.password; | |
127 | description = '' | |
128 | password to use to encrypt data | |
129 | ''; | |
130 | }; | |
131 | directoriesMustExist = lib.mkOption { | |
132 | type = lib.types.bool; | |
133 | default = true; | |
134 | description = '' | |
135 | Raise error if backuped directory doesn't exist | |
136 | ''; | |
137 | }; | |
138 | bucket = lib.mkOption { | |
139 | type = lib.types.str; | |
140 | description = '' | |
141 | Bucket to use | |
142 | ''; | |
143 | }; | |
144 | remotes = lib.mkOption { | |
145 | type = lib.types.listOf lib.types.str; | |
146 | description = '' | |
147 | Remotes to use for backup | |
148 | ''; | |
149 | }; | |
150 | includedPaths = lib.mkOption { | |
151 | type = lib.types.listOf lib.types.str; | |
152 | default = []; | |
153 | description = '' | |
154 | Included paths (subdirs of rootDir) | |
155 | ''; | |
156 | }; | |
157 | excludeFile = lib.mkOption { | |
158 | type = lib.types.lines; | |
159 | default = ""; | |
160 | description = '' | |
161 | Content to put in exclude file | |
162 | ''; | |
163 | }; | |
164 | ignoredPaths = lib.mkOption { | |
165 | type = lib.types.listOf lib.types.str; | |
166 | default = []; | |
167 | description = '' | |
168 | List of paths to ignore when checking non-backed-up directories | |
169 | Can use (POSIX extended) regex | |
170 | ''; | |
171 | }; | |
172 | }; | |
173 | }); | |
174 | }; | |
175 | }; | |
176 | ||
177 | config = lib.mkIf config.services.borgBackup.enable { | |
178 | system.activationScripts.borg_backup = '' | |
179 | install -m 0700 -o root -g root -d ${varDir} | |
180 | ''; | |
181 | secrets.keys = lib.listToAttrs (lib.flatten (lib.mapAttrsToList (k: v: | |
182 | let | |
183 | bucket = if v.hash or true then builtins.hashString "sha256" v.bucket else v.bucket; | |
184 | in map (remote: [ | |
185 | (lib.nameValuePair "borg_backup/${remote}_${k}/borgmatic.yaml" { | |
186 | permissions = "0400"; | |
187 | text = borgProfile v remote bucket; | |
188 | }) | |
189 | (lib.nameValuePair "borg_backup/${remote}_${k}" { | |
190 | permissions = "0700"; | |
191 | isDir = true; | |
192 | }) | |
193 | ]) v.remotes) config.services.borgBackup.profiles)) // { | |
194 | "borg_backup/identity" = { | |
195 | permissions = "0400"; | |
196 | text = "{{ .ssl_keys.borg_backup }}"; | |
197 | }; | |
198 | "borg_backup/ignored_list" = { | |
199 | permissions = "0400"; | |
200 | text = let | |
201 | ignored = map | |
202 | (v: map (p: "${v.rootDir}/${p}") v.ignoredPaths) | |
203 | (builtins.attrValues config.services.borgBackup.profiles); | |
204 | in builtins.concatStringsSep "\n" (lib.flatten ignored); | |
205 | }; | |
206 | "borg_backup/backuped_list" = { | |
207 | permissions = "0400"; | |
208 | text = let | |
209 | included = map | |
210 | (v: map (p: "${v.rootDir}/${p}") v.includedPaths) | |
211 | (builtins.attrValues config.services.borgBackup.profiles); | |
212 | in builtins.concatStringsSep "\n" (lib.flatten included); | |
213 | }; | |
214 | }; | |
215 | ||
216 | programs.ssh.knownHostsFiles = [ | |
217 | (pkgs.writeText | |
218 | "borg_backup_known_hosts" | |
219 | (builtins.concatStringsSep | |
220 | "\n" | |
221 | (builtins.filter | |
222 | (v: v != null) | |
223 | (builtins.map | |
224 | (v: v.sshKnownHosts) | |
225 | (builtins.attrValues cfg.remotes) | |
226 | ) | |
227 | ) | |
228 | ) | |
229 | ) | |
230 | ]; | |
231 | environment.systemPackages = [ pkgs.borgbackup pkgs.borgmatic borg_backup_full_with_ignored borg_backup check_backups ]; | |
232 | services.cron = { | |
233 | enable = true; | |
234 | systemCronJobs = [ | |
235 | "0 0 * * * root ${borg_backup}/bin/borg_backup 300" | |
236 | ]; | |
237 | ||
238 | }; | |
239 | ||
240 | }; | |
241 | } |