mirror of
https://github.com/warp-tech/warpgate.git
synced 2024-09-20 06:46:17 +08:00
Credential policies
This commit is contained in:
parent
93a1259c38
commit
890c5d8b5a
|
@ -127,12 +127,20 @@ pub enum UserAuthCredential {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub struct UserRequireCredentialsPolicy {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub http: Option<Vec<String>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub ssh: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub credentials: Vec<UserAuthCredential>,
|
pub credentials: Vec<UserAuthCredential>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub require: Option<Vec<String>>,
|
pub require: Option<UserRequireCredentialsPolicy>,
|
||||||
pub roles: Vec<String>,
|
pub roles: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@ use super::ConfigProvider;
|
||||||
use crate::helpers::hash::verify_password_hash;
|
use crate::helpers::hash::verify_password_hash;
|
||||||
use crate::helpers::otp::verify_totp;
|
use crate::helpers::otp::verify_totp;
|
||||||
use crate::{
|
use crate::{
|
||||||
AuthCredential, AuthResult, Target, User, UserAuthCredential, UserSnapshot, WarpgateConfig,
|
AuthCredential, AuthResult, ProtocolName, Target, User, UserAuthCredential, UserSnapshot,
|
||||||
|
WarpgateConfig,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
@ -71,6 +72,7 @@ impl ConfigProvider for FileConfigProvider {
|
||||||
&mut self,
|
&mut self,
|
||||||
username: &str,
|
username: &str,
|
||||||
credentials: &[AuthCredential],
|
credentials: &[AuthCredential],
|
||||||
|
protocol: ProtocolName,
|
||||||
) -> Result<AuthResult> {
|
) -> Result<AuthResult> {
|
||||||
if credentials.is_empty() {
|
if credentials.is_empty() {
|
||||||
return Ok(AuthResult::Rejected);
|
return Ok(AuthResult::Rejected);
|
||||||
|
@ -159,8 +161,16 @@ impl ConfigProvider for FileConfigProvider {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
match user.require {
|
if let Some(ref policy) = user.require {
|
||||||
Some(ref required_kinds) => {
|
let required_kinds = match protocol {
|
||||||
|
"SSH" => &policy.ssh,
|
||||||
|
"HTTP" => &policy.http,
|
||||||
|
_ => {
|
||||||
|
error!(%protocol, "Unkown protocol");
|
||||||
|
return Ok(AuthResult::Rejected);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Some(required_kinds) = required_kinds {
|
||||||
let mut remaining_required_kinds = HashSet::new();
|
let mut remaining_required_kinds = HashSet::new();
|
||||||
remaining_required_kinds.extend(required_kinds);
|
remaining_required_kinds.extend(required_kinds);
|
||||||
for kind in required_kinds {
|
for kind in required_kinds {
|
||||||
|
@ -181,14 +191,15 @@ impl ConfigProvider for FileConfigProvider {
|
||||||
return Ok(AuthResult::Rejected);
|
return Ok(AuthResult::Rejected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => Ok(if !valid_credentials.is_empty() {
|
|
||||||
AuthResult::Accepted {
|
|
||||||
username: user.username.clone(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
AuthResult::Rejected
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(if !valid_credentials.is_empty() {
|
||||||
|
AuthResult::Accepted {
|
||||||
|
username: user.username.clone(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AuthResult::Rejected
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn authorize_target(&mut self, username: &str, target_name: &str) -> Result<bool> {
|
async fn authorize_target(&mut self, username: &str, target_name: &str) -> Result<bool> {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
mod file;
|
mod file;
|
||||||
use crate::{Secret, Target, UserSnapshot};
|
use crate::{ProtocolName, Secret, Target, UserSnapshot};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
@ -36,6 +36,7 @@ pub trait ConfigProvider {
|
||||||
&mut self,
|
&mut self,
|
||||||
username: &str,
|
username: &str,
|
||||||
credentials: &[AuthCredential],
|
credentials: &[AuthCredential],
|
||||||
|
protocol: ProtocolName,
|
||||||
) -> Result<AuthResult>;
|
) -> Result<AuthResult>;
|
||||||
|
|
||||||
async fn authorize_target(&mut self, username: &str, target: &str) -> Result<bool>;
|
async fn authorize_target(&mut self, username: &str, target: &str) -> Result<bool>;
|
||||||
|
|
|
@ -64,7 +64,7 @@ impl Api {
|
||||||
let result = {
|
let result = {
|
||||||
let mut config_provider = services.config_provider.lock().await;
|
let mut config_provider = services.config_provider.lock().await;
|
||||||
config_provider
|
config_provider
|
||||||
.authorize(&body.username, &credentials)
|
.authorize(&body.username, &credentials, crate::common::PROTOCOL_NAME)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.context("Failed to authorize user"))?
|
.map_err(|e| e.context("Failed to authorize user"))?
|
||||||
};
|
};
|
||||||
|
|
|
@ -952,7 +952,7 @@ impl ServerSession {
|
||||||
.config_provider
|
.config_provider
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.authorize(username, &self.credentials)
|
.authorize(username, &self.credentials, crate::PROTOCOL_NAME)
|
||||||
.await?
|
.await?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,18 @@ pub fn load_config(path: &Path, secure: bool) -> Result<WarpgateConfig> {
|
||||||
secure_file(path).context("Could not secure config")?;
|
secure_file(path).context("Could not secure config")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let store: WarpgateConfigStore = Config::builder()
|
let mut store: serde_yaml::Value = Config::builder()
|
||||||
.add_source(File::from(path))
|
.add_source(File::from(path))
|
||||||
.add_source(Environment::with_prefix("WARPGATE"))
|
.add_source(Environment::with_prefix("WARPGATE"))
|
||||||
.build()
|
.build()
|
||||||
.context("Could not load config")?
|
.context("Could not load config")?
|
||||||
.try_deserialize()
|
.try_deserialize()
|
||||||
.context("Could not parse config")?;
|
.context("Could not parse YAML")?;
|
||||||
|
|
||||||
|
check_and_migrate_config(&mut store);
|
||||||
|
|
||||||
|
let store: WarpgateConfigStore =
|
||||||
|
serde_yaml::from_value(store).context("Could not load config")?;
|
||||||
|
|
||||||
let config = WarpgateConfig {
|
let config = WarpgateConfig {
|
||||||
store,
|
store,
|
||||||
|
@ -35,6 +40,42 @@ pub fn load_config(path: &Path, secure: bool) -> Result<WarpgateConfig> {
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_and_migrate_config(store: &mut serde_yaml::Value) {
|
||||||
|
use serde_yaml::Value;
|
||||||
|
if let Some(map) = store.as_mapping_mut() {
|
||||||
|
if let Some(web_admin) = map.remove(&Value::String("web_admin".into())) {
|
||||||
|
warn!("The `web_admin` config section is deprecated. Rename it to `http`.");
|
||||||
|
map.insert(Value::String("http".into()), web_admin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(Value::Sequence(ref mut users)) = map.get_mut(&Value::String("users".into())) {
|
||||||
|
for user in users {
|
||||||
|
if let Value::Mapping(ref mut user) = user {
|
||||||
|
if let Some(new_require) = match user.get(&Value::String("require".into())) {
|
||||||
|
Some(Value::Sequence(ref old_requires)) => Some(Value::Mapping(
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
Value::String("ssh".into()),
|
||||||
|
Value::Sequence(old_requires.clone()),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Value::String("http".into()),
|
||||||
|
Value::Sequence(old_requires.clone()),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
)),
|
||||||
|
x => x.cloned(),
|
||||||
|
} {
|
||||||
|
user.insert(Value::String("require".into()), new_require);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn watch_config<P: AsRef<Path>>(
|
pub async fn watch_config<P: AsRef<Path>>(
|
||||||
path: P,
|
path: P,
|
||||||
config: Arc<Mutex<WarpgateConfig>>,
|
config: Arc<Mutex<WarpgateConfig>>,
|
||||||
|
|
Loading…
Reference in a new issue