Compare commits

...

10 commits

Author SHA1 Message Date
alteriks d3c3a71909
Merge 48f7324080 into f40679cd52 2025-02-20 15:13:43 +01:00
sinavir f40679cd52
Accept invalid certs and fix token_cache_path ()
* Add accept-invalid-certs option for cli
* Fix token_cache_path behavior

---------

Co-authored-by: sinavir <sinavir@sinavir.fr>
2025-02-20 08:07:48 +00:00
Firstyear 52824b58f1
Accept lowercase ldap pwd hashes () 2025-02-20 04:34:27 +00:00
Krzysztof Dajka 48f7324080 Merge branch 'master' of github.com:alteriks/kanidm 2025-02-11 11:50:55 +01:00
Krzysztof Dajka 791a182767 NEW: proxmox example docs
Add redirect URL to proxmox documentation

Update contributors list

Update examples/proxmox.md
2025-02-11 11:49:57 +01:00
alteriks 51a1e815b2
Merge branch 'master' into master 2025-02-11 09:55:56 +01:00
James Hodgkinson 399e1d71b8
Update examples/proxmox.md 2025-02-11 07:26:44 +10:00
Krzysztof Dajka 2c53ae77c5 Update contributors list 2024-12-05 09:54:51 +01:00
Krzysztof Dajka 3a3d3eb807 Add redirect URL to proxmox documentation 2024-12-05 09:54:21 +01:00
Krzysztof Dajka 6184d645d2 NEW: proxmox example 2024-12-04 21:42:34 +01:00
7 changed files with 170 additions and 9 deletions
CONTRIBUTORS.md
examples
libs
client/src
crypto/src
tools/cli/src

View file

@ -1,6 +1,6 @@
## Author
- William Brown (Firstyear): william@blackhats.net.au
- William Brown (Firstyear): <william@blackhats.net.au>
## Contributors
@ -44,6 +44,7 @@
- adamcstephens
- Chris Olstrom (colstrom)
- Christopher-Robin (cebbinghaus)
- Krzysztof Dajka (alteriks)
- Fabian Kammel (datosh)
- Andris Raugulis (arthepsy)
- Jason (argonaut0)

Binary file not shown.

After

(image error) Size: 70 KiB

91
examples/proxmox.md Normal file
View file

@ -0,0 +1,91 @@
# Proxmox PVE/PBS
## Helpful links
- <https://pve.proxmox.com/wiki/User_Management>
- <https://pve.proxmox.com/pve-docs/pve-admin-guide.html#pveum_openid>
## Proxmox OIDC limitation
As of December 2024, the OIDC implementation in Proxmox supports only authentication.
Authorization has to be done manually.
Mapping user to specific groups won't work yet (steps 2,3,4).
Patch for this feature exists, but it hasn't been tested extensively:
<https://lore.proxmox.com/pve-devel/20240901165512.687801-1-thomas@atskinner.net/>
See also:
<https://forum.proxmox.com/threads/openid-connect-default-group.103394/>
## On Kanidm
### 1. Create the proxmox resource server and configure the redirect URL
```bash
kanidm system oauth2 create proxmox "proxmox" https://yourproxmox.example.com
kanidm system oauth2 add-redirect-url "proxmox" https://yourproxmox.example.com
```
### 2. Create the appropriate group(s)
```bash
kanidm group create proxmox_users --name idm_admin
kanidm group create proxmox_admins --name idm_admin
```
### 3. Add the appropriate users to the group
```bash
kanidm group add-members proxmox_users user.name
kanidm group add-members proxmox_admins user.name
```
### 4. scope map
```bash
kanidm system oauth2 update-claim-map-join 'proxmox' 'proxmox_role' array
kanidm system oauth2 update-claim-map 'proxmox' 'proxmox_role' 'proxmox_admins' 'admin'
kanidm system oauth2 update-claim-map 'proxmox' 'proxmox_role' 'proxmox_users' 'user'
```
### 5. Add the scopes
```bash
kanidm system oauth2 update-scope-map proxmox proxmox_users email profile openid
```
### 6. Get the client secret
```bash
kanidm system oauth2 show-basic-secret proxmox
```
Copy the value that is returned.
## On proxmox server
### Using WebGUI
Go to <https://yourproxmox.example.com>
Select Datacenter->Realms->Add->OpenID Connect Server
![](media/kanidm_proxmox.png)
Issuer URL:
- <https://idm.example.com:8443/oauth2/openid/proxmox>
When kanidm is behind reverse proxy or when using docker port mapping:
- <https://idm.example.com/oauth2/openid/proxmox>
Realm: give some proper name or anything that's meaningful
Client ID: name given in step 1 (resource server)
Client Key: secret from step 6
Autocreate Users: Automatically create users if they do not exist. Users are stored in Proxmox Cluster File System (pmxcfs) - /etc/pve/user.cfg
### Using CLI
Login to proxmox node and execute:
```bash
pveum realm add kanidm --type openid --issuer-url https://idm.example.com/oauth2/openid/proxmox --client-id proxmox --client-key="secret from step 6" --username-claim username --scopes="email profile openid" --autocreate
```

View file

@ -94,7 +94,7 @@ pub struct KanidmClientConfigInstance {
pub verify_hostnames: Option<bool>,
/// Whether to verify the Certificate Authority details of the server's TLS certificate, defaults to `true`.
///
/// Environment variable is slightly inverted - `KANIDM_SKIP_HOSTNAME_VERIFICATION`.
/// Environment variable is slightly inverted - `KANIDM_ACCEPT_INVALID_CERTS`.
pub verify_ca: Option<bool>,
/// Optionally you can specify the path of a CA certificate to use for verifying the server, if you're not using one trusted by your system certificate store.
///
@ -453,6 +453,13 @@ impl KanidmClientBuilder {
}
}
pub fn set_token_cache_path(self, token_cache_path: Option<String>) -> Self {
KanidmClientBuilder {
token_cache_path,
..self
}
}
#[allow(clippy::result_unit_err)]
pub fn add_root_certificate_filepath(self, ca_path: &str) -> Result<Self, ClientError> {
//Okay we have a ca to add. Let's read it in and setup.

View file

@ -662,9 +662,13 @@ impl TryFrom<&str> for Password {
});
}
// Test 389ds formats
// Test 389ds/openldap formats. Shout outs openldap which sometimes makes these
// lowercase.
if let Some(ds_ssha1) = value.strip_prefix("{SHA}") {
if let Some(ds_ssha1) = value
.strip_prefix("{SHA}")
.or_else(|| value.strip_prefix("{sha}"))
{
let h = general_purpose::STANDARD.decode(ds_ssha1).map_err(|_| ())?;
if h.len() != DS_SHA1_HASH_LEN {
return Err(());
@ -674,7 +678,10 @@ impl TryFrom<&str> for Password {
});
}
if let Some(ds_ssha1) = value.strip_prefix("{SSHA}") {
if let Some(ds_ssha1) = value
.strip_prefix("{SSHA}")
.or_else(|| value.strip_prefix("{ssha}"))
{
let sh = general_purpose::STANDARD.decode(ds_ssha1).map_err(|_| ())?;
let (h, s) = sh.split_at(DS_SHA1_HASH_LEN);
if s.len() != DS_SHA_SALT_LEN {
@ -685,7 +692,10 @@ impl TryFrom<&str> for Password {
});
}
if let Some(ds_ssha256) = value.strip_prefix("{SHA256}") {
if let Some(ds_ssha256) = value
.strip_prefix("{SHA256}")
.or_else(|| value.strip_prefix("{sha256}"))
{
let h = general_purpose::STANDARD
.decode(ds_ssha256)
.map_err(|_| ())?;
@ -697,7 +707,10 @@ impl TryFrom<&str> for Password {
});
}
if let Some(ds_ssha256) = value.strip_prefix("{SSHA256}") {
if let Some(ds_ssha256) = value
.strip_prefix("{SSHA256}")
.or_else(|| value.strip_prefix("{ssha256}"))
{
let sh = general_purpose::STANDARD
.decode(ds_ssha256)
.map_err(|_| ())?;
@ -710,7 +723,10 @@ impl TryFrom<&str> for Password {
});
}
if let Some(ds_ssha512) = value.strip_prefix("{SHA512}") {
if let Some(ds_ssha512) = value
.strip_prefix("{SHA512}")
.or_else(|| value.strip_prefix("{sha512}"))
{
let h = general_purpose::STANDARD
.decode(ds_ssha512)
.map_err(|_| ())?;
@ -722,7 +738,10 @@ impl TryFrom<&str> for Password {
});
}
if let Some(ds_ssha512) = value.strip_prefix("{SSHA512}") {
if let Some(ds_ssha512) = value
.strip_prefix("{SSHA512}")
.or_else(|| value.strip_prefix("{ssha512}"))
{
let sh = general_purpose::STANDARD
.decode(ds_ssha512)
.map_err(|_| ())?;
@ -1441,8 +1460,12 @@ mod tests {
#[test]
fn test_password_from_ds_sha1() {
let im_pw = "{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=";
let _r = Password::try_from(im_pw).expect("Failed to parse");
let im_pw = "{sha}W6ph5Mm5Pz8GgiULbPgzG37mj9g=";
let password = "password";
let r = Password::try_from(im_pw).expect("Failed to parse");
// Known weak, require upgrade.
assert!(r.requires_upgrade());
assert!(r.verify(password).unwrap_or(false));
@ -1451,8 +1474,12 @@ mod tests {
#[test]
fn test_password_from_ds_ssha1() {
let im_pw = "{SSHA}EyzbBiP4u4zxOrLpKTORI/RX3HC6TCTJtnVOCQ==";
let _r = Password::try_from(im_pw).expect("Failed to parse");
let im_pw = "{ssha}EyzbBiP4u4zxOrLpKTORI/RX3HC6TCTJtnVOCQ==";
let password = "password";
let r = Password::try_from(im_pw).expect("Failed to parse");
// Known weak, require upgrade.
assert!(r.requires_upgrade());
assert!(r.verify(password).unwrap_or(false));
@ -1461,8 +1488,12 @@ mod tests {
#[test]
fn test_password_from_ds_sha256() {
let im_pw = "{SHA256}XohImNooBHFR0OVvjcYpJ3NgPQ1qq73WKhHvch0VQtg=";
let _r = Password::try_from(im_pw).expect("Failed to parse");
let im_pw = "{sha256}XohImNooBHFR0OVvjcYpJ3NgPQ1qq73WKhHvch0VQtg=";
let password = "password";
let r = Password::try_from(im_pw).expect("Failed to parse");
// Known weak, require upgrade.
assert!(r.requires_upgrade());
assert!(r.verify(password).unwrap_or(false));
@ -1471,8 +1502,12 @@ mod tests {
#[test]
fn test_password_from_ds_ssha256() {
let im_pw = "{SSHA256}luYWfFJOZgxySTsJXHgIaCYww4yMpu6yest69j/wO5n5OycuHFV/GQ==";
let _r = Password::try_from(im_pw).expect("Failed to parse");
let im_pw = "{ssha256}luYWfFJOZgxySTsJXHgIaCYww4yMpu6yest69j/wO5n5OycuHFV/GQ==";
let password = "password";
let r = Password::try_from(im_pw).expect("Failed to parse");
// Known weak, require upgrade.
assert!(r.requires_upgrade());
assert!(r.verify(password).unwrap_or(false));
@ -1481,8 +1516,12 @@ mod tests {
#[test]
fn test_password_from_ds_sha512() {
let im_pw = "{SHA512}sQnzu7wkTrgkQZF+0G1hi5AI3Qmzvv0bXgc5THBqi7mAsdd4Xll27ASbRt9fEyavWi6m0QP9B8lThf+rDKy8hg==";
let _r = Password::try_from(im_pw).expect("Failed to parse");
let im_pw = "{sha512}sQnzu7wkTrgkQZF+0G1hi5AI3Qmzvv0bXgc5THBqi7mAsdd4Xll27ASbRt9fEyavWi6m0QP9B8lThf+rDKy8hg==";
let password = "password";
let r = Password::try_from(im_pw).expect("Failed to parse");
// Known weak, require upgrade.
assert!(r.requires_upgrade());
assert!(r.verify(password).unwrap_or(false));
@ -1491,8 +1530,12 @@ mod tests {
#[test]
fn test_password_from_ds_ssha512() {
let im_pw = "{SSHA512}JwrSUHkI7FTAfHRVR6KoFlSN0E3dmaQWARjZ+/UsShYlENOqDtFVU77HJLLrY2MuSp0jve52+pwtdVl2QUAHukQ0XUf5LDtM";
let _r = Password::try_from(im_pw).expect("Failed to parse");
let im_pw = "{ssha512}JwrSUHkI7FTAfHRVR6KoFlSN0E3dmaQWARjZ+/UsShYlENOqDtFVU77HJLLrY2MuSp0jve52+pwtdVl2QUAHukQ0XUf5LDtM";
let password = "password";
let r = Password::try_from(im_pw).expect("Failed to parse");
// Known weak, require upgrade.
assert!(r.requires_upgrade());
assert!(r.verify(password).unwrap_or(false));

View file

@ -91,6 +91,18 @@ impl CommonOpt {
false => client_builder,
};
let client_builder = match self.accept_invalid_certs {
true => {
warn!(
"TLS Certificate Verification disabled!!! This can lead to credential and account compromise!!!"
);
client_builder.danger_accept_invalid_certs(true)
}
false => client_builder,
};
let client_builder = client_builder.set_token_cache_path(self.token_cache_path.clone());
client_builder.build().unwrap_or_else(|e| {
error!("Failed to build client instance -- {:?}", e);
std::process::exit(1);

View file

@ -87,6 +87,13 @@ pub struct CommonOpt {
default_value_t = false
)]
skip_hostname_verification: bool,
/// Don't verify CA
#[clap(
long = "accept-invalid-certs",
env = "KANIDM_ACCEPT_INVALID_CERTS",
default_value_t = false
)]
accept_invalid_certs: bool,
/// Path to a file to cache tokens in, defaults to ~/.cache/kanidm_tokens
#[clap(short, long, env = "KANIDM_TOKEN_CACHE_PATH", hide = true, default_value = None,
value_parser = clap::builder::NonEmptyStringValueParser::new())]