Compare commits

..

15 Commits

9 changed files with 322 additions and 192 deletions

1
.gitea/README.md Symbolic link
View File

@@ -0,0 +1 @@
../templates/README.md

2
.gitignore vendored
View File

@@ -5,3 +5,5 @@ config/
key.txt key.txt
passphrase passphrase
ssh/ ssh/
/README.md
/notify.sh

174
Pipfile.lock generated
View File

@@ -18,10 +18,11 @@
"default": { "default": {
"bracex": { "bracex": {
"hashes": [ "hashes": [
"sha256:01f715cd0ed7a622ec8b32322e715813f7574de531f09b70f6f3b2c10f682425", "sha256:a27eaf1df42cf561fed58b7a8f3fdf129d1ea16a81e1fadd1d17989bc6384beb",
"sha256:64e2a6d14de9c8e022cf40539ac8468ba7c4b99550a2b05fc87fd20e392e568f" "sha256:efdc71eff95eaff5e0f8cfebe7d01adf2c8637c8c92edaf63ef348c241a82418"
], ],
"version": "==2.1.1" "markers": "python_version >= '3.8'",
"version": "==2.4"
}, },
"humanfriendly": { "humanfriendly": {
"hashes": [ "hashes": [
@@ -33,107 +34,126 @@
}, },
"pyyaml": { "pyyaml": {
"hashes": [ "hashes": [
"sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5",
"sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc",
"sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df",
"sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741",
"sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206",
"sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27",
"sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595",
"sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62",
"sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98",
"sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696",
"sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290",
"sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9",
"sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d",
"sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6",
"sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867",
"sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47",
"sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486",
"sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6",
"sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3",
"sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007",
"sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938",
"sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0",
"sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c",
"sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735",
"sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d",
"sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28",
"sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4",
"sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba",
"sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8",
"sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5",
"sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd",
"sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3",
"sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0",
"sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515",
"sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c",
"sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c",
"sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924",
"sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34",
"sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43",
"sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859",
"sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673",
"sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54",
"sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a",
"sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b",
"sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab",
"sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa",
"sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c",
"sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585",
"sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d",
"sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"
], ],
"index": "pypi", "index": "pypi",
"version": "==5.4.1" "version": "==6.0.1"
}, },
"wcmatch": { "wcmatch": {
"hashes": [ "hashes": [
"sha256:4d54ddb506c90b5a5bba3a96a1cfb0bb07127909e19046a71d689ddfb18c3617", "sha256:14554e409b142edeefab901dc68ad570b30a72a8ab9a79106c5d5e9a6d241bd5",
"sha256:9146b1ab9354e0797ef6ef69bc89cb32cb9f46d1b9eeef69c559aeec8f3bffb6" "sha256:86c17572d0f75cbf3bcb1a18f3bf2f9e72b39a9c08c9b4a74e991e1882a8efb3"
], ],
"index": "pypi", "index": "pypi",
"version": "==8.2" "version": "==8.5"
} }
}, },
"develop": { "develop": {
"mypy": { "mypy": {
"hashes": [ "hashes": [
"sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9", "sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7",
"sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a", "sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e",
"sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9", "sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c",
"sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e", "sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169",
"sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2", "sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208",
"sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212", "sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0",
"sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b", "sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1",
"sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885", "sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1",
"sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150", "sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7",
"sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703", "sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45",
"sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072", "sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143",
"sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457", "sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5",
"sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e", "sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f",
"sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0", "sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd",
"sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb", "sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245",
"sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97", "sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f",
"sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8", "sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332",
"sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811", "sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30",
"sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6", "sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183",
"sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de", "sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f",
"sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504", "sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85",
"sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921", "sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46",
"sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d" "sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71",
"sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660",
"sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb",
"sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c",
"sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.910" "version": "==1.6.1"
}, },
"mypy-extensions": { "mypy-extensions": {
"hashes": [ "hashes": [
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d",
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"
], ],
"version": "==0.4.3" "markers": "python_version >= '3.5'",
}, "version": "==1.0.0"
"toml": {
"hashes": [
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
"version": "==0.10.2"
}, },
"types-pyyaml": { "types-pyyaml": {
"hashes": [ "hashes": [
"sha256:1d9e431e9f1f78a65ea957c558535a3b15ad67ea4912bce48a6c1b613dcf81ad", "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062",
"sha256:f1d1357168988e45fa20c65aecb3911462246a84809015dd889ebf8b1db74124" "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24"
], ],
"index": "pypi", "index": "pypi",
"version": "==5.4.10" "version": "==6.0.12.12"
}, },
"typing-extensions": { "typing-extensions": {
"hashes": [ "hashes": [
"sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e", "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0",
"sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7", "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"
"sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"
], ],
"version": "==3.10.0.2" "markers": "python_version >= '3.8'",
"version": "==4.8.0"
} }
} }
} }

View File

@@ -486,8 +486,8 @@ def main(argv: typing.List[str]):
body_text = write_logs("Logged errors and warnings:", "EWN") body_text = write_logs("Logged errors and warnings:", "EWN")
body_text += "\n" body_text += b"\n\n"
body_text += write_logs("All logs:") body_text += write_logs("All log messages:")
# Subject summary # Subject summary
if errmsg and warnmsg: if errmsg and warnmsg:

View File

Binary file not shown.

View File

@@ -1,10 +1,34 @@
#!/bin/bash #!/bin/bash
# These can be overridden when running this script # These can be overridden when running this script
set_default_variables()
{
HOSTNAME=${HOSTNAME:-$(hostname)} HOSTNAME=${HOSTNAME:-$(hostname)}
BACKUP_HOST=${BACKUP_HOST:-backup.jim.sh} BACKUP_HOST=${BACKUP_HOST:-backup.jim.sh}
BACKUP_PORT=${BACKUP_PORT:-222}
BACKUP_USER=${BACKUP_USER:-jim-backups} BACKUP_USER=${BACKUP_USER:-jim-backups}
BACKUP_REPO=${BACKUP_REPO:-borg/${HOSTNAME}} BACKUP_REPO=${BACKUP_REPO:-borg/${HOSTNAME}}
SYSTEMD_UNIT=${SYSTEMD_UNIT:-borg-backup}
# Use stable host ID in case MAC address changes.
# Note that this host ID is only used to manage locks, so it's
# not crucial that it remains stable.
UUID=$(python3 -c 'import uuid;print(uuid.getnode())')
HOSTID=${BORG_HOST_ID:-"${HOSTNAME}@${UUID}"}
log "Configuration:"
log " HOSTNAME Local hostname: \033[1m${HOSTNAME}"
log " HOSTID Local host ID: \033[1m${HOSTID}"
log " BACKUP_USER Backup server user: \033[1m${BACKUP_USER}"
log " BACKUP_HOST Backup server host: \033[1m${BACKUP_HOST}"
log " BACKUP_PORT Backup server port: \033[1m${BACKUP_PORT}"
log " BACKUP_REPO Server repository: \033[1m${BACKUP_REPO}"
log " SYSTEMD_UNIT Systemd unit name: \033[1m${SYSTEMD_UNIT}"
for i in $(seq 15 -1 1); do
printf "\rPress ENTER or wait $i seconds to continue... \b"
if read -t 1 ; then break; fi
done
}
# Main dir is where this repo was checked out # Main dir is where this repo was checked out
BORG_DIR="$(realpath "$(dirname "$0")")" BORG_DIR="$(realpath "$(dirname "$0")")"
@@ -12,9 +36,6 @@ cd "${BORG_DIR}"
BORG_BIN="${BORG_DIR}/bin/borg.$(uname -m)" BORG_BIN="${BORG_DIR}/bin/borg.$(uname -m)"
# Use stable host ID in case MAC address changes
HOSTID="${HOSTNAME}@$(python -c 'import uuid;print(uuid.getnode())')"
function error_handler() { function error_handler() {
echo "Error at $1 line $2:" echo "Error at $1 line $2:"
echo -n '>>> ' ; tail -n +"$2" < "$1" | head -1 echo -n '>>> ' ; tail -n +"$2" < "$1" | head -1
@@ -29,8 +50,7 @@ set -o errtrace
setup_venv() setup_venv()
{ {
if ! which pipenv >/dev/null 2>&1 ; then if ! which pipenv >/dev/null 2>&1 ; then
echo "pipenv not found, try: sudo apt install pipenv" error "pipenv not found, try: sudo apt install pipenv"
exit 1
fi fi
mkdir -p .venv mkdir -p .venv
pipenv install pipenv install
@@ -42,13 +62,14 @@ create_borg_vars()
VARS=${BORG_DIR}/vars.sh VARS=${BORG_DIR}/vars.sh
# These variables are used elsewhere in this script # These variables are used elsewhere in this script
BORG_REPO="ssh://${BACKUP_USER}@${BACKUP_HOST}/./${BACKUP_REPO}" BORG_REPO="ssh://${BACKUP_USER}@${BACKUP_HOST}:${BACKUP_PORT}/./${BACKUP_REPO}"
BORG=${BORG_DIR}/borg.sh BORG=${BORG_DIR}/borg.sh
SSH=$BORG_DIR/ssh SSH=$BORG_DIR/ssh
cat >"$VARS" <<EOF cat >"$VARS" <<EOF
export BACKUP_USER=${BACKUP_USER} export BACKUP_USER=${BACKUP_USER}
export BACKUP_HOST=${BACKUP_HOST} export BACKUP_HOST=${BACKUP_HOST}
export BACKUP_PORT=${BACKUP_PORT}
export BACKUP_REPO=${BACKUP_REPO} export BACKUP_REPO=${BACKUP_REPO}
export HOSTNAME=${HOSTNAME} export HOSTNAME=${HOSTNAME}
export BORG_REPO=${BORG_REPO} export BORG_REPO=${BORG_REPO}
@@ -58,47 +79,67 @@ export BORG_DIR=${BORG_DIR}
export SSH=${SSH} export SSH=${SSH}
export BORG=${BORG} export BORG=${BORG}
export BORG_BIN=${BORG_BIN} export BORG_BIN=${BORG_BIN}
export SYSTEMD_UNIT=${SYSTEMD_UNIT}
EOF EOF
if ! "$BORG" -h >/dev/null ; then if ! "$BORG" -h >/dev/null ; then
error "Can't run the borg wrapper; does borg work?" error "Can't run the borg wrapper; does borg work?"
fi fi
} }
# Update paths in README and backup.py # Copy templated files, filling in templates as needed
update_paths() install_templated_files()
{ {
sed -i \ DOCS="README.md"
-e "s!\${HOSTNAME}!${HOSTNAME}!g" \ SCRIPTS="notify.sh logs.sh"
for i in ${DOCS} ${SCRIPTS}; do
sed -e "s!\${HOSTNAME}!${HOSTNAME}!g" \
-e "s!\${BORG_DIR}!${BORG_DIR}!g" \ -e "s!\${BORG_DIR}!${BORG_DIR}!g" \
-e "s!\${BORG_BIN}!${BORG_BIN}!g" \
-e "s!\${BACKUP_USER}!${BACKUP_USER}!g" \ -e "s!\${BACKUP_USER}!${BACKUP_USER}!g" \
-e "s!\${BACKUP_HOST}!${BACKUP_HOST}!g" \ -e "s!\${BACKUP_HOST}!${BACKUP_HOST}!g" \
-e "s!\${BACKUP_PORT}!${BACKUP_PORT}!g" \
-e "s!\${BACKUP_REPO}!${BACKUP_REPO}!g" \ -e "s!\${BACKUP_REPO}!${BACKUP_REPO}!g" \
README.md -e "s!\${SYSTEMD_UNIT}!${SYSTEMD_UNIT}!g" \
templates/$i > $i
done
chmod +x ${SCRIPTS}
}
# Update local paths in scripts
update_paths()
{
sed -i\ sed -i\
-e "1c#!${BORG_DIR}/.venv/bin/python" \ -e "1c#!${BORG_DIR}/.venv/bin/python" \
backup.py backup.py
} }
# See if we're just supposed to update an existing install # See if we're just supposed to update an existing install, or recovering
if [ "$1" == "--update-paths" ] || [ "$1" == "--update" ] ; then parse_args()
{
RECOVER=0
UPDATE=0
if [ "$1" == "--recover" ] ; then
if [ -e "vars.sh" ]; then if [ -e "vars.sh" ]; then
echo "Updating paths and variables" error "It looks like this borg was already set up, can only recover from fresh start"
update_paths
setup_venv
update_vars
exit 0
else
echo "Can't update, not set up yet"
exit 1
fi
fi fi
RECOVER=1
if [ -e "vars.sh" ]; then elif [ "$1" == "--update-paths" ] || [ "$1" == "--update" ] ; then
echo "Error: BORG_DIR $BORG_DIR already looks set up; giving up." if [ ! -e "vars.sh" ]; then
echo "Use \"git clean\" to return it to original state if desired" error "Can't update, not set up yet"
exit 1
fi fi
UPDATE=1
elif [ -n "$1" ] ; then
error "Unknown arg $1"
elif [ -e "vars.sh" ]; then
warn "Error: BORG_DIR $BORG_DIR already looks set up."
warn "Use \"git clean\" to return it to original state if desired".
warn "Or specify --update to refresh things from latest git."
error "Giving up"
fi
}
# Make a temp dir to work in # Make a temp dir to work in
TMP=$(mktemp -d) TMP=$(mktemp -d)
@@ -140,7 +181,18 @@ print_random_key()
generate_keys() generate_keys()
{ {
if [ $RECOVER -eq 1 ] ; then
notice "Recovering configuration in order to use an existing backup"
read -s -p "Repo key for \"borg ${HOSTNAME}\": " PASS_REPOKEY
echo
read -s -p "Again: " PASS_REPOKEY2
echo
if [ -z "$PASS_REPOKEY" ] || [ $PASS_REPOKEY != $PASS_REPOKEY2 ] ; then
error "Bad repo key"
fi
else
PASS_REPOKEY=$(print_random_key) PASS_REPOKEY=$(print_random_key)
fi
echo "$PASS_REPOKEY" > passphrase echo "$PASS_REPOKEY" > passphrase
chmod 600 passphrase chmod 600 passphrase
} }
@@ -183,15 +235,18 @@ EOF
ssh -F "$SSH/config" -o BatchMode=no -o PubkeyAuthentication=no \ ssh -F "$SSH/config" -o BatchMode=no -o PubkeyAuthentication=no \
-o ControlMaster=yes -o ControlPath="$TMP/ssh-control" \ -o ControlMaster=yes -o ControlPath="$TMP/ssh-control" \
-o StrictHostKeyChecking=accept-new \ -o StrictHostKeyChecking=accept-new \
-p "${BACKUP_PORT}" \
-f "${BACKUP_USER}@${BACKUP_HOST}" sleep 600 -f "${BACKUP_USER}@${BACKUP_HOST}" sleep 600
if ! run_ssh_command true >/dev/null 2>&1 </dev/null ; then if ! run_ssh_command true >/dev/null 2>&1 </dev/null ; then
error "SSH failed" error "SSH failed"
fi fi
log "Connected to ${BACKUP_USER}@${BACKUP_HOST}" log "Connected to ${BACKUP_USER}@${BACKUP_HOST}:${BACKUP_PORT}"
# Since we now have an SSH connection, check that the repo doesn't exist # Since we now have an SSH connection, check repo existence
if run_ssh_command "test -e $BACKUP_REPO" ; then if [ $RECOVER -eq 0 ] && run_ssh_command "test -e $BACKUP_REPO"; then
error "$BACKUP_REPO already exists on the server, bailing out" error "$BACKUP_REPO already exists on the server, bailing out"
elif [ $RECOVER -ne 0 ] && ! run_ssh_command "test -e $BACKUP_REPO"; then
error "$BACKUP_REPO does NOT exist on the server, can't recover backup config"
fi fi
# Copy SSH keys to the server's authorized_keys file, removing any # Copy SSH keys to the server's authorized_keys file, removing any
@@ -213,7 +268,8 @@ EOF
# Test that everything worked # Test that everything worked
log "Testing SSH login with new key" log "Testing SSH login with new key"
if ! ssh -F "$SSH/config" -i "$SSH/id_ecdsa_appendonly" -T \ if ! ssh -F "$SSH/config" -i "$SSH/id_ecdsa_appendonly" -T \
"${BACKUP_USER}@${BACKUP_HOST}" "$REMOTE_BORG" --version </dev/null ; then -p "${BACKUP_PORT}" "${BACKUP_USER}@${BACKUP_HOST}" "$REMOTE_BORG" \
--version </dev/null ; then
error "Logging in with a key failed -- is server set up correctly?" error "Logging in with a key failed -- is server set up correctly?"
fi fi
log "Remote connection OK!" log "Remote connection OK!"
@@ -242,8 +298,8 @@ EOF
configure_systemd() configure_systemd()
{ {
TIMER=borg-backup.timer TIMER=${SYSTEMD_UNIT}.timer
SERVICE=borg-backup.service SERVICE=${SYSTEMD_UNIT}.service
TIMER_UNIT=${BORG_DIR}/${TIMER} TIMER_UNIT=${BORG_DIR}/${TIMER}
SERVICE_UNIT=${BORG_DIR}/${SERVICE} SERVICE_UNIT=${BORG_DIR}/${SERVICE}
@@ -283,6 +339,19 @@ Restart=on-failure
RestartSec=600 RestartSec=600
EOF EOF
if [ $RECOVER -eq 1 ] ; then
log "Partially setting up systemd"
ln -sfv "${TIMER_UNIT}" /etc/systemd/system
ln -sfv "${SERVICE_UNIT}" /etc/systemd/system
systemctl --no-ask-password daemon-reload
systemctl --no-ask-password stop ${TIMER}
systemctl --no-ask-password disable ${TIMER}
warn "Since we're recovering, systemd automatic backups aren't enabled"
warn "Do something like this to configure automatic backups:"
echo " sudo systemctl enable ${TIMER} &&"
echo " sudo systemctl start ${TIMER}"
warn ""
else
log "Setting up systemd" log "Setting up systemd"
if ( if (
ln -sfv "${TIMER_UNIT}" /etc/systemd/system && ln -sfv "${TIMER_UNIT}" /etc/systemd/system &&
@@ -304,36 +373,63 @@ EOF
echo " sudo systemctl start ${TIMER}" echo " sudo systemctl start ${TIMER}"
warn "" warn ""
fi fi
fi
} }
git_setup() git_setup()
{ {
if ! git checkout -b "setup-${HOSTNAME}" ; then log "Committing local changes to git"
if ! git checkout -b "setup-${HOSTNAME}" ||
! git add ${SYSTEMD_UNIT}.service ${SYSTEMD_UNIT}.timer vars.sh ||
! git commit -a -m "autocommit after initial setup on ${HOSTNAME}" ; then
warn "Git setup failed; ignoring" warn "Git setup failed; ignoring"
return return
fi fi
log "Committing local changes to git"
git add README.md borg-backup.service borg-backup.timer vars.sh
git commit -a -m "autocommit after initial setup on ${HOSTNAME}"
} }
log "Configuration:" main() {
log " Backup server host: ${BACKUP_HOST}" if [ $UPDATE -eq 1 ] ; then
log " Backup server user: ${BACKUP_USER}" notice "Non-destructively updating paths, variables, and venv..."
log " Repository path: ${BACKUP_REPO}" source vars.sh
set_default_variables
install_templated_files
update_paths
setup_venv
create_borg_vars
notice "Testing SSH"
ssh -F "$SSH/config" -i "$SSH/id_ecdsa_appendonly" \
-o BatchMode=no -o StrictHostKeyChecking=ask \
-p "${BACKUP_PORT}" "${BACKUP_USER}@${BACKUP_HOST}" info >/dev/null
notice "Testing borg: if host location changed, say 'y' here"
${BORG_DIR}/borg.sh info
notice "Done -- check 'git diff' and verify changes."
exit 0
fi
set_default_variables
setup_venv setup_venv
create_borg_vars create_borg_vars
generate_keys generate_keys
configure_ssh configure_ssh
create_repo [ $RECOVER -eq 0 ] && create_repo
export_keys export_keys
configure_systemd configure_systemd
install_templated_files
update_paths update_paths
git_setup git_setup
echo echo
if [ $RECOVER -eq 1 ] ; then
notice "You should be set up with borg pointing to the existing repo now."
notice "Use commands like these to look at the backup:"
notice " sudo ${BORG_DIR}/borg.sh info"
notice " sudo ${BORG_DIR}/borg.sh list"
notice "You'll want to now restore files like ${BORG_DIR}/config.yaml before enabling systemd timers"
else
notice "Add this password to Bitwarden:" notice "Add this password to Bitwarden:"
notice "" notice ""
notice " Name: borg ${HOSTNAME}" notice " Name: borg ${HOSTNAME}"
@@ -344,7 +440,12 @@ notice "Test the backup file list with"
notice " sudo ${BORG_DIR}/backup.py --dry-run" notice " sudo ${BORG_DIR}/backup.py --dry-run"
notice "and make any necessary adjustments to:" notice "and make any necessary adjustments to:"
notice " ${BORG_DIR}/config.yaml" notice " ${BORG_DIR}/config.yaml"
fi
echo echo
echo "All done" echo "All done"
}
parse_args "$@"
main

View File

@@ -9,41 +9,37 @@ Run on client:
Customize `/opt/borg/config.yaml` as desired. Customize `/opt/borg/config.yaml` as desired.
Cheat sheet Cheat sheet
=========== ===========
*After setup, the copy of this file on the client will have the *After setup, /opt/borg/README.md will have the variables in this
variables in this section filled in automatically* section filled in automatically*
## Configuration ## Configuration
Hostname: ${HOSTNAME} Hostname: ${HOSTNAME}
Base directory: ${BORG_DIR} Base directory: ${BORG_DIR}
Destination: ${BACKUP_USER}@${BACKUP_HOST} Destination: ${BACKUP_USER}@${BACKUP_HOST}:${BACKUP_PORT}
Repository: ${BACKUP_REPO} Repository: ${BACKUP_REPO}
## Commands ## Commands
See when next backup is scheduled: See when next backup is scheduled:
systemctl list-timers borg-backup.timer systemctl list-timers ${SYSTEMD_UNIT}.timer
See status of most recent backup: See log of most recent backup attempt:
systemctl status --full --lines 999999 --no-pager --all borg-backup ${BORG_DIR}/logs.sh
${BORG_DIR}/logs.sh -f
Watch log:
journalctl --all --follow --unit borg-backup
Start backup now: Start backup now:
sudo systemctl start borg-backup sudo systemctl restart ${SYSTEMD_UNIT}
Interrupt backup in progress: Interrupt backup in progress:
sudo systemctl stop borg-backup sudo systemctl stop ${SYSTEMD_UNIT}
Show backups and related info: Show backups and related info:
@@ -61,6 +57,13 @@ Mount and look at files:
sudo -s # to explore as root sudo -s # to explore as root
sudo umount mnt sudo umount mnt
Update borg-setup from git:
cd /opt/borg
sudo git remote update
sudo git rebase origin/master
sudo ./inital-setup.sh --update
sudo git commit -m 'Update borg-setup'
## Compaction and remote access ## Compaction and remote access
@@ -71,13 +74,13 @@ compromised, then you can run compaction manually directly on the
backup host by logging in via SSH (bitwarden `ssh ${BACKUP_HOST} / backup host by logging in via SSH (bitwarden `ssh ${BACKUP_HOST} /
${BACKUP_USER}`) and compacting there: ${BACKUP_USER}`) and compacting there:
ssh ${BACKUP_USER}@${BACKUP_HOST} borg/borg compact --verbose --progress ${BACKUP_REPO} ssh -p ${BACKUP_PORT} ${BACKUP_USER}@${BACKUP_HOST} borg/borg compact --verbose --progress ${BACKUP_REPO}
This doesn't require the repo key. It shouldn't be entered on the untrusted This doesn't require the repo key. That key shouldn't be entered on
backup host, so for operations that need it, use a trusted host and run borg the untrusted backup host, so for operations that need it, use a
remotely instead, e.g.: trusted host and run borg remotely instead, e.g.:
${BORG_DIR}/bin/borg --remote-path borg/borg info ${BACKUP_USER}@${BACKUP_HOST}:borg/${HOSTNAME} ${BORG_BIN} --remote-path borg/borg info ssh://${BACKUP_USER}@${BACKUP_HOST}:${BACKUP_PORT}/borg/${HOSTNAME}
The repo passphrase is in bitwarden `borg ${HOSTNAME} / repo key`. The repo passphrase is in bitwarden `borg ${HOSTNAME} / repo key`.
@@ -139,5 +142,4 @@ Then run `borg.x86_64`. Confirm the version with `borg.armv7l --version`.
*Note:* This uses the deprecated `llfuse` instead of the newer `pyfuse3`. *Note:* This uses the deprecated `llfuse` instead of the newer `pyfuse3`.
`pyfuse3` doesn't work because, at minimum, it pulls in `trio` which `pyfuse3` doesn't work because, at minimum, it pulls in `trio` which
requires `ssl` which is explicitly excluded by requires `ssl` which is explicitly excluded by `scripts/borg.exe.spec`.
`scripts/borg.exe.spec`.

3
templates/logs.sh Normal file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
exec journalctl --all --unit ${SYSTEMD_UNIT} --since "$(systemctl show -P ExecMainStartTimestamp ${SYSTEMD_UNIT})" "$@"

1
notify.sh → templates/notify.sh Executable file → Normal file
View File

@@ -22,5 +22,6 @@ EMAIL="$2"
ssh \ ssh \
-F "$SSH/config" \ -F "$SSH/config" \
-i "$SSH/id_ecdsa_notify" \ -i "$SSH/id_ecdsa_notify" \
-p "$BACKUP_PORT" \
"$BACKUP_USER@$BACKUP_HOST" \ "$BACKUP_USER@$BACKUP_HOST" \
borg/notify.sh "$EMAIL" borg/notify.sh "$EMAIL"