fixed #748 - per-protocol external ports (#973)

This commit is contained in:
Eugene 2024-03-26 10:16:22 +01:00 committed by GitHub
parent 8896bb361e
commit 72236d004a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 68 additions and 21 deletions

View file

@ -9,6 +9,7 @@ use poem::http::{self, uri};
use poem_openapi::{Object, Union};
use serde::{Deserialize, Serialize};
pub use target::*;
use tracing::warn;
use uri::Scheme;
use url::Url;
use uuid::Uuid;
@ -102,13 +103,16 @@ pub enum SshHostKeyVerificationMode {
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct SSHConfig {
pub struct SshConfig {
#[serde(default = "_default_false")]
pub enable: bool,
#[serde(default = "_default_ssh_listen")]
pub listen: ListenEndpoint,
#[serde(default)]
pub external_port: Option<u16>,
#[serde(default = "_default_ssh_keys_path")]
pub keys: String,
@ -116,25 +120,35 @@ pub struct SSHConfig {
pub host_key_verification: SshHostKeyVerificationMode,
}
impl Default for SSHConfig {
impl Default for SshConfig {
fn default() -> Self {
SSHConfig {
SshConfig {
enable: false,
listen: _default_ssh_listen(),
keys: _default_ssh_keys_path(),
host_key_verification: Default::default(),
external_port: None,
}
}
}
impl SshConfig {
pub fn external_port(&self) -> u16 {
self.external_port.unwrap_or(self.listen.port())
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct HTTPConfig {
pub struct HttpConfig {
#[serde(default = "_default_false")]
pub enable: bool,
#[serde(default = "_default_http_listen")]
pub listen: ListenEndpoint,
#[serde(default)]
pub external_port: Option<u16>,
#[serde(default)]
pub certificate: String,
@ -151,11 +165,12 @@ pub struct HTTPConfig {
pub cookie_max_age: Duration,
}
impl Default for HTTPConfig {
impl Default for HttpConfig {
fn default() -> Self {
HTTPConfig {
HttpConfig {
enable: false,
listen: _default_http_listen(),
external_port: None,
certificate: "".to_owned(),
key: "".to_owned(),
trust_x_forwarded_headers: false,
@ -165,14 +180,23 @@ impl Default for HTTPConfig {
}
}
impl HttpConfig {
pub fn external_port(&self) -> u16 {
self.external_port.unwrap_or(self.listen.port())
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct MySQLConfig {
pub struct MySqlConfig {
#[serde(default = "_default_false")]
pub enable: bool,
#[serde(default = "_default_mysql_listen")]
pub listen: ListenEndpoint,
#[serde(default)]
pub external_port: Option<u16>,
#[serde(default)]
pub certificate: String,
@ -180,17 +204,24 @@ pub struct MySQLConfig {
pub key: String,
}
impl Default for MySQLConfig {
impl Default for MySqlConfig {
fn default() -> Self {
MySQLConfig {
MySqlConfig {
enable: false,
listen: _default_mysql_listen(),
external_port: None,
certificate: "".to_owned(),
key: "".to_owned(),
}
}
}
impl MySqlConfig {
pub fn external_port(&self) -> u16 {
self.external_port.unwrap_or(self.listen.port())
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct RecordingsConfig {
#[serde(default = "_default_false")]
@ -263,13 +294,13 @@ pub struct WarpgateConfigStore {
pub database_url: Secret<String>,
#[serde(default)]
pub ssh: SSHConfig,
pub ssh: SshConfig,
#[serde(default)]
pub http: HTTPConfig,
pub http: HttpConfig,
#[serde(default)]
pub mysql: MySQLConfig,
pub mysql: MySqlConfig,
#[serde(default)]
pub log: LogConfig,
@ -370,4 +401,13 @@ impl WarpgateConfig {
};
Url::parse(&url).map_err(WarpgateError::UrlParse)
}
pub fn validate(&self) {
if let Some(ref ext) = self.store.external_host {
if ext.contains(':') {
warn!("Looks like your `external_host` config option contains a port - it will be ignored.");
warn!("Set the external port via the `http.external_port`, `ssh.external_port` or `mysql.external_port` options.");
}
}
}
}

View file

@ -33,6 +33,10 @@ enum InstanceInfoResponse {
Ok(Json<Info>),
}
fn strip_port(host: &str) -> Option<&str> {
host.split(':').next()
}
#[OpenApi]
impl Api {
#[oai(path = "/info", method = "get", operation_id = "get_info")]
@ -47,8 +51,10 @@ impl Api {
.store
.external_host
.as_deref()
.or_else(|| req.header(http::header::HOST))
.and_then(strip_port)
.or_else(|| req.header(http::header::HOST).and_then(strip_port))
.or_else(|| req.original_uri().host());
Ok(InstanceInfoResponse::Ok(Json(Info {
version: env!("CARGO_PKG_VERSION").to_string(),
username: session.get_username(),
@ -61,17 +67,17 @@ impl Api {
ports: if session.is_authenticated() {
PortsInfo {
ssh: if config.store.ssh.enable {
Some(config.store.ssh.listen.port())
Some(config.store.ssh.external_port())
} else {
None
},
http: if config.store.http.enable {
Some(config.store.http.listen.port())
Some(config.store.http.external_port())
} else {
None
},
mysql: if config.store.mysql.enable {
Some(config.store.mysql.listen.port())
Some(config.store.mysql.external_port())
} else {
None
},

View file

@ -14,7 +14,7 @@ use uuid::Uuid;
use warpgate_common::helpers::fs::{secure_directory, secure_file};
use warpgate_common::helpers::hash::hash_password;
use warpgate_common::{
HTTPConfig, ListenEndpoint, MySQLConfig, SSHConfig, Secret, UserAuthCredential,
HttpConfig, ListenEndpoint, MySqlConfig, SshConfig, Secret, UserAuthCredential,
UserPasswordCredential, UserRequireCredentialsPolicy, WarpgateConfigStore, WarpgateError,
};
use warpgate_core::consts::{BUILTIN_ADMIN_ROLE_NAME, BUILTIN_ADMIN_USERNAME};
@ -74,7 +74,7 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {
let theme = ColorfulTheme::default();
let mut store = WarpgateConfigStore {
http: HTTPConfig {
http: HttpConfig {
enable: true,
..Default::default()
},
@ -141,7 +141,7 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {
if !is_docker() {
store.http.listen = prompt_endpoint(
"Endpoint to listen for HTTP connections on",
HTTPConfig::default().listen,
HttpConfig::default().listen,
);
}
}
@ -168,7 +168,7 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {
if store.ssh.enable {
store.ssh.listen = prompt_endpoint(
"Endpoint to listen for SSH connections on",
SSHConfig::default().listen,
SshConfig::default().listen,
);
}
}
@ -191,7 +191,7 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {
if store.mysql.enable {
store.mysql.listen = prompt_endpoint(
"Endpoint to listen for MySQL connections on",
MySQLConfig::default().listen,
MySqlConfig::default().listen,
);
}
}

View file

@ -33,6 +33,7 @@ pub fn load_config(path: &Path, secure: bool) -> Result<WarpgateConfig> {
};
info!("Using config: {path:?}");
config.validate();
Ok(config)
}