use reqwest;
use serde::{Deserialize, Serialize};
use serde_json;
use std::env;
use std::fs;
use std::io::prelude::*;
use std::path::Path;
use crate::errors::*;
const KUBERNETES_TOKEN_PATH: &str =
"/var/run/secrets/kubernetes.io/serviceaccount/token";
fn kubernetes_jwt() -> Result<String> {
fs::read_to_string(KUBERNETES_TOKEN_PATH).map_err(|err| Error::FileRead {
path: Path::new(KUBERNETES_TOKEN_PATH).to_owned(),
cause: Box::new(err.into()),
})
}
#[derive(Debug, Serialize)]
struct VaultKubernetesLogin<'a> {
role: &'a str,
jwt: &'a str,
}
#[derive(Debug, Deserialize)]
struct VaultAuthResponse {
auth: VaultAuth,
}
#[derive(Debug, Deserialize)]
struct VaultAuth {
client_token: String,
}
fn auth(
client: reqwest::Client,
addr: &reqwest::Url,
auth_path: &str,
role: &str,
jwt: &str,
) -> Result<String> {
let url = addr.join(&format!("v1/auth/{}/login", auth_path))?;
let payload = VaultKubernetesLogin { role, jwt };
let mkerr = |err| Error::Url {
url: url.to_owned(),
cause: Box::new(err),
};
let mut res = client
.post(url.clone())
.header("Connection", "close")
.body(serde_json::to_vec(&payload)?)
.send()
.map_err(|err| (&mkerr)(Error::Other(err.into())))?;
let mut body = String::new();
res.read_to_string(&mut body)?;
if res.status().is_success() {
let auth_res: VaultAuthResponse = serde_json::from_str(&body)?;
Ok(auth_res.auth.client_token)
} else {
let status = res.status().to_owned();
Err(mkerr(Error::UnexpectedHttpStatus {
status,
body: body.trim().to_owned(),
}))
}
}
pub(crate) fn vault_kubernetes_token(addr: &reqwest::Url) -> Result<Option<String>> {
let role = match env::var("VAULT_KUBERNETES_ROLE") {
Ok(role) => role,
Err(_) => return Ok(None),
};
let auth_path = env::var("VAULT_KUBERNETES_AUTH_PATH")
.unwrap_or_else(|_| "kubernetes".to_owned());
let jwt = kubernetes_jwt()?;
let client = reqwest::Client::new();
Ok(Some(auth(client, addr, &auth_path, &role, &jwt)?))
}