Skip to content

Commit

Permalink
feat(secrets-vault): Add Secrets Server implementation for Vault
Browse files Browse the repository at this point in the history
Signed-off-by: Joonas Bergius <joonas@cosmonic.com>
  • Loading branch information
joonas committed Jul 10, 2024
1 parent fda93b6 commit 4e727a7
Show file tree
Hide file tree
Showing 7 changed files with 1,063 additions and 0 deletions.
35 changes: 35 additions & 0 deletions .github/workflows/secrets-vault.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Secrets Vault

permissions:
contents: read

on:
push:
branches:
- "main"
pull_request:
branches:
- "main"

defaults:
run:
shell: bash
working-directory: ./secrets/secrets-vault

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Lint
run: |
cargo clippy -- --no-deps
- name: Test
run: |
cargo test
- name: Build
run: |
cargo build --release
28 changes: 28 additions & 0 deletions secrets/secrets-vault/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "secrets-vault"
version = "0.1.0"
edition = "2021"

[dependencies]
anyhow = { version = "1.0.86", default-features = false, features = ["std"] }
async-nats = { version = "0.33", default-features = false, features = ["server_2_10"] }
axum = { version = "0.7.5", default-features = false, features = ["http1", "json", "tokio", "tracing"] }
bytes = { version = "1", default-features = false }
clap = { version = "4.5.4", features = ["derive", "env", "string"] }
ed25519-dalek = { version = "2.1.1", features = ["alloc", "pkcs8"] }
futures = { version = "0.3.30", default-features = false, features = [] }
jsonwebtoken = { version = "9.3.0" }
nkeys = { git = "https://github.com/wasmcloud/nkeys.git", features = ["jwk", "xkeys"], rev = "7e52c00793bd1c10a0fb8ad17acdf6e6e1f76ae6" }
nkeys_041 = { package = "nkeys", version = "0.4.1", features = ["xkeys"] }
serde = { version = "1.0.203", default-features = false, features = ["std"] }
serde_json = { version = "1.0.117", default-features = false, features = ["std"] }
tokio = { version = "1.38.0", default-features = false, features = ["full"] }
tracing = { version = "0.1.40", default-features = false, features = [] }
tracing-subscriber = { version = "0.3.18", default-features = false, features = ["fmt"] }
vaultrs = { version = "0.7.2", default-features = false, features = ["rustls"] }
wasmcloud-secrets-client = { version = "0.1.0", git = "https://github.com/wasmcloud/wasmCloud.git", branch = "feat/host-secrets-impl" }
wasmcloud-secrets-types = { version = "0.1.0", git = "https://github.com/wasmcloud/wasmCloud.git", branch = "feat/host-secrets-impl" }
wascap = { version = "0.15.0" }

[dev-dependencies]
testcontainers = { version = "0.20.0", default-features = false }
7 changes: 7 additions & 0 deletions secrets/secrets-vault/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# secrets-vault

[wasmCloud Secrets][wasmcloud-secrets] Server implementation for HashiCorp Vault that uses the [JWT Auth method][vault-jwt-auth] to fetch secrets stored in the [KV Secrets Engine - version][vault-kv-secrets].

[vault-jwt-auth]: https://developer.hashicorp.com/vault/docs/auth/jwt
[vault-kv-secrets]: https://developer.hashicorp.com/vault/docs/secrets/kv/kv-v2
[wasmcloud-secrets]: https://github.com/wasmCloud/wasmCloud/issues/2190
58 changes: 58 additions & 0 deletions secrets/secrets-vault/src/jwks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use anyhow::Result;
use axum::{extract::State, http::StatusCode, routing::get, Json, Router};
use nkeys::{JsonWebKey, KeyPair};
use serde::{Deserialize, Serialize};
use std::{net::SocketAddrV4, sync::Arc};

#[derive(Debug)]
pub(crate) struct VaultSecretsJwksServer {
keys: Vec<JsonWebKey>,
listen_address: SocketAddrV4,
}

struct SharedState {
keys: Vec<JsonWebKey>,
}

#[derive(Debug, Serialize, Deserialize)]
struct JwksResponse {
keys: Vec<JsonWebKey>,
}

impl VaultSecretsJwksServer {
pub fn new(nkeys: Vec<KeyPair>, listen_address: SocketAddrV4) -> Result<Self> {
let mut keys = vec![];
for kp in nkeys {
keys.push(JsonWebKey::try_from(kp)?);
}
Ok(Self {
keys,
listen_address,
})
}

pub async fn serve(&self) -> Result<()> {
let state = Arc::new(SharedState {
keys: self.keys.clone(),
});
let app = Router::new()
.route("/.well-known/keys", get(handle_well_known_keys))
.with_state(state);

let listener = tokio::net::TcpListener::bind(self.listen_address).await?;
axum::serve(listener, app).await?;

Ok(())
}
}

async fn handle_well_known_keys(
State(state): State<Arc<SharedState>>,
) -> (StatusCode, Json<JwksResponse>) {
(
StatusCode::OK,
Json(JwksResponse {
keys: state.keys.clone(),
}),
)
}
Loading

0 comments on commit 4e727a7

Please sign in to comment.