From ff6becc72cbd0831c74ff8d1145f609e3bb99fdf Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 15 Mar 2026 11:36:47 +0800 Subject: [PATCH] updates --- Cargo.lock | 33 +++++++++++--- Cargo.toml | 2 + README.md | 15 +++++++ src/config_util.rs | 106 ++++++++++++++++++++++++++++++++++++++++----- src/main.rs | 2 +- 5 files changed, 140 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c882e7..cb84cfc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1898,6 +1898,8 @@ dependencies = [ "reqwest", "rust_util", "sequoia-openpgp", + "serde", + "serde_json", "sha-1", "tar", "tiny-encrypt", @@ -2666,18 +2668,28 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.216" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.216" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -2686,14 +2698,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] @@ -3873,6 +3886,12 @@ dependencies = [ "zstd", ] +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + [[package]] name = "zopfli" version = "0.8.1" diff --git a/Cargo.toml b/Cargo.toml index 1ba8b3b..ceba3a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,3 +29,5 @@ tar = "0.4" flate2 = "1.0" rust_util = "0.6" tiny-encrypt = { version = "1.8", default-features = false } +serde = { version = "1.0.228", features = ["serde_derive"] } +serde_json = "1.0.149" diff --git a/README.md b/README.md index 3cb4197..4783a61 100644 --- a/README.md +++ b/README.md @@ -68,3 +68,18 @@ cd nettle make sudo make install ``` + +
+ +Use STS token resolver: +```json +{ + "oss_config": { + "endpoint": "_endpoint", + "sts_token_resolver": ["assume-role.ts", "ROLE_ARN"], + "bucket": "_bucket", + "path": "_path" + } +} + +``` diff --git a/src/config_util.rs b/src/config_util.rs index 20a9a22..05c5250 100644 --- a/src/config_util.rs +++ b/src/config_util.rs @@ -1,10 +1,9 @@ use chrono::Utc; use json::JsonValue; -use rust_util::{new_box_ioerror, XResult}; -use std::{ - fs, - path::Path, -}; +use rust_util::{new_box_ioerror, util_cmd, XResult}; +use serde::Deserialize; +use std::process::Command; +use std::{fs, path::Path}; pub const NAME: &str = env!("CARGO_PKG_NAME"); pub const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -37,11 +36,22 @@ const ETC_OSS_BACKUPD_CONFIG: &str = "/etc/oss-backupd/config.json"; } */ +#[derive(Debug, Deserialize)] +pub struct StsResolverResponse { + pub mode: String, + pub access_key_id: String, + pub access_key_secret: String, + pub sts_token: String, + pub expiration: String, +} + #[derive(Debug, Clone)] pub struct OSSConfig { pub endpoint: Option, pub access_key_id: Option, pub access_key_secret: Option, + pub security_token: Option, + pub sts_token_resolver: Option>, pub bucket: Option, pub path: Option, } @@ -184,6 +194,12 @@ fn parse_oss_backupd_config_item( if oc.access_key_secret.is_none() && root_oc.access_key_secret.is_some() { oc.access_key_secret = root_oc.access_key_secret.clone(); } + if oc.security_token.is_none() && root_oc.security_token.is_some() { + oc.security_token = root_oc.security_token.clone(); + } + if oc.sts_token_resolver.is_none() && root_oc.sts_token_resolver.is_some() { + oc.sts_token_resolver = root_oc.sts_token_resolver.clone(); + } if oc.bucket.is_none() && root_oc.bucket.is_some() { oc.bucket = root_oc.bucket.clone(); } @@ -249,13 +265,68 @@ fn parse_sub_oss_config(json: &json::JsonValue) -> Option { } fn parse_oss_config(oss_config: &json::JsonValue) -> OSSConfig { - OSSConfig { + let mut oss_config = OSSConfig { endpoint: get_string_value(oss_config, "endpoint"), access_key_id: get_string_value(oss_config, "access_key_id"), access_key_secret: get_string_value(oss_config, "access_key_secret"), + security_token: None, + sts_token_resolver: get_string_array_value(oss_config, "sts_token_resolver"), bucket: get_string_value(oss_config, "bucket"), path: get_string_value(oss_config, "path"), + }; + + if oss_config.sts_token_resolver.is_some() + && oss_config.access_key_id.is_none() + && oss_config.access_key_secret.is_none() + { + if let Some(sts_token_resolver) = &oss_config.sts_token_resolver { + if sts_token_resolver.len() > 0 { + let mut cmd = Command::new(&sts_token_resolver[0]); + for arg in sts_token_resolver.iter().skip(1) { + cmd.arg(arg); + } + match util_cmd::run_command_and_wait(&mut cmd) { + Ok(exit_status) => { + if exit_status.success() { + match cmd.output() { + Ok(output) => { + match serde_json::from_slice::( + output.stdout.as_slice(), + ) { + Ok(sts_resolver_response) => { + oss_config.access_key_id = Some(sts_resolver_response.access_key_id); + oss_config.access_key_secret = Some(sts_resolver_response.access_key_secret); + oss_config.security_token = Some(sts_resolver_response.sts_token); + } + Err(err) => { + failure!( + "Parse {:?} response failed: {}", + sts_token_resolver, + err + ); + } + } + } + Err(err) => { + failure!("Execute {:?} failed: {}", sts_token_resolver, err); + } + } + } else { + failure!( + "Execute {:?} exit failed: {}", + sts_token_resolver, + exit_status + ); + } + } + Err(err) => { + failure!("Execute {:?} failed: {}", sts_token_resolver, err); + } + } + } + } } + oss_config } fn get_string_value(json: &JsonValue, key: &str) -> Option { @@ -263,6 +334,20 @@ fn get_string_value(json: &JsonValue, key: &str) -> Option { value.as_str().map(|s| s.to_owned()) } +fn get_string_array_value(json: &JsonValue, key: &str) -> Option> { + let value = &json[key]; + if let JsonValue::Array(array) = value { + let mut arr = vec![]; + for value in array { + if let Some(s) = value.as_str() { + arr.push(s.to_owned()); + } + } + return Some(arr); + } + return None; +} + fn get_tiny_encrypt_value(json: &JsonValue) -> Option { let tiny_encrypt = &json["tiny_encrypt"]; let config = get_string_value(tiny_encrypt, "config"); @@ -324,10 +409,11 @@ fn get_config_content(custom_oss_backupd_config: Option<&str>, verbose: bool) -> } }; } - let home_dot_oss_backupd_config = &get_user_home_dir(DOT_OSS_BACKUPD_CONFIG).unwrap_or_else(|e| { - warning!("Get user home error: {}", e); - String::new() - }); + let home_dot_oss_backupd_config = + &get_user_home_dir(DOT_OSS_BACKUPD_CONFIG).unwrap_or_else(|e| { + warning!("Get user home error: {}", e); + String::new() + }); if !home_dot_oss_backupd_config.is_empty() { let home_dot_oss_backupd_config_path = Path::new(home_dot_oss_backupd_config); if home_dot_oss_backupd_config_path.exists() { diff --git a/src/main.rs b/src/main.rs index d391967..3f91a0e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ use std::{ fs::{ self, File }, }; use std::path::PathBuf; -use rust_util::{XResult, util_time::*, util_msg::*}; +use rust_util::{XResult, util_time::*}; use tiny_encrypt::CmdEncrypt; use oss_util::*; use config_util::*;