routes: Add WireGuard configuration resource

The * GET /wireguard/config/<instance-id>* resource returns the
WireGuard client configuration assigned to the specified instance ID.
The resource contents are stored in the Kubernetes Secret, in a data
field named `wireguard-config`.  The contents of this field are returned
directly as a string, without any transformation.  Thus, the value must
be a complete, valid WireGuard configuration document.  Instances will
fetch and save this configuration when they first launch, to configure
their access to the VPN.
master
Dustin 2022-10-02 17:47:25 -05:00
parent 3f17373624
commit 25524d5290
4 changed files with 68 additions and 0 deletions

View File

@ -257,6 +257,42 @@ pub async fn unassign_wireguard_config(
Ok(()) Ok(())
} }
/// Retrieve the WireGuard config assigned to the specified EC2 instance
///
/// This function finds the first WireGuard client configuration, stored as a
/// Kubernetes Secret resource, associated with the specified EC2 instance.
///
/// If multiple WireGuard configs are assigned to an EC2 instance, only the
/// first one returned by the Kubernetes list query is returned.
pub async fn get_wireguard_config(
instance_id: &str,
) -> Result<Option<String>, kube::Error> {
let client = Client::try_default().await?;
let secrets: Api<Secret> = Api::default_namespaced(client);
let lp = ListParams::default()
.fields("type=dynk8s.du5t1n.me/wireguard-config")
.labels(&format!("dynk8s.du5t1n.me/ec2-instance-id={}", instance_id));
for s in secrets.list(&lp).await? {
if let Some(data) = s.data {
match data.get("wireguard-config") {
Some(s) => match String::from_utf8(s.0.clone()) {
Ok(s) => return Ok(Some(s)),
Err(e) => {
error!("Invalid WireGuard configuration: {}", e);
}
},
None => {
error!(concat!(
"Invalid WireGuard configuration: ",
"missing wireguard-config property"
));
}
};
}
}
Ok(None)
}
/// Generate and store a bootstrap token for the specified EC2 instance /// Generate and store a bootstrap token for the specified EC2 instance
/// ///
/// This function generates a new bootstrap token and stores it as a Kubernetes /// This function generates a new bootstrap token and stores it as a Kubernetes

View File

@ -12,6 +12,7 @@ fn rocket() -> _ {
"/", "/",
rocket::routes![ rocket::routes![
routes::health::get_health, routes::health::get_health,
routes::wireguard::get_node_wireguard,
routes::sns::post_sns_notify, routes::sns::post_sns_notify,
routes::sns::get_sns_notify, routes::sns::get_sns_notify,
routes::sns::put_sns_notify, routes::sns::put_sns_notify,

View File

@ -1,3 +1,4 @@
//! Rocket route handlers //! Rocket route handlers
pub mod health; pub mod health;
pub mod sns; pub mod sns;
pub mod wireguard;

30
src/routes/wireguard.rs Normal file
View File

@ -0,0 +1,30 @@
use crate::k8s::get_wireguard_config;
#[rocket::get("/wireguard/config/<instance_id>")]
pub async fn get_node_wireguard(instance_id: String) -> Option<String> {
if let Ok(Some(token)) = get_wireguard_config(&instance_id).await {
Some(token)
} else {
None
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::rocket;
use rocket::http::Status;
use rocket::local::blocking::Client;
use rocket::uri;
#[test]
fn test_get_node_wireguard_404() {
let client = Client::tracked(rocket()).unwrap();
let res = client
.get(uri!(get_node_wireguard(
instance_id = "i-0a1b2c3d4e5f6f7f8"
)))
.dispatch();
assert_eq!(res.status(), Status::NotFound);
}
}