Skip to content

Commit a8c7f5d

Browse files
authored
Support listening on unix sockets (#9)
If `host` in the config fails to parse as an IP address it will trying to bind to a Unix socket. Tested with Caddy and cURL by doing cURL: `curl --unix-socket /tmp/calagopus.sock http://localhost/` Caddy: `caddy reverse-proxy --from :8080 --to unix//tmp/calagopus.sock`
1 parent 0b4657f commit a8c7f5d

File tree

4 files changed

+95
-60
lines changed

4 files changed

+95
-60
lines changed

application/src/commands/diagnostics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ pub async fn diagnostics(
187187
.trim_start_matches("http://"),
188188
"{redacted}",
189189
)
190-
.replace(&config.api.host.to_string(), "{redacted}")
190+
.replace(&config.api.host, "{redacted}")
191191
.replace(&config.system.sftp.bind_address.to_string(), "{redacted}");
192192

193193
if !config.api.ssl.cert.is_empty() {

application/src/config.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ use utoipa::ToSchema;
1919
fn app_name() -> String {
2020
"Pterodactyl".to_string()
2121
}
22-
fn api_host() -> std::net::IpAddr {
23-
std::net::IpAddr::from([0, 0, 0, 0])
22+
fn api_host() -> String {
23+
"0.0.0.0".to_string()
2424
}
2525
fn api_port() -> u16 {
2626
8080
@@ -440,8 +440,7 @@ nestify::nest! {
440440
#[schema(inline)]
441441
pub api: #[derive(ToSchema, Deserialize, Serialize, DefaultFromSerde)] #[serde(default)] pub struct Api {
442442
#[serde(default = "api_host")]
443-
#[schema(value_type = String)]
444-
pub host: std::net::IpAddr,
443+
pub host: String,
445444
#[serde(default = "api_port")]
446445
pub port: u16,
447446

application/src/main.rs

Lines changed: 90 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use anyhow::Context;
22
use axum::{
33
body::Body,
4-
extract::Request,
4+
extract::{ConnectInfo, Request},
55
http::{HeaderMap, HeaderValue, Method, Response, StatusCode},
66
middleware::Next,
77
response::IntoResponse,
@@ -590,74 +590,111 @@ async fn main() {
590590
}
591591
});
592592

593-
let address = SocketAddr::from((state.config.api.host, state.config.api.port));
593+
if let Ok(host) = state.config.api.host.parse::<std::net::IpAddr>() {
594+
let address = SocketAddr::from((host, state.config.api.port));
594595

595-
if config.api.ssl.enabled {
596-
tracing::info!("loading ssl certs");
596+
if config.api.ssl.enabled {
597+
tracing::info!("loading ssl certs");
597598

598-
let config = axum_server::tls_rustls::RustlsConfig::from_pem_file(
599-
config.api.ssl.cert.as_str(),
600-
config.api.ssl.key.as_str(),
601-
)
602-
.await
603-
.context("failed to load SSL certificate and key")
604-
.unwrap();
599+
let config = axum_server::tls_rustls::RustlsConfig::from_pem_file(
600+
config.api.ssl.cert.as_str(),
601+
config.api.ssl.key.as_str(),
602+
)
603+
.await
604+
.context("failed to load SSL certificate and key")
605+
.unwrap();
605606

606-
tracing::info!(
607-
"{} listening on {}",
608-
"https server".bright_red(),
609-
address.to_string().cyan(),
610-
);
607+
tracing::info!(
608+
"{} listening on {}",
609+
"https server".bright_red(),
610+
address.to_string().cyan(),
611+
);
611612

612-
match axum_server::bind_rustls(address, config)
613-
.serve(router.into_make_service_with_connect_info::<SocketAddr>())
614-
.await
615-
{
616-
Ok(_) => {}
617-
Err(err) => {
618-
if err.kind() == std::io::ErrorKind::AddrInUse {
619-
tracing::error!("failed to start https server (address already in use)");
620-
} else {
621-
tracing::error!("failed to start https server: {:?}", err,);
622-
}
613+
match axum_server::bind_rustls(address, config)
614+
.serve(router.into_make_service_with_connect_info::<SocketAddr>())
615+
.await
616+
{
617+
Ok(_) => {}
618+
Err(err) => {
619+
if err.kind() == std::io::ErrorKind::AddrInUse {
620+
tracing::error!("failed to start https server (address already in use)");
621+
} else {
622+
tracing::error!("failed to start https server: {:?}", err,);
623+
}
623624

624-
std::process::exit(1);
625+
std::process::exit(1);
626+
}
625627
}
626-
}
627-
} else {
628-
tracing::info!(
629-
"{} listening on {}",
630-
"http server".bright_red(),
631-
address.to_string().cyan(),
632-
);
628+
} else {
629+
tracing::info!(
630+
"{} listening on {}",
631+
"http server".bright_red(),
632+
address.to_string().cyan(),
633+
);
633634

634-
match axum::serve(
635-
match tokio::net::TcpListener::bind(address).await {
636-
Ok(listener) => listener,
635+
match axum::serve(
636+
match tokio::net::TcpListener::bind(address).await {
637+
Ok(listener) => listener,
638+
Err(err) => {
639+
if err.kind() == std::io::ErrorKind::AddrInUse {
640+
tracing::error!("failed to start http server (address already in use)");
641+
std::process::exit(1);
642+
} else {
643+
tracing::error!("failed to start http server: {:?}", err);
644+
std::process::exit(1);
645+
}
646+
}
647+
},
648+
router.into_make_service_with_connect_info::<SocketAddr>(),
649+
)
650+
.await
651+
{
652+
Ok(_) => {}
637653
Err(err) => {
638654
if err.kind() == std::io::ErrorKind::AddrInUse {
639655
tracing::error!("failed to start http server (address already in use)");
640-
std::process::exit(1);
641656
} else {
642657
tracing::error!("failed to start http server: {:?}", err);
643-
std::process::exit(1);
644658
}
659+
660+
std::process::exit(1);
645661
}
646-
},
647-
router.into_make_service_with_connect_info::<SocketAddr>(),
648-
)
649-
.await
662+
}
663+
}
664+
} else {
665+
#[cfg(unix)]
650666
{
651-
Ok(_) => {}
652-
Err(err) => {
653-
if err.kind() == std::io::ErrorKind::AddrInUse {
654-
tracing::error!("failed to start http server (address already in use)");
655-
} else {
656-
tracing::error!("failed to start http server: {:?}", err);
657-
}
667+
let socket_path = &state.config.api.host;
658668

659-
std::process::exit(1);
660-
}
669+
tracing::info!(
670+
"{} listening on {}",
671+
"http server".bright_red(),
672+
socket_path.cyan(),
673+
);
674+
675+
let router = router.layer(axum::middleware::from_fn(
676+
|mut req: Request, next: Next| async move {
677+
req.extensions_mut().insert(ConnectInfo(SocketAddr::from((
678+
std::net::IpAddr::from([127, 0, 0, 1]),
679+
0,
680+
))));
681+
next.run(req).await
682+
},
683+
));
684+
685+
let _ = tokio::fs::remove_file(socket_path).await;
686+
687+
let listener = tokio::net::UnixListener::bind(socket_path).unwrap();
688+
689+
axum::serve(listener, router.into_make_service())
690+
.await
691+
.unwrap();
692+
}
693+
694+
#[cfg(not(unix))]
695+
{
696+
tracing::error!("unix socket support is only available on unix systems");
697+
std::process::exit(1);
661698
}
662699
}
663700
}

application/src/routes/api/update.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ mod post {
1717

1818
#[schema(inline)]
1919
api: Option<#[derive(ToSchema, Deserialize)] pub struct ApiPayload {
20-
#[schema(value_type = Option<String>)]
21-
host: Option<std::net::IpAddr>,
20+
host: Option<String>,
2221
port: Option<u16>,
2322

2423
#[schema(inline)]

0 commit comments

Comments
 (0)