Skip to content

Commit cce9fe0

Browse files
author
vsilent
committed
feat: Add SSH key validation endpoint POST /server/{id}/ssh-key/validate
1 parent 402a818 commit cce9fe0

File tree

2 files changed

+76
-0
lines changed

2 files changed

+76
-0
lines changed

src/routes/server/ssh_key.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,81 @@ pub async fn get_public_key(
229229
Ok(JsonResponse::build().set_item(Some(response)).ok("OK"))
230230
}
231231

232+
/// Response for SSH validation
233+
#[derive(Debug, Clone, Default, Serialize)]
234+
pub struct ValidateResponse {
235+
pub valid: bool,
236+
pub server_id: i32,
237+
pub srv_ip: Option<String>,
238+
pub message: String,
239+
}
240+
241+
/// Validate SSH connection for a server
242+
/// POST /server/{id}/ssh-key/validate
243+
///
244+
/// This endpoint validates that:
245+
/// 1. The server exists and belongs to the user
246+
/// 2. The SSH key is active
247+
/// 3. The key can be retrieved from Vault
248+
///
249+
/// Note: This does not actually test the SSH connection to the remote server
250+
/// (that would require an SSH client library). It validates the key is ready for use.
251+
#[tracing::instrument(name = "Validate SSH key for server.")]
252+
#[post("/{id}/ssh-key/validate")]
253+
pub async fn validate_key(
254+
path: web::Path<(i32,)>,
255+
user: web::ReqData<Arc<models::User>>,
256+
pg_pool: web::Data<PgPool>,
257+
vault_client: web::Data<VaultClient>,
258+
) -> Result<impl Responder> {
259+
let server_id = path.0;
260+
let server = verify_server_ownership(pg_pool.get_ref(), server_id, &user.id).await?;
261+
262+
// Check if server has an active key
263+
if server.key_status != "active" {
264+
let response = ValidateResponse {
265+
valid: false,
266+
server_id,
267+
srv_ip: server.srv_ip.clone(),
268+
message: format!("SSH key status is '{}', not active", server.key_status),
269+
};
270+
return Ok(JsonResponse::build()
271+
.set_item(Some(response))
272+
.ok("Validation failed"));
273+
}
274+
275+
// Verify we can fetch the key from Vault
276+
match vault_client
277+
.get_ref()
278+
.fetch_ssh_public_key(&user.id, server_id)
279+
.await
280+
{
281+
Ok(_public_key) => {
282+
let response = ValidateResponse {
283+
valid: true,
284+
server_id,
285+
srv_ip: server.srv_ip.clone(),
286+
message: "SSH key is valid and ready for connection".to_string(),
287+
};
288+
Ok(JsonResponse::build()
289+
.set_item(Some(response))
290+
.ok("SSH key validated successfully"))
291+
}
292+
Err(e) => {
293+
tracing::warn!("Failed to fetch SSH key from Vault during validation: {}", e);
294+
let response = ValidateResponse {
295+
valid: false,
296+
server_id,
297+
srv_ip: server.srv_ip.clone(),
298+
message: "SSH key could not be retrieved from secure storage".to_string(),
299+
};
300+
Ok(JsonResponse::build()
301+
.set_item(Some(response))
302+
.ok("Validation failed"))
303+
}
304+
}
305+
}
306+
232307
/// Delete SSH key for a server (disconnect)
233308
/// DELETE /server/{id}/ssh-key
234309
#[tracing::instrument(name = "Delete SSH key for server.")]

src/startup.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ pub async fn run(
254254
.service(crate::routes::server::ssh_key::generate_key)
255255
.service(crate::routes::server::ssh_key::upload_key)
256256
.service(crate::routes::server::ssh_key::get_public_key)
257+
.service(crate::routes::server::ssh_key::validate_key)
257258
.service(crate::routes::server::ssh_key::delete_key),
258259
)
259260
.service(

0 commit comments

Comments
 (0)