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:
| M | Cargo.lock | | | 88 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------ |
| M | Cargo.toml | | | 3 | +++ |
| M | README.md | | | 6 | ++++++ |
| M | src/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 {