Add book chapter + cli

This commit is contained in:
William Brown 2023-10-28 09:21:47 +10:00 committed by Firstyear
parent b80a3b271c
commit ecc46bb015
12 changed files with 59 additions and 19 deletions

View file

@ -76,6 +76,9 @@ permissions. All binds have the permissions of "anonymous" even if the anonymous
The exception is service accounts which can use api-tokens during an LDAP bind for elevated read
permissions.
The ability to bind with the POSIX password can be disabled to prevent password bruteforce attempts.
This does not prevent api-token binds.
### Filtering Objects
It is recommended that client applications filter accounts that can authenticate with
@ -183,6 +186,16 @@ these components and must only contain alphanumeric characters.
After the basedn is changed, the new value will take effect after a server restart. If you have a
replicated topology, you must restart all servers.
## Disable POSIX Password Binds
If you do not have applications that require LDAP password binds, then you should disable this
function to limit access.
```
kanidm system domain set-ldap-allow-unix-password-bind [true|false]
kanidm system domain set-ldap-allow-unix-password-bind -D admin false
```
## Examples
Given a default install with domain "idm.example.com" the configured LDAP DN will be

View file

@ -1832,6 +1832,14 @@ impl KanidmClient {
.await
}
pub async fn idm_set_ldap_allow_unix_password_bind(
&self,
enable: bool,
) -> Result<(), ClientError> {
self.perform_put_request("/v1/domain/_attr/ldap_allow_unix_pw_bind", vec![enable])
.await
}
pub async fn idm_domain_get_ssid(&self) -> Result<String, ClientError> {
self.perform_get_request("/v1/domain/_attr/domain_ssid")
.await

View file

@ -66,7 +66,6 @@ pub const ATTR_DN: &str = "dn";
pub const ATTR_DOMAIN_DISPLAY_NAME: &str = "domain_display_name";
pub const ATTR_DOMAIN_LDAP_BASEDN: &str = "domain_ldap_basedn";
pub const ATTR_DOMAIN_NAME: &str = "domain_name";
pub const ATTR_DOMAIN_LDAP_ALLOW_UNIX_PW_BIND: &str = "domain_ldap_allow_unix_pw_bind";
pub const ATTR_DOMAIN_SSID: &str = "domain_ssid";
pub const ATTR_DOMAIN_TOKEN_KEY: &str = "domain_token_key";
pub const ATTR_DOMAIN_UUID: &str = "domain_uuid";
@ -94,6 +93,7 @@ pub const ATTR_IPANTHASH: &str = "ipanthash";
pub const ATTR_IPASSHPUBKEY: &str = "ipasshpubkey";
pub const ATTR_JWS_ES256_PRIVATE_KEY: &str = "jws_es256_private_key";
pub const ATTR_LAST_MODIFIED_CID: &str = "last_modified_cid";
pub const ATTR_LDAP_ALLOW_UNIX_PW_BIND: &str = "ldap_allow_unix_pw_bind";
pub const ATTR_LEGALNAME: &str = "legalname";
pub const ATTR_LOGINSHELL: &str = "loginshell";
pub const ATTR_MAIL: &str = "mail";

View file

@ -1395,6 +1395,7 @@ lazy_static! {
Attribute::Es256PrivateKeyDer,
Attribute::FernetPrivateKeyStr,
Attribute::CookiePrivateKey,
Attribute::LdapAllowUnixPwBind,
],
modify_removed_attrs: vec![
Attribute::DomainDisplayName,
@ -1403,12 +1404,13 @@ lazy_static! {
Attribute::Es256PrivateKeyDer,
Attribute::CookiePrivateKey,
Attribute::FernetPrivateKeyStr,
Attribute::LdapAllowUnixPwBind,
],
modify_present_attrs: vec![
Attribute::DomainDisplayName,
Attribute::DomainLdapBasedn,
Attribute::DomainSsid,
Attribute::LdapAllowUnixPwBind,
],
..Default::default()
};

View file

@ -71,7 +71,6 @@ pub enum Attribute {
DomainDisplayName,
DomainLdapBasedn,
DomainName,
DomainLdapAllowUnixPwBind,
DomainSsid,
DomainTokenKey,
DomainUuid,
@ -96,6 +95,7 @@ pub enum Attribute {
IpaSshPubKey,
JwsEs256PrivateKey,
LastModifiedCid,
LdapAllowUnixPwBind,
/// An LDAP Compatible emailAddress
LdapEmailAddress,
/// An LDAP Compatible sshkeys virtual attribute
@ -248,7 +248,6 @@ impl TryFrom<String> for Attribute {
ATTR_DOMAIN_DISPLAY_NAME => Attribute::DomainDisplayName,
ATTR_DOMAIN_LDAP_BASEDN => Attribute::DomainLdapBasedn,
ATTR_DOMAIN_NAME => Attribute::DomainName,
ATTR_DOMAIN_LDAP_ALLOW_UNIX_PW_BIND => Attribute::DomainLdapAllowUnixPwBind,
ATTR_DOMAIN_SSID => Attribute::DomainSsid,
ATTR_DOMAIN_TOKEN_KEY => Attribute::DomainTokenKey,
ATTR_DOMAIN_UUID => Attribute::DomainUuid,
@ -273,6 +272,7 @@ impl TryFrom<String> for Attribute {
ATTR_IPASSHPUBKEY => Attribute::IpaSshPubKey,
ATTR_JWS_ES256_PRIVATE_KEY => Attribute::JwsEs256PrivateKey,
ATTR_LAST_MODIFIED_CID => Attribute::LastModifiedCid,
ATTR_LDAP_ALLOW_UNIX_PW_BIND => Attribute::LdapAllowUnixPwBind,
ATTR_LDAP_EMAIL_ADDRESS => Attribute::LdapEmailAddress,
ATTR_LDAP_KEYS => Attribute::LdapKeys,
ATTR_SSH_PUBLICKEY => Attribute::SshPublicKey,
@ -399,7 +399,6 @@ impl From<Attribute> for &'static str {
Attribute::Dn => ATTR_DN,
Attribute::Domain => ATTR_DOMAIN,
Attribute::DomainDisplayName => ATTR_DOMAIN_DISPLAY_NAME,
Attribute::DomainLdapAllowUnixPwBind => ATTR_DOMAIN_LDAP_ALLOW_UNIX_PW_BIND,
Attribute::DomainLdapBasedn => ATTR_DOMAIN_LDAP_BASEDN,
Attribute::DomainName => ATTR_DOMAIN_NAME,
Attribute::DomainSsid => ATTR_DOMAIN_SSID,
@ -426,6 +425,7 @@ impl From<Attribute> for &'static str {
Attribute::IpaSshPubKey => ATTR_IPASSHPUBKEY,
Attribute::JwsEs256PrivateKey => ATTR_JWS_ES256_PRIVATE_KEY,
Attribute::LastModifiedCid => ATTR_LAST_MODIFIED_CID,
Attribute::LdapAllowUnixPwBind => ATTR_LDAP_ALLOW_UNIX_PW_BIND,
Attribute::LdapEmailAddress => ATTR_LDAP_EMAIL_ADDRESS,
Attribute::LdapKeys => ATTR_LDAP_KEYS,
Attribute::LdapSshPublicKey => ATTR_LDAP_SSHPUBLICKEY,

View file

@ -111,10 +111,10 @@ pub static ref SCHEMA_ATTR_DOMAIN_NAME: SchemaAttribute = SchemaAttribute {
..Default::default()
};
pub static ref SCHEMA_ATTR_DOMAIN_LDAP_ALLOW_UNIX_PW_BIND: SchemaAttribute = SchemaAttribute {
uuid: UUID_SCHEMA_ATTR_DOMAIN_LDAP_ALLOW_UNIX_PW_BIND,
name: Attribute::DomainLdapAllowUnixPwBind.into(),
description: "Configuration to allow binds to LDAP objects using UNIX passwords.".to_string(),
pub static ref SCHEMA_ATTR_LDAP_ALLOW_UNIX_PW_BIND: SchemaAttribute = SchemaAttribute {
uuid: UUID_SCHEMA_ATTR_LDAP_ALLOW_UNIX_PW_BIND,
name: Attribute::LdapAllowUnixPwBind.into(),
description: "Configuration to enable binds to LDAP objects using their UNIX password.".to_string(),
unique: false,
syntax: SyntaxType::Boolean,
..Default::default()
@ -716,8 +716,11 @@ pub static ref SCHEMA_CLASS_DOMAIN_INFO: SchemaClass = SchemaClass {
uuid: UUID_SCHEMA_CLASS_DOMAIN_INFO,
name: EntryClass::DomainInfo.into(),
description: "Local domain information and partial configuration.to_string().".to_string(),
systemmay: vec![Attribute::DomainSsid.into(), Attribute::DomainLdapBasedn.into(),Attribute::DomainLdapAllowUnixPwBind.into()],
systemmay: vec![
Attribute::DomainSsid.into(),
Attribute::DomainLdapBasedn.into(),
Attribute::LdapAllowUnixPwBind.into()
],
systemmust: vec![
Attribute::Name.into(),
Attribute::DomainUuid.into(),

View file

@ -241,9 +241,7 @@ pub const UUID_SCHEMA_ATTR_AUTH_PRIVILEGE_EXPIRY: Uuid =
uuid!("00000000-0000-0000-0000-ffff00000142");
pub const UUID_SCHEMA_ATTR_IMAGE: Uuid = uuid!("00000000-0000-0000-0000-ffff00000143");
pub const UUID_SCHEMA_ATTR_DENIED_NAME: Uuid = uuid!("00000000-0000-0000-0000-ffff00000144");
// Leave 145 for ldap unix pw bind
pub const UUID_SCHEMA_ATTR_DOMAIN_LDAP_ALLOW_UNIX_PW_BIND: Uuid =
pub const UUID_SCHEMA_ATTR_LDAP_ALLOW_UNIX_PW_BIND: Uuid =
uuid!("00000000-0000-0000-0000-ffff00000145");
pub const UUID_SCHEMA_CLASS_ACCOUNT_POLICY: Uuid = uuid!("00000000-0000-0000-0000-ffff00000146");

View file

@ -665,7 +665,7 @@ mod tests {
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await;
let disallow_unix_pw_flag = ModifyEvent::new_internal_invalid(
filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))),
ModifyList::new_purge_and_set(Attribute::DomainLdapAllowUnixPwBind, Value::Bool(false)),
ModifyList::new_purge_and_set(Attribute::LdapAllowUnixPwBind, Value::Bool(false)),
);
assert!(idms_prox_write
.qs_write
@ -684,7 +684,7 @@ mod tests {
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await;
let allow_unix_pw_flag = ModifyEvent::new_internal_invalid(
filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))),
ModifyList::new_purge_and_set(Attribute::DomainLdapAllowUnixPwBind, Value::Bool(true)),
ModifyList::new_purge_and_set(Attribute::LdapAllowUnixPwBind, Value::Bool(true)),
);
assert!(idms_prox_write.qs_write.modify(&allow_unix_pw_flag).is_ok());
assert!(idms_prox_write.commit().is_ok());

View file

@ -565,7 +565,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
SCHEMA_ATTR_DOMAIN_DISPLAY_NAME.clone().into(),
SCHEMA_ATTR_DOMAIN_LDAP_BASEDN.clone().into(),
SCHEMA_ATTR_DOMAIN_NAME.clone().into(),
SCHEMA_ATTR_DOMAIN_LDAP_ALLOW_UNIX_PW_BIND.clone().into(),
SCHEMA_ATTR_LDAP_ALLOW_UNIX_PW_BIND.clone().into(),
SCHEMA_ATTR_DOMAIN_SSID.clone().into(),
SCHEMA_ATTR_DOMAIN_TOKEN_KEY.clone().into(),
SCHEMA_ATTR_DOMAIN_UUID.clone().into(),

View file

@ -817,7 +817,7 @@ pub trait QueryServerTransaction<'a> {
fn get_domain_ldap_allow_unix_pw_bind(&mut self) -> Result<bool, OperationError> {
self.internal_search_uuid(UUID_DOMAIN_INFO).map(|entry| {
entry
.get_ava_single_bool(Attribute::DomainLdapAllowUnixPwBind)
.get_ava_single_bool(Attribute::LdapAllowUnixPwBind)
.unwrap_or(true)
})
}

View file

@ -5,7 +5,8 @@ impl DomainOpt {
pub fn debug(&self) -> bool {
match self {
DomainOpt::SetDisplayName(copt) => copt.copt.debug,
DomainOpt::SetLdapBasedn { copt, .. } => copt.debug,
DomainOpt::SetLdapBasedn { copt, .. }
| DomainOpt::SetLdapAllowUnixPasswordBind { copt, .. } => copt.debug,
DomainOpt::Show(copt) | DomainOpt::ResetTokenKey(copt) => copt.debug,
}
}
@ -37,6 +38,13 @@ impl DomainOpt {
Err(e) => handle_client_error(e, &copt.output_mode),
}
}
DomainOpt::SetLdapAllowUnixPasswordBind { copt, enable } => {
let client = copt.to_client(OpType::Write).await;
match client.idm_set_ldap_allow_unix_password_bind(*enable).await {
Ok(_) => println!("Success"),
Err(e) => handle_client_error(e, &copt.output_mode),
}
}
DomainOpt::Show(copt) => {
let client = copt.to_client(OpType::Read).await;
match client.idm_domain_get().await {

View file

@ -870,6 +870,14 @@ pub enum DomainOpt {
#[clap(name = "new-basedn")]
new_basedn: String,
},
/// Enable or disable unix passwords being used to bind via LDAP. Unless you have a specific
/// requirement for this, you should disable this.
SetLdapAllowUnixPasswordBind {
#[clap(flatten)]
copt: CommonOpt,
#[clap(name = "allow", action = clap::ArgAction::Set)]
enable: bool,
},
#[clap(name = "show")]
/// Show information about this system's domain
Show(CommonOpt),