Skip to content

Commit

Permalink
fix(dgw): better status code for unreachable KDC server (#618)
Browse files Browse the repository at this point in the history
  • Loading branch information
CBenoit authored Dec 20, 2023
1 parent 4c4df60 commit d0cbd7f
Showing 1 changed file with 42 additions and 18 deletions.
60 changes: 42 additions & 18 deletions devolutions-gateway/src/api/kdc_proxy.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use std::io;
use std::net::SocketAddr;

use axum::extract::{self, ConnectInfo, State};
use axum::http::StatusCode;
use axum::routing::post;
use axum::Router;
use picky_krb::messages::KdcProxyMessage;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpStream, UdpSocket};

use crate::http::HttpError;
use crate::http::{HttpError, HttpErrorBuilder};
use crate::token::AccessTokenClaims;
use crate::DgwState;

Expand Down Expand Up @@ -81,56 +83,54 @@ async fn kdc_proxy(
trace!("Connecting to KDC server located at {kdc_addr} using protocol {protocol}...");

let kdc_reply_message = if protocol == "tcp" {
#[allow(clippy::redundant_closure)] // We get a better caller location for the error by using a closure.
let mut connection = TcpStream::connect(kdc_addr.as_addr())
.await
.map_err(HttpError::internal().with_msg("unable to connect to KDC server").err())?;
.map_err(|e| unable_to_reach_kdc_server_err(e))?;

trace!("Connected! Forwarding KDC message...");

connection
.write_all(&kdc_proxy_message.kerb_message.0 .0)
.await
.map_err(
HttpError::internal()
HttpError::bad_gateway()
.with_msg("unable to send the message to the KDC server")
.err(),
)?;

trace!("Reading KDC reply...");

read_kdc_reply_message(&mut connection)
.await
.map_err(HttpError::internal().with_msg("unable to read KDC reply message").err())?
read_kdc_reply_message(&mut connection).await.map_err(
HttpError::bad_gateway()
.with_msg("unable to read KDC reply message")
.err(),
)?
} else {
// we assume that ticket length is not greater than 2048
let mut buf = [0; 2048];

let port = portpicker::pick_unused_port().ok_or_else(|| HttpError::internal().msg("No free ports"))?;
let port = portpicker::pick_unused_port().ok_or_else(|| HttpError::internal().msg("no free ports"))?;

trace!("Binding UDP listener to 127.0.0.1:{port}...");

let udp_socket = UdpSocket::bind(("127.0.0.1", port)).await.map_err(
HttpError::internal()
.with_msg("unable to send the message to the KDC server")
.err(),
)?;
let udp_socket = UdpSocket::bind(("127.0.0.1", port))
.await
.map_err(HttpError::internal().with_msg("unable to bind UDP socket").err())?;

trace!("Binded! Forwarding KDC message...");

// first 4 bytes contains message length. we don't need it for UDP
#[allow(clippy::redundant_closure)] // We get a better caller location for the error by using a closure.
udp_socket
.send_to(&kdc_proxy_message.kerb_message.0 .0[4..], kdc_addr.as_addr())
.await
.map_err(
HttpError::internal()
.with_msg("unable to send the message to the KDC server")
.err(),
)?;
.map_err(|e| unable_to_reach_kdc_server_err(e))?;

trace!("Reading KDC reply...");

let n = udp_socket.recv(&mut buf).await.map_err(
HttpError::internal()
HttpError::bad_gateway()
.with_msg("unable to read reply from the KDC server")
.err(),
)?;
Expand All @@ -156,3 +156,27 @@ async fn read_kdc_reply_message(connection: &mut TcpStream) -> std::io::Result<V
connection.read_exact(&mut buf[4..]).await?;
Ok(buf)
}

#[track_caller]
fn unable_to_reach_kdc_server_err(error: io::Error) -> HttpError {
use io::ErrorKind;

let builder = match error.kind() {
ErrorKind::TimedOut => HttpErrorBuilder::new(StatusCode::GATEWAY_TIMEOUT),
ErrorKind::ConnectionRefused => HttpError::bad_gateway(),
ErrorKind::ConnectionAborted => HttpError::bad_gateway(),
ErrorKind::ConnectionReset => HttpError::bad_gateway(),
ErrorKind::BrokenPipe => HttpError::bad_gateway(),
ErrorKind::OutOfMemory => HttpError::internal(),
// FIXME: once stabilized use new IO error variants
// - https://github.com/rust-lang/rust/pull/106375
// - https://github.com/rust-lang/rust/issues/86442
// ErrorKind::NetworkDown => HttpErrorBuilder::new(StatusCode::SERVICE_UNAVAILABLE),
// ErrorKind::NetworkUnreachable => HttpError::bad_gateway(),
// ErrorKind::HostUnreachable => HttpError::bad_gateway(),
// TODO: When the above is applied, we can return an internal error in the fallback branch.
_ => HttpError::bad_gateway(),
};

builder.with_msg("unable to reach KDC server").err()(error)
}

0 comments on commit d0cbd7f

Please sign in to comment.