mirror of
https://github.com/warp-tech/warpgate.git
synced 2024-09-20 06:46:17 +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",
|
||||
"rcgen",
|
||||
"sd-notify",
|
||||
"sea-orm",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"time 0.3.11",
|
||||
"tokio",
|
||||
|
@ -4805,6 +4807,7 @@ dependencies = [
|
|||
"warpgate-admin",
|
||||
"warpgate-common",
|
||||
"warpgate-core",
|
||||
"warpgate-db-entities",
|
||||
"warpgate-protocol-http",
|
||||
"warpgate-protocol-mysql",
|
||||
"warpgate-protocol-ssh",
|
||||
|
|
|
@ -170,7 +170,7 @@ impl Default for MySQLConfig {
|
|||
fn default() -> Self {
|
||||
MySQLConfig {
|
||||
enable: false,
|
||||
listen: _default_http_listen(),
|
||||
listen: _default_mysql_listen(),
|
||||
certificate: "".to_owned(),
|
||||
key: "".to_owned(),
|
||||
}
|
||||
|
@ -225,12 +225,15 @@ pub enum ConfigProviderKind {
|
|||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub struct WarpgateConfigStore {
|
||||
#[serde(default)]
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub targets: Vec<Target>,
|
||||
|
||||
#[serde(default)]
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub users: Vec<User>,
|
||||
|
||||
#[serde(default)]
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub roles: Vec<Role>,
|
||||
|
||||
#[serde(default)]
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub static BUILTIN_ADMIN_TARGET_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(),
|
||||
);
|
||||
|
||||
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());
|
||||
|
||||
#[allow(clippy::unwrap_used)]
|
||||
|
@ -228,6 +232,23 @@ pub async fn proxy_normal_request(
|
|||
if let TlsMode::Required = options.tls.mode {
|
||||
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 {
|
||||
client = client.danger_accept_invalid_certs(true);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,9 @@ futures = "0.3"
|
|||
notify = "^5.0.0-beta.1"
|
||||
qrcode = "0.12"
|
||||
rcgen = {version = "0.9", features = ["zeroize"]}
|
||||
serde_json = "1.0"
|
||||
serde_yaml = "0.8.23"
|
||||
sea-orm = { version = "^0.9", default-features = false }
|
||||
time = "0.3"
|
||||
tokio = {version = "1.20", features = ["tracing", "signal", "macros"]}
|
||||
tracing = "0.1"
|
||||
|
@ -30,6 +32,7 @@ uuid = "1.0"
|
|||
warpgate-admin = {version = "*", path = "../warpgate-admin"}
|
||||
warpgate-common = {version = "*", path = "../warpgate-common"}
|
||||
warpgate-core = {version = "*", path = "../warpgate-core"}
|
||||
warpgate-db-entities = {version = "*", path = "../warpgate-db-entities"}
|
||||
warpgate-protocol-http = {version = "*", path = "../warpgate-protocol-http"}
|
||||
warpgate-protocol-mysql = {version = "*", path = "../warpgate-protocol-mysql"}
|
||||
warpgate-protocol-ssh = {version = "*", path = "../warpgate-protocol-ssh"}
|
||||
|
|
|
@ -6,16 +6,18 @@ use std::path::{Path, PathBuf};
|
|||
use anyhow::Result;
|
||||
use dialoguer::theme::ColorfulTheme;
|
||||
use rcgen::generate_simple_self_signed;
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
|
||||
use tracing::*;
|
||||
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, User, UserAuthCredential,
|
||||
UserPasswordCredential, WarpgateConfigStore,
|
||||
HTTPConfig, ListenEndpoint, MySQLConfig, SSHConfig, Secret, UserAuthCredential,
|
||||
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_db_entities::{Role, User, UserRoleAssignment};
|
||||
|
||||
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")
|
||||
.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:");
|
||||
|
@ -211,10 +203,61 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {
|
|||
info!("Saved into {}", cli.config.display());
|
||||
|
||||
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_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");
|
||||
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(),
|
||||
};
|
||||
|
||||
info!(
|
||||
"Using config: {path:?} (users: {}, targets: {}, roles: {})",
|
||||
config.store.users.len(),
|
||||
config.store.targets.len(),
|
||||
config.store.roles.len(),
|
||||
);
|
||||
info!("Using config: {path:?}");
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue