Add /etc/skel templating and notes adjacent to kanidm-unixd and packaging (#1113)

This commit is contained in:
kalebo 2022-11-03 17:49:11 -06:00 committed by GitHub
parent 692c0a3978
commit 55ee2410d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 215 additions and 12 deletions

5
.gitignore vendored
View file

@ -19,6 +19,11 @@ orca/example_profiles/small/orca-edited.toml
kanidm_unix_int/pam_tester/Cargo.lock
.vscode/
# kanidm simple packaging
deployment-config/
kanidm_simple_pkg/
kanidm-client-tools.tar.gz
# python things
**/__pycache__/**
**/.venv/**

View file

@ -20,6 +20,7 @@
* Thomas Sanchez (daedric)
* Dominik Süß (theSuess)
* Florian Klink (flokli)
* Kaleb Olson (kalebo)
## Acknowledgements

1
Cargo.lock generated
View file

@ -2334,6 +2334,7 @@ dependencies = [
"toml",
"tracing",
"users",
"walkdir",
]
[[package]]

View file

@ -160,6 +160,7 @@ webauthn-rs-proto = "0.4.7"
# webauthn-rs-proto = { path = "../webauthn-rs/webauthn-rs-proto" }
web-sys = "^0.3.60"
whoami = "^1.2.3"
walkdir = "2"
yew = "^0.19.3"
yew-agent = "^0.1.0"

View file

@ -5,5 +5,6 @@
# home_prefix = "/home/"
# home_attr = "uuid"
# home_alias = "spn"
# use_etc_skel = false
# uid_attr_map = "spn"
# gid_attr_map = "spn"

View file

@ -30,6 +30,7 @@
- [PAM and nsswitch](integrations/pam_and_nsswitch.md)
- [RADIUS](integrations/radius.md)
- [LDAP](integrations/ldap.md)
- [Traefik](integrations/traefik.md)
# Integration Examples

View file

@ -45,6 +45,7 @@ You can also configure some unixd-specific options with the file /etc/kanidm/uni
home_prefix = "/home/"
home_attr = "uuid"
home_alias = "spn"
use_etc_skel = false
uid_attr_map = "spn"
gid_attr_map = "spn"
@ -73,6 +74,9 @@ Valid choices are `none`, `uuid`, `name`, `spn`. Defaults to `spn`.
> from the name to the UUID folder. Automatic support is provided for this via the unixd
> tasks daemon, as documented here.
`use_etc_skel` controls if home directories should be prepopulated with the contents of `/etc/skel`
when first created. Defaults to false.
`uid_attr_map` chooses which attribute is used for domain local users in presentation. Defaults
to `spn`. Users from a trust will always use spn.

View file

@ -0,0 +1,56 @@
# Traefik
Traefik is a flexible HTTP reverse proxy webserver that can be integrated with Docker to allow dynamic configuration
and to automatically use LetsEncrypt to provide valid TLS certificates.
We can leverage this in the setup of Kanidm by specifying the configuration of Kanidm and Traefik in the same [Docker Compose configuration](https://docs.docker.com/compose/).
## Example setup
Create a new directory and copy the following YAML file into it as `docker-compose.yml`.
Edit the YAML to update the LetsEncrypt account email for your domain and the FQDN where Kanidm will be made available.
Ensure you adjust this file or Kanidm's configuration to have a matching HTTPS port; the line `traefik.http.services.kanidm.loadbalancer.server.port=8443` sets this on the Traefik side.
> **NOTE** You will need to generate self-signed certificates for Kanidm, and copy the configuration into the `kanidm_data` volume. Some instructions are available in the "Installing the Server" section of this book.
`docker-compose.yml`
```yaml
version: "3.4"
services:
traefik:
image: traefik:v2.6
container_name: traefik
command:
- "--certificatesresolvers.http.acme.email=admin@example.com"
- "--certificatesresolvers.http.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.http.acme.tlschallenge=true"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.websecure.http.tls=true"
- "--entrypoints.websecure.http.tls.certResolver=http"
- "--log.level=INFO"
- "--providers.docker=true"
- "--providers.docker.exposedByDefault=false"
- "--serverstransport.insecureskipverify=true"
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- "443:443"
kanidm:
container_name: kanidm
image: kanidm/server:devel
restart: unless-stopped
volumes:
- kanidm_data:/data
labels:
- traefik.enable=true
- traefik.http.routers.kanidm.entrypoints=websecure
- traefik.http.routers.kanidm.rule=Host(`idm.example.com`)
- traefik.http.routers.kanidm.service=kanidm
- traefik.http.serversTransports.kanidm.insecureSkipVerify=true
- traefik.http.services.kanidm.loadbalancer.server.port=8443
- traefik.http.services.kanidm.loadbalancer.server.scheme=https
volumes:
kanidm_data: {}
```
Finally you may run `docker-compose up` to start up both Kanidm and Traefik.

View file

@ -70,6 +70,7 @@ tokio-util = { workspace = true, features = ["codec"] }
tracing.workspace = true
reqwest.workspace = true
users.workspace = true
walkdir.workspace = true
[features]
# default = [ "libsqlite3-sys/bundled" ]

View file

@ -10,5 +10,6 @@ pub const DEFAULT_SHELL: &str = "/bin/sh";
pub const DEFAULT_HOME_PREFIX: &str = "/home/";
pub const DEFAULT_HOME_ATTR: HomeAttr = HomeAttr::Uuid;
pub const DEFAULT_HOME_ALIAS: Option<HomeAttr> = Some(HomeAttr::Spn);
pub const DEFAULT_USE_ETC_SKEL: bool = false;
pub const DEFAULT_UID_ATTR_MAP: UidAttr = UidAttr::Spn;
pub const DEFAULT_GID_ATTR_MAP: UidAttr = UidAttr::Spn;

View file

@ -12,6 +12,7 @@
use std::ffi::CString;
use std::os::unix::fs::symlink;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::time::Duration;
use std::{fs, io};
@ -29,6 +30,7 @@ use tokio::net::UnixStream;
use tokio::time;
use tokio_util::codec::{Decoder, Encoder, Framed};
use users::{get_effective_gid, get_effective_uid};
use walkdir::WalkDir;
struct TaskCodec;
@ -68,7 +70,18 @@ impl TaskCodec {
}
}
fn create_home_directory(info: &HomeDirectoryInfo, home_prefix: &str) -> Result<(), String> {
fn chown(path: &Path, gid: u32) -> Result<(), String> {
let path_os = CString::new(path.as_os_str().as_bytes())
.map_err(|_| "Unable to create c-string".to_string())?;
// Change the owner to the gid - remember, kanidm ONLY has gid's, the uid is implied.
if unsafe { lchown(path_os.as_ptr(), gid, gid) } != 0 {
return Err("Unable to set ownership".to_string());
}
Ok(())
}
fn create_home_directory(info: &HomeDirectoryInfo, home_prefix: &str, use_etc_skel: bool) -> Result<(), String> {
// Final sanity check to prevent certain classes of attacks.
let name = info
.name
@ -96,14 +109,10 @@ fn create_home_directory(info: &HomeDirectoryInfo, home_prefix: &str) -> Result<
return Err("Invalid/Corrupt home directory path - no prefix found".to_string());
}
let hd_path_os =
CString::new(hd_path_raw.clone()).map_err(|_| "Unable to create c-string".to_string())?;
// Does the home directory exist?
if !hd_path.exists() {
// Set a umask
let before = unsafe { umask(0o0027) };
// TODO: Should we copy content from /etc/skel?
// Create the dir
if let Err(e) = fs::create_dir_all(hd_path) {
let _ = unsafe { umask(before) };
@ -111,10 +120,26 @@ fn create_home_directory(info: &HomeDirectoryInfo, home_prefix: &str) -> Result<
}
let _ = unsafe { umask(before) };
// Change the owner to the gid - remember, kanidm ONLY has gid's, the uid is implied.
chown(hd_path, info.gid)?;
if unsafe { lchown(hd_path_os.as_ptr(), info.gid, info.gid) } != 0 {
return Err("Unable to set ownership".to_string());
// Copy in structure from /etc/skel/ if present
let skel_dir = Path::new("/etc/skel/");
if use_etc_skel && skel_dir.exists() {
info!("preparing homedir using /etc/skel");
for entry in WalkDir::new(skel_dir).into_iter().filter_map(|e| e.ok()) {
let dest = &hd_path.join(
entry
.path()
.strip_prefix(skel_dir)
.map_err(|e| e.to_string())?,
);
if entry.path().is_dir() {
fs::create_dir_all(dest).map_err(|e| e.to_string())?;
} else {
fs::copy(entry.path(), dest).map_err(|e| e.to_string())?;
}
chown(dest, info.gid)?;
}
}
}
@ -166,7 +191,7 @@ fn create_home_directory(info: &HomeDirectoryInfo, home_prefix: &str) -> Result<
Ok(())
}
async fn handle_tasks(stream: UnixStream, home_prefix: &str) {
async fn handle_tasks(stream: UnixStream, cfg: &KanidmUnixdConfig) {
let mut reqs = Framed::new(stream, TaskCodec::new());
loop {
@ -174,7 +199,7 @@ async fn handle_tasks(stream: UnixStream, home_prefix: &str) {
Some(Ok(TaskRequest::HomeDirectory(info))) => {
debug!("Received task -> HomeDirectory({:?})", info);
let resp = match create_home_directory(&info, home_prefix) {
let resp = match create_home_directory(&info, &cfg.home_prefix, cfg.use_etc_skel) {
Ok(()) => TaskResponse::Success,
Err(msg) => TaskResponse::Error(msg),
};
@ -249,7 +274,7 @@ async fn main() {
info!("Found kanidm_unixd, waiting for tasks ...");
// Yep! Now let the main handler do it's job.
// If it returns (dc, etc, then we loop and try again).
handle_tasks(stream, &cfg.home_prefix).await;
handle_tasks(stream, &cfg).await;
}
Err(e) => {
error!("Unable to find kanidm_unixd, sleeping ...");

View file

@ -9,7 +9,7 @@ use serde::Deserialize;
use crate::constants::{
DEFAULT_CACHE_TIMEOUT, DEFAULT_CONN_TIMEOUT, DEFAULT_DB_PATH, DEFAULT_GID_ATTR_MAP,
DEFAULT_HOME_ALIAS, DEFAULT_HOME_ATTR, DEFAULT_HOME_PREFIX, DEFAULT_SHELL, DEFAULT_SOCK_PATH,
DEFAULT_TASK_SOCK_PATH, DEFAULT_UID_ATTR_MAP,
DEFAULT_TASK_SOCK_PATH, DEFAULT_UID_ATTR_MAP, DEFAULT_USE_ETC_SKEL,
};
#[derive(Debug, Deserialize)]
@ -24,6 +24,7 @@ struct ConfigInt {
home_prefix: Option<String>,
home_attr: Option<String>,
home_alias: Option<String>,
use_etc_skel: Option<bool>,
uid_attr_map: Option<String>,
gid_attr_map: Option<String>,
}
@ -81,6 +82,7 @@ pub struct KanidmUnixdConfig {
pub home_prefix: String,
pub home_attr: HomeAttr,
pub home_alias: Option<HomeAttr>,
pub use_etc_skel: bool,
pub uid_attr_map: UidAttr,
pub gid_attr_map: UidAttr,
}
@ -135,6 +137,7 @@ impl KanidmUnixdConfig {
home_prefix: DEFAULT_HOME_PREFIX.to_string(),
home_attr: DEFAULT_HOME_ATTR,
home_alias: DEFAULT_HOME_ALIAS,
use_etc_skel: DEFAULT_USE_ETC_SKEL,
uid_attr_map: DEFAULT_UID_ATTR_MAP,
gid_attr_map: DEFAULT_GID_ATTR_MAP,
}
@ -220,6 +223,7 @@ impl KanidmUnixdConfig {
}
})
.unwrap_or(self.home_alias),
use_etc_skel: config.use_etc_skel.unwrap_or(self.use_etc_skel),
uid_attr_map: config
.uid_attr_map
.and_then(|v| match v.as_str() {

View file

@ -0,0 +1,19 @@
Name: Kanidm Authentication
Default: yes
Priority: 300
Auth-Type: Primary
Auth:
[success=end new_authtok_reqd=done default=ignore] pam_kanidm.so ignore_unknown_user
Account-Type: Primary
Account:
[success=end new_authtok_reqd=done default=ignore] pam_kanidm.so ignore_unknown_user
Session-Type: Additional
Session:
optional pam_kanidm.so
Password-Type: Additional
Password:
optional pam_kanidm.so

83
platform/debian/simple_pkg.sh Executable file
View file

@ -0,0 +1,83 @@
#!/usr/bin/env bash
set -xe
## NOTE this is based on the Arch Linux PKGBUILD. It combines kanidm_tools, unixd and ssh
# as well as the systemd services. This is a simple alternative for building a tarball for
# use on debian based systems (tested on ubuntu 22.04).
pushd "$( dirname -- "$0"; )/../../"
pkgdir=$(realpath kanidm_simple_pkg)
rm -rf "$pkgdir"
mkdir -p "$pkgdir"
# build the project
make release/kanidm release/kanidm-unixd
# enable the following block to include deployment specific configuration files
if [ 1 -eq 0 ]; then
mkdir -p deployment-config
# Customize the following heredocs according to the deployment
cat << EOF > deployment-config/config
uri = "https://idm.example.com"
verify_ca = true
verify_hostnames = true
EOF
cat << EOF > deployment-config/unixd
pam_allowed_login_groups = [""]
EOF
install -Dm644 deployment-config/config "${pkgdir}/etc/kanidm/config"
install -Dm644 deployment-config/unixd "${pkgdir}/etc/kanidm/unixd"
fi
# This is for allowing login via PAM. It needs to be enabled using `pam-auth-update`
install -Dm644 platform/debian/pam-config-kanidm "${pkgdir}/usr/share/pam-configs/kanidm"
# Install kanidm cli
install -Dm755 target/release/kanidm "${pkgdir}/usr/local/sbin/kanidm"
install -Dm644 target/release/build/completions/_kanidm "${pkgdir}/usr/share/zsh/site-functions/_kanidm"
install -Dm644 target/release/build/completions/kanidm.bash "${pkgdir}/usr/share/bash-completion/completions/kanidm.sh"
# Install systemd service files
install -Dm644 examples/systemd/kanidm-unixd.service "${pkgdir}/usr/lib/systemd/system/kanidm-unixd.service"
install -Dm644 examples/systemd/kanidm-unixd-tasks.service "${pkgdir}/usr/lib/systemd/system/kanidm-unixd-tasks.service"
# NB., the debian style lib dir and security dir
install -Dm755 target/release/libnss_kanidm.so "${pkgdir}/usr/lib/x86_64-linux-gnu/libnss_kanidm.so.2"
install -Dm755 target/release/libpam_kanidm.so "${pkgdir}/usr/lib/x86_64-linux-gnu/security/pam_kanidm.so"
# install kanidm unix utilities
install -Dm755 target/release/kanidm_cache_clear "${pkgdir}/usr/local/sbin/kanidm_cache_clear"
install -Dm755 target/release/kanidm_cache_invalidate "${pkgdir}/usr/local/sbin/kanidm_cache_invalidate"
install -Dm755 target/release/kanidm_ssh_authorizedkeys "${pkgdir}/usr/local/sbin/kanidm_ssh_authorizedkeys"
install -Dm755 target/release/kanidm_ssh_authorizedkeys_direct "${pkgdir}/usr/local/sbin/kanidm_ssh_authorizedkeys_direct"
install -Dm755 target/release/kanidm_unixd "${pkgdir}/usr/local/sbin/kanidm_unixd"
install -Dm755 target/release/kanidm_unixd_status "${pkgdir}/usr/local/sbin/kanidm_unixd_status"
install -Dm755 target/release/kanidm_unixd_tasks "${pkgdir}/usr/local/sbin/kanidm_unixd_tasks"
# Install Bash and ZSH completions
install -Dm644 target/release/build/completions/_kanidm_ssh_authorizedkeys_direct "${pkgdir}/usr/share/zsh/site-functions/_kanidm_ssh_authorizedkeys_direct"
install -Dm644 target/release/build/completions/_kanidm_cache_clear "${pkgdir}/usr/share/zsh/site-functions/_kanidm_cache_clear"
install -Dm644 target/release/build/completions/_kanidm_cache_invalidate "${pkgdir}/usr/share/zsh/site-functions/_kanidm_cache_invalidate"
install -Dm644 target/release/build/completions/_kanidm_ssh_authorizedkeys "${pkgdir}/usr/share/zsh/site-functions/_kanidm_ssh_authorizedkeys"
install -Dm644 target/release/build/completions/_kanidm_unixd_status "${pkgdir}/usr/share/zsh/site-functions/_kanidm_unixd_status"
install -Dm644 target/release/build/completions/kanidm_ssh_authorizedkeys_direct.bash "${pkgdir}/usr/share/bash-completion/completions/kanidm_ssh_authorizedkeys_direct.sh"
install -Dm644 target/release/build/completions/kanidm_cache_clear.bash "${pkgdir}/usr/share/bash-completion/completions/kanidm_cache_clear.sh"
install -Dm644 target/release/build/completions/kanidm_cache_invalidate.bash "${pkgdir}/usr/share/bash-completion/completions/kanidm_cache_invalidate.sh"
install -Dm644 target/release/build/completions/kanidm_ssh_authorizedkeys.bash "${pkgdir}/usr/share/bash-completion/completions/kanidm_ssh_authorizedkeys.sh"
install -Dm644 target/release/build/completions/kanidm_unixd_status.bash "${pkgdir}/usr/share/bash-completion/completions/kanidm_unixd_status.sh"
tar cvzf "kanidm-client-tools.tar.gz" -C "$pkgdir" .
# extract the package in root, enable and run the systemd services and then setup nsswitch according to the docs
# and run pam-auth-update. You may also want to setup the ssh config. It's wise to leave a root console open until
# you've confirmed pam-auth-update worked so you don't lock yourself out.
popd