diff --git a/Cargo.lock b/Cargo.lock index e28418d..da495e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,6 +78,7 @@ dependencies = [ "futures-util", "main_error", "md5 0.8.0", + "secretfile", "thiserror 2.0.18", "tokio", "tracing", @@ -940,6 +941,12 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "secretfile" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e1f99fdbcc14f9d8292bd680cb6ec6c265d141e5073f61024fcddc891e405c1" + [[package]] name = "serde" version = "1.0.228" diff --git a/Cargo.toml b/Cargo.toml index cb55e59..cd6126e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ tracing = "0.1.44" tracing-subscriber = "0.3.23" futures-util = "0.3.32" md5 = "0.8.0" +secretfile = "0.1.1" [profile.release] lto = true diff --git a/README.md b/README.md index 02a2922..b38148e 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,19 @@ Backup program for demos.tf demos. -A simple program that incrementally backs up every demo file from demos.tf to a local directory. +A simple program that incrementally backs up every demo file from demos.tf to a +local directory. ## Usage -The following environment variables are required for the program +The following environment variables are required for the program: STORAGE_ROOT: The directory to store the demos in STATE_FILE: The textfile to store the backup progress in between runs -The program will look in a .env file if the variables aren't set in the environment \ No newline at end of file +The following optional environment variables can additionaly be supplied: + + ACCESS_KEY_FILE: File containig the api access key, required to backup private demos + +The program will look in a .env file if the variables aren't set in the +environment diff --git a/nix/module.nix b/nix/module.nix index 93ee8eb..ea4b737 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -36,6 +36,10 @@ in { default = "*:0/10"; description = "Interval to run the service"; }; + keyFile = mkOption { + type = types.nullOr types.str; + description = "access key file path"; + }; package = mkOption { type = types.package; @@ -48,15 +52,24 @@ in { systemd.services.demostf-backup = { description = "Backup demos for demos.tf"; - environment = { - STORAGE_ROOT = cfg.target; - SOURCE = cfg.api; - STATE_FILE = cfg.stateFile; - RUST_LOG = cfg.logLevel; - }; + environment = + { + STORAGE_ROOT = cfg.target; + SOURCE = cfg.api; + STATE_FILE = cfg.stateFile; + RUST_LOG = cfg.logLevel; + } + // optionalAttrs (cfg.keyFile != null) { + ACCESS_KEY_FILE = "$CREDENTIALS_DIRECTORY/api_key"; + }; serviceConfig = { ExecStart = "${cfg.package}/bin/demostf-backup"; + + LoadCredential = optionals (cfg.keyFile != null) [ + "api_key:${cfg.keyFile}" + ]; + ReadWritePaths = [cfg.target cfg.stateFile]; Restart = "on-failure"; User = cfg.user; diff --git a/src/backup.rs b/src/backup.rs index 93762db..57ace27 100644 --- a/src/backup.rs +++ b/src/backup.rs @@ -1,5 +1,5 @@ -use crate::store::Store; use crate::Error; +use crate::store::Store; use demostf_client::{ApiClient, Demo, ListOrder, ListParams}; use std::time::Duration; use tokio::time::timeout; @@ -11,11 +11,13 @@ pub struct Backup { } impl Backup { - pub fn new(store: Store) -> Self { - Backup { - store, - client: ApiClient::new(), + pub fn new(store: Store, access_key: Option) -> Self { + let mut client = ApiClient::new(); + if let Some(access_key) = access_key { + info!("using access key"); + client.set_access_key(access_key); } + Backup { store, client } } #[instrument(skip_all, fields(demo.id = demo.id, demo.name = name))] diff --git a/src/main.rs b/src/main.rs index 4c2928e..a768e92 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,13 @@ async fn main() -> Result<(), MainError> { let mut args: HashMap<_, _> = dotenvy::vars().collect(); let store = Store::new(args.get("STORAGE_ROOT").expect("no STORAGE_ROOT set")); let state_path = PathBuf::from(args.remove("STATE_FILE").expect("no STATE_FILE set")); - let backup = Backup::new(store); + let key_file = args.remove("ACCESS_KEY_FILE"); + let access_key = key_file + .as_deref() + .map(secretfile::load) + .transpose()? + .filter(|key| !key.is_empty()); + let backup = Backup::new(store, access_key); let last_page = if state_path.is_file() { max(