mirror of
https://github.com/warp-tech/warpgate.git
synced 2024-09-20 14:56:18 +08:00
http tls redirect following
This commit is contained in:
parent
0808837f70
commit
2c6dbd01ac
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -4796,6 +4796,8 @@ dependencies = [
|
||||||
"qrcode",
|
"qrcode",
|
||||||
"rcgen",
|
"rcgen",
|
||||||
"sd-notify",
|
"sd-notify",
|
||||||
|
"sea-orm",
|
||||||
|
"serde_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"time 0.3.11",
|
"time 0.3.11",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -4805,6 +4807,7 @@ dependencies = [
|
||||||
"warpgate-admin",
|
"warpgate-admin",
|
||||||
"warpgate-common",
|
"warpgate-common",
|
||||||
"warpgate-core",
|
"warpgate-core",
|
||||||
|
"warpgate-db-entities",
|
||||||
"warpgate-protocol-http",
|
"warpgate-protocol-http",
|
||||||
"warpgate-protocol-mysql",
|
"warpgate-protocol-mysql",
|
||||||
"warpgate-protocol-ssh",
|
"warpgate-protocol-ssh",
|
||||||
|
|
|
@ -170,7 +170,7 @@ impl Default for MySQLConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
MySQLConfig {
|
MySQLConfig {
|
||||||
enable: false,
|
enable: false,
|
||||||
listen: _default_http_listen(),
|
listen: _default_mysql_listen(),
|
||||||
certificate: "".to_owned(),
|
certificate: "".to_owned(),
|
||||||
key: "".to_owned(),
|
key: "".to_owned(),
|
||||||
}
|
}
|
||||||
|
@ -225,12 +225,15 @@ pub enum ConfigProviderKind {
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct WarpgateConfigStore {
|
pub struct WarpgateConfigStore {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
pub targets: Vec<Target>,
|
pub targets: Vec<Target>,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
pub users: Vec<User>,
|
pub users: Vec<User>,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
pub roles: Vec<Role>,
|
pub roles: Vec<Role>,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
pub static BUILTIN_ADMIN_TARGET_NAME: &str = "warpgate:admin";
|
pub static BUILTIN_ADMIN_TARGET_NAME: &str = "warpgate:admin";
|
||||||
pub static BUILTIN_ADMIN_ROLE_NAME: &str = "warpgate:admin";
|
pub static BUILTIN_ADMIN_ROLE_NAME: &str = "warpgate:admin";
|
||||||
|
pub static BUILTIN_ADMIN_USERNAME: &str = "admin";
|
||||||
|
|
|
@ -101,7 +101,11 @@ fn construct_uri(req: &Request, options: &TargetHTTPOptions, websocket: bool) ->
|
||||||
.clone(),
|
.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let scheme = target_uri.scheme().context("No scheme in the URL")?;
|
let scheme = match options.tls.mode {
|
||||||
|
TlsMode::Disabled => &Scheme::HTTP,
|
||||||
|
TlsMode::Preferred => target_uri.scheme().context("No scheme in the URL")?,
|
||||||
|
TlsMode::Required => &Scheme::HTTPS,
|
||||||
|
};
|
||||||
uri = uri.scheme(scheme.clone());
|
uri = uri.scheme(scheme.clone());
|
||||||
|
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
|
@ -228,6 +232,23 @@ pub async fn proxy_normal_request(
|
||||||
if let TlsMode::Required = options.tls.mode {
|
if let TlsMode::Required = options.tls.mode {
|
||||||
client = client.https_only(true);
|
client = client.https_only(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client = client.redirect(reqwest::redirect::Policy::custom({
|
||||||
|
let tls_mode = options.tls.mode.clone();
|
||||||
|
let uri = uri.clone();
|
||||||
|
move |attempt| {
|
||||||
|
if tls_mode == TlsMode::Preferred
|
||||||
|
&& uri.scheme() == Some(&Scheme::HTTP)
|
||||||
|
&& attempt.url().scheme() == "https"
|
||||||
|
{
|
||||||
|
debug!("Following HTTP->HTTPS redirect");
|
||||||
|
attempt.follow()
|
||||||
|
} else {
|
||||||
|
attempt.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
if !options.tls.verify {
|
if !options.tls.verify {
|
||||||
client = client.danger_accept_invalid_certs(true);
|
client = client.danger_accept_invalid_certs(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,9 @@ futures = "0.3"
|
||||||
notify = "^5.0.0-beta.1"
|
notify = "^5.0.0-beta.1"
|
||||||
qrcode = "0.12"
|
qrcode = "0.12"
|
||||||
rcgen = {version = "0.9", features = ["zeroize"]}
|
rcgen = {version = "0.9", features = ["zeroize"]}
|
||||||
|
serde_json = "1.0"
|
||||||
serde_yaml = "0.8.23"
|
serde_yaml = "0.8.23"
|
||||||
|
sea-orm = { version = "^0.9", default-features = false }
|
||||||
time = "0.3"
|
time = "0.3"
|
||||||
tokio = {version = "1.20", features = ["tracing", "signal", "macros"]}
|
tokio = {version = "1.20", features = ["tracing", "signal", "macros"]}
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
@ -30,6 +32,7 @@ uuid = "1.0"
|
||||||
warpgate-admin = {version = "*", path = "../warpgate-admin"}
|
warpgate-admin = {version = "*", path = "../warpgate-admin"}
|
||||||
warpgate-common = {version = "*", path = "../warpgate-common"}
|
warpgate-common = {version = "*", path = "../warpgate-common"}
|
||||||
warpgate-core = {version = "*", path = "../warpgate-core"}
|
warpgate-core = {version = "*", path = "../warpgate-core"}
|
||||||
|
warpgate-db-entities = {version = "*", path = "../warpgate-db-entities"}
|
||||||
warpgate-protocol-http = {version = "*", path = "../warpgate-protocol-http"}
|
warpgate-protocol-http = {version = "*", path = "../warpgate-protocol-http"}
|
||||||
warpgate-protocol-mysql = {version = "*", path = "../warpgate-protocol-mysql"}
|
warpgate-protocol-mysql = {version = "*", path = "../warpgate-protocol-mysql"}
|
||||||
warpgate-protocol-ssh = {version = "*", path = "../warpgate-protocol-ssh"}
|
warpgate-protocol-ssh = {version = "*", path = "../warpgate-protocol-ssh"}
|
||||||
|
|
|
@ -6,16 +6,18 @@ use std::path::{Path, PathBuf};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use dialoguer::theme::ColorfulTheme;
|
use dialoguer::theme::ColorfulTheme;
|
||||||
use rcgen::generate_simple_self_signed;
|
use rcgen::generate_simple_self_signed;
|
||||||
|
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
|
||||||
use tracing::*;
|
use tracing::*;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use warpgate_common::helpers::fs::{secure_directory, secure_file};
|
use warpgate_common::helpers::fs::{secure_directory, secure_file};
|
||||||
use warpgate_common::helpers::hash::hash_password;
|
use warpgate_common::helpers::hash::hash_password;
|
||||||
use warpgate_common::{
|
use warpgate_common::{
|
||||||
HTTPConfig, ListenEndpoint, MySQLConfig, SSHConfig, Secret, User, UserAuthCredential,
|
HTTPConfig, ListenEndpoint, MySQLConfig, SSHConfig, Secret, UserAuthCredential,
|
||||||
UserPasswordCredential, WarpgateConfigStore,
|
UserPasswordCredential, UserRequireCredentialsPolicy, WarpgateConfigStore, WarpgateError,
|
||||||
};
|
};
|
||||||
use warpgate_core::consts::BUILTIN_ADMIN_ROLE_NAME;
|
use warpgate_core::consts::{BUILTIN_ADMIN_ROLE_NAME, BUILTIN_ADMIN_USERNAME};
|
||||||
use warpgate_core::Services;
|
use warpgate_core::Services;
|
||||||
|
use warpgate_db_entities::{Role, User, UserRoleAssignment};
|
||||||
|
|
||||||
use crate::config::load_config;
|
use crate::config::load_config;
|
||||||
|
|
||||||
|
@ -187,20 +189,10 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
let password = dialoguer::Password::with_theme(&theme)
|
let admin_password = dialoguer::Password::with_theme(&theme)
|
||||||
.with_prompt("Set a password for the Warpgate admin user")
|
.with_prompt("Set a password for the Warpgate admin user")
|
||||||
.interact()?;
|
.interact()?;
|
||||||
|
|
||||||
store.users.push(User {
|
|
||||||
id: Uuid::new_v4(),
|
|
||||||
username: "admin".into(),
|
|
||||||
credentials: vec![UserAuthCredential::Password(UserPasswordCredential {
|
|
||||||
hash: Secret::new(hash_password(&password)),
|
|
||||||
})],
|
|
||||||
credential_policy: None,
|
|
||||||
roles: vec![BUILTIN_ADMIN_ROLE_NAME.into()],
|
|
||||||
});
|
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
info!("Generated configuration:");
|
info!("Generated configuration:");
|
||||||
|
@ -211,10 +203,61 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {
|
||||||
info!("Saved into {}", cli.config.display());
|
info!("Saved into {}", cli.config.display());
|
||||||
|
|
||||||
let config = load_config(&cli.config, true)?;
|
let config = load_config(&cli.config, true)?;
|
||||||
Services::new(config.clone()).await?;
|
let services = Services::new(config.clone()).await?;
|
||||||
warpgate_protocol_ssh::generate_host_keys(&config)?;
|
warpgate_protocol_ssh::generate_host_keys(&config)?;
|
||||||
warpgate_protocol_ssh::generate_client_keys(&config)?;
|
warpgate_protocol_ssh::generate_client_keys(&config)?;
|
||||||
|
|
||||||
|
{
|
||||||
|
let db = services.db.lock().await;
|
||||||
|
|
||||||
|
let admin_role = Role::Entity::find()
|
||||||
|
.filter(Role::Column::Name.eq(BUILTIN_ADMIN_ROLE_NAME))
|
||||||
|
.all(&*db)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.ok_or(anyhow::anyhow!("Database inconsistent: no admin role"))?;
|
||||||
|
|
||||||
|
let admin_user = match User::Entity::find()
|
||||||
|
.filter(User::Column::Username.eq(BUILTIN_ADMIN_USERNAME))
|
||||||
|
.all(&*db)
|
||||||
|
.await?
|
||||||
|
.first()
|
||||||
|
{
|
||||||
|
Some(x) => x.to_owned(),
|
||||||
|
None => {
|
||||||
|
let values = User::ActiveModel {
|
||||||
|
id: Set(Uuid::new_v4()),
|
||||||
|
username: Set(BUILTIN_ADMIN_USERNAME.to_owned()),
|
||||||
|
credentials: Set(serde_json::to_value(vec![UserAuthCredential::Password(
|
||||||
|
UserPasswordCredential {
|
||||||
|
hash: Secret::new(hash_password(&admin_password)),
|
||||||
|
},
|
||||||
|
)])?),
|
||||||
|
credential_policy: Set(serde_json::to_value(
|
||||||
|
None::<UserRequireCredentialsPolicy>,
|
||||||
|
)?),
|
||||||
|
};
|
||||||
|
values.insert(&*db).await.map_err(WarpgateError::from)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if UserRoleAssignment::Entity::find()
|
||||||
|
.filter(UserRoleAssignment::Column::UserId.eq(admin_user.id))
|
||||||
|
.filter(UserRoleAssignment::Column::RoleId.eq(admin_role.id))
|
||||||
|
.all(&*db)
|
||||||
|
.await?
|
||||||
|
.is_empty()
|
||||||
|
{
|
||||||
|
let values = UserRoleAssignment::ActiveModel {
|
||||||
|
user_id: Set(admin_user.id),
|
||||||
|
role_id: Set(admin_role.id),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
values.insert(&*db).await.map_err(WarpgateError::from)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
info!("Generating a TLS certificate");
|
info!("Generating a TLS certificate");
|
||||||
let cert = generate_simple_self_signed(vec![
|
let cert = generate_simple_self_signed(vec![
|
||||||
|
|
|
@ -32,12 +32,7 @@ pub fn load_config(path: &Path, secure: bool) -> Result<WarpgateConfig> {
|
||||||
paths_relative_to: path.parent().context("FS root reached")?.to_path_buf(),
|
paths_relative_to: path.parent().context("FS root reached")?.to_path_buf(),
|
||||||
};
|
};
|
||||||
|
|
||||||
info!(
|
info!("Using config: {path:?}");
|
||||||
"Using config: {path:?} (users: {}, targets: {}, roles: {})",
|
|
||||||
config.store.users.len(),
|
|
||||||
config.store.targets.len(),
|
|
||||||
config.store.roles.len(),
|
|
||||||
);
|
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue