undercroft

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit f0251ead597039cc36b957ef40506d3839c884ce
parent dcb369be0a77342ccc02c4d0e78456733ac8187d
Author: Daniel Barlow <dan@telent.net>
Date:   Wed,  5 Aug 2026 21:28:11 +0100

convert yaml to json if called with Accept: application/json

Diffstat:
MCargo.lock | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
MCargo.toml | 3+++
MREADME.md | 6++++++
Msrc/main.rs | 51+++++++++++++++++++++++++++++++++++++++++++++++----
4 files changed, 117 insertions(+), 31 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -193,7 +193,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -657,38 +657,39 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", + "serde_derive", ] [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "itoa", "memchr", @@ -721,6 +722,19 @@ dependencies = [ ] [[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -742,22 +756,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] -name = "slops" -version = "0.1.0" -dependencies = [ - "axum", - "clap", - "clap_derive", - "hyper", - "hyper-util", - "log", - "rustls", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -797,6 +795,17 @@ dependencies = [ ] [[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -827,7 +836,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -908,12 +917,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] +name = "undercroft" +version = "0.1.0" +dependencies = [ + "axum", + "clap", + "clap_derive", + "hyper", + "hyper-util", + "log", + "rustls", + "serde", + "serde_json", + "serde_yaml", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml @@ -14,3 +14,6 @@ tokio-rustls = "0.26" tower-service = "0.3.2" log = "0.4.29" rustls = "0.23.38" +serde = { version = "1.0.229", features = ["derive"] } +serde_json = "1.0.151" +serde_yaml = "0.9.34" diff --git a/README.md b/README.md @@ -16,3 +16,9 @@ Then start the service: SOPS_AGE_KEY_FILE=sops-secrets.age \ undercroft --ca-certificate certs/ca.crt --certificate certs/server.crt --key private/server.key --bind localhost:3000 example-secrets.enc.yaml ``` + + +To test it, + +``` +curl --connect-to ::127.0.0.1:3000 --cert certs/client.crt --key private/client.key --cacert certs/ca.crt -v https://noetbook:3002 diff --git a/src/main.rs b/src/main.rs @@ -1,6 +1,7 @@ use axum::{ extract::Request, + http::{HeaderMap,HeaderValue}, routing::get, Router, http::StatusCode }; @@ -21,12 +22,15 @@ use rustls::{ server::WebPkiClientVerifier, RootCertStore }; +use serde_yaml::Value as YamlValue; use tower_service::Service; use clap::Parser; use log::{error,warn}; +const JSON_MIME : HeaderValue = HeaderValue::from_static("application/json"); + #[derive(Parser)] #[command(name = "undercroft")] #[command(version = "0.1")] @@ -46,8 +50,27 @@ struct Cli { secrets: String } +fn yaml_to_json(content: &str) -> Result<String, &str> { + match serde_yaml::from_str::<YamlValue>(content) { + Ok(v) => { + match serde_json::to_string_pretty(&v) { + Ok(s) => Ok(s), + Err(e) => { + println!("error {:?}", e); + Err("can't json") + } + } + }, + Err(e) => { + println!("error {:?}", e); + Err("can't yaml") + } + } +} + + -async fn send_it(secrets_path : String) -> (StatusCode, String) { +async fn send_it(headers: HeaderMap, secrets_path : String) -> (StatusCode, String) { let output = Command::new("sops") .arg("decrypt") .arg(secrets_path) @@ -55,9 +78,29 @@ async fn send_it(secrets_path : String) -> (StatusCode, String) { .expect("failed to execute process"); if output.status.success() { - (StatusCode::OK, String::from_utf8(output.stdout).expect("bad utf8")) + let body = String::from_utf8(output.stdout).expect("bad utf8"); + match headers.get("accept") { + Some(ctype) if ctype == &JSON_MIME => { + // this is a cheat as we only look for a single mime type + // and we only look in the first Accept header, so you + // can easily not get json if you ask for any other content-type + // as well + + if let Ok(j) = yaml_to_json(&body) { + (StatusCode::OK, j) + } else { + (StatusCode::INTERNAL_SERVER_ERROR, "serialize error".to_string()) + } + } + _ => { + (StatusCode::OK, body) + } + } } else { - (StatusCode::INTERNAL_SERVER_ERROR, String::from_utf8(output.stderr).expect("bad utf8")) + println!("error calling sops: {:}", + String::from_utf8(output.stderr).expect("bad utf8")); + (StatusCode::INTERNAL_SERVER_ERROR, + "error calling sops".to_string()) } } @@ -111,7 +154,7 @@ async fn main() { println!("listening on {}", addr); - let handler = move || send_it(cli.secrets) ; + let handler = move |headers: HeaderMap| send_it(headers, cli.secrets) ; let app = Router::new().route("/", get(handler)); loop {