mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
Add tls generator to main kanidmd (#1743)
This commit is contained in:
parent
32a7200305
commit
8b331325ae
2
Makefile
2
Makefile
|
@ -60,6 +60,7 @@ build/kanidmd: ## Build the kanidmd docker image locally
|
||||||
build/kanidmd:
|
build/kanidmd:
|
||||||
@$(CONTAINER_TOOL) build $(CONTAINER_TOOL_ARGS) -f server/Dockerfile \
|
@$(CONTAINER_TOOL) build $(CONTAINER_TOOL_ARGS) -f server/Dockerfile \
|
||||||
-t $(IMAGE_BASE)/server:$(IMAGE_VERSION) \
|
-t $(IMAGE_BASE)/server:$(IMAGE_VERSION) \
|
||||||
|
--platform $(IMAGE_ARCH) \
|
||||||
--build-arg "KANIDM_BUILD_PROFILE=container_generic" \
|
--build-arg "KANIDM_BUILD_PROFILE=container_generic" \
|
||||||
--build-arg "KANIDM_FEATURES=" \
|
--build-arg "KANIDM_FEATURES=" \
|
||||||
$(CONTAINER_BUILD_ARGS) .
|
$(CONTAINER_BUILD_ARGS) .
|
||||||
|
@ -68,6 +69,7 @@ build/kanidmd:
|
||||||
build/radiusd: ## Build the radiusd docker image locally
|
build/radiusd: ## Build the radiusd docker image locally
|
||||||
build/radiusd:
|
build/radiusd:
|
||||||
@$(CONTAINER_TOOL) build $(CONTAINER_TOOL_ARGS) \
|
@$(CONTAINER_TOOL) build $(CONTAINER_TOOL_ARGS) \
|
||||||
|
--platform $(IMAGE_ARCH) \
|
||||||
-f rlm_python/Dockerfile \
|
-f rlm_python/Dockerfile \
|
||||||
-t $(IMAGE_BASE)/radius:$(IMAGE_VERSION) .
|
-t $(IMAGE_BASE)/radius:$(IMAGE_VERSION) .
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# Kanidm
|
# Kanidm
|
||||||
|
|
||||||
- [Introduction to Kanidm](intro.md)
|
- [Introduction to Kanidm](intro.md)
|
||||||
|
- [Evaluation Quickstart](quickstart.md)
|
||||||
- [Installing the Server](installing_the_server.md)
|
- [Installing the Server](installing_the_server.md)
|
||||||
- [Choosing a Domain Name](choosing_a_domain_name.md)
|
- [Choosing a Domain Name](choosing_a_domain_name.md)
|
||||||
- [Preparing for your Deployment](prepare_the_server.md)
|
- [Preparing for your Deployment](prepare_the_server.md)
|
||||||
|
|
83
book/src/quickstart.md
Normal file
83
book/src/quickstart.md
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
# Evaluation Quickstart
|
||||||
|
|
||||||
|
This section will guide you through a quick setup of Kanidm for evaluation. It's recommended that
|
||||||
|
for a production deployment you follow the steps in the
|
||||||
|
[installation chapter](installing_the_server.html) instead as there are a number of security
|
||||||
|
considerations you should understand.
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
- docker or podman
|
||||||
|
- `x86_64` cpu supporting `x86_64_v2` OR `aarch64` cpu supporting `neon`
|
||||||
|
|
||||||
|
### Get the software
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker pull kanidm/server:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configure the container
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker volume create kanidmd
|
||||||
|
docker create --name kanidmd \
|
||||||
|
-p 443:8443 \
|
||||||
|
-p 636:3636 \
|
||||||
|
-v kanidmd:/data \
|
||||||
|
kanidm/server:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configure the server
|
||||||
|
|
||||||
|
Create server.toml
|
||||||
|
|
||||||
|
```toml
|
||||||
|
{{#rustdoc_include ../../examples/server_container.toml}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add configuration to container
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker cp server.toml kanidmd:/data/server.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generate evaluation certificates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --rm -i -t -v kanidmd:/data \
|
||||||
|
kanidm/server:latest \
|
||||||
|
kanidmd cert-generate -c /data/server.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Recover the admin password
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --rm -i -t -v kanidmd:/data \
|
||||||
|
kanidm/server:latest \
|
||||||
|
kanidmd recover-account admin -c /data/server.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Start Kanidmd
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker start kanidmd
|
||||||
|
```
|
||||||
|
|
||||||
|
### Setup the client configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# ~/.config/kanidm
|
||||||
|
|
||||||
|
uri = "https://localhost:443"
|
||||||
|
verify_ca = false
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check you can login
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kanidm login
|
||||||
|
```
|
||||||
|
|
||||||
|
### What next?
|
||||||
|
|
||||||
|
You can now follow the steps in the [administration section](administrivia.md)
|
|
@ -19,8 +19,8 @@ auth_token = "putyourtokenhere"
|
||||||
|
|
||||||
radius_cert_path = "/certs/cert.pem" # the TLS certificate
|
radius_cert_path = "/certs/cert.pem" # the TLS certificate
|
||||||
radius_key_path = "/certs/key.pem" # the signing key for radius TLS
|
radius_key_path = "/certs/key.pem" # the signing key for radius TLS
|
||||||
radius_dh_path = "/certs/dh.pem" # the diffie-hellman output
|
|
||||||
radius_ca_path = "/certs/ca.pem" # the CA certificate
|
radius_ca_path = "/certs/ca.pem" # the CA certificate
|
||||||
|
radius_dh_path = "/certs/dh.pem" # the diffie-hellman output
|
||||||
|
|
||||||
# A list of groups, if a user is in them, they're approved for RADIUS authentication
|
# A list of groups, if a user is in them, they're approved for RADIUS authentication
|
||||||
radius_required_groups = [
|
radius_required_groups = [
|
||||||
|
|
|
@ -47,7 +47,6 @@ RUN python3 -m pip install --no-cache-dir --no-warn-script-location /pkg/pykanid
|
||||||
|
|
||||||
COPY rlm_python/radius_entrypoint.py /radius_entrypoint.py
|
COPY rlm_python/radius_entrypoint.py /radius_entrypoint.py
|
||||||
|
|
||||||
ENV LD_PRELOAD=/usr/lib64/libpython3.so
|
|
||||||
ENV KANIDM_CONFIG_FILE="/data/kanidm"
|
ENV KANIDM_CONFIG_FILE="/data/kanidm"
|
||||||
|
|
||||||
RUN chmod a+r /etc/raddb/certs/ -R
|
RUN chmod a+r /etc/raddb/certs/ -R
|
||||||
|
|
|
@ -79,12 +79,15 @@ def setup_certs(
|
||||||
if kanidm_config_object.radius_dh_path is not None:
|
if kanidm_config_object.radius_dh_path is not None:
|
||||||
cert_dh = Path(kanidm_config_object.radius_dh_path).expanduser().resolve()
|
cert_dh = Path(kanidm_config_object.radius_dh_path).expanduser().resolve()
|
||||||
if not cert_dh.exists():
|
if not cert_dh.exists():
|
||||||
print(f"Failed to find radiusd dh file ({cert_dh}), quitting!", file=sys.stderr)
|
# print(f"Failed to find radiusd dh file ({cert_dh}), quitting!", file=sys.stderr)
|
||||||
sys.exit(1)
|
# sys.exit(1)
|
||||||
|
print(f"Generating dh params in {cert_dh}")
|
||||||
|
subprocess.check_call(["openssl", "dhparam", "-out", cert_dh, "2048"])
|
||||||
if cert_dh != CERT_DH_DEST:
|
if cert_dh != CERT_DH_DEST:
|
||||||
print(f"Copying {cert_dh} to {CERT_DH_DEST}")
|
print(f"Copying {cert_dh} to {CERT_DH_DEST}")
|
||||||
shutil.copyfile(cert_dh, CERT_DH_DEST)
|
shutil.copyfile(cert_dh, CERT_DH_DEST)
|
||||||
|
|
||||||
|
|
||||||
server_key = Path(kanidm_config_object.radius_key_path).expanduser().resolve()
|
server_key = Path(kanidm_config_object.radius_key_path).expanduser().resolve()
|
||||||
if not server_key.exists() or not server_key.is_file():
|
if not server_key.exists() or not server_key.is_file():
|
||||||
print(
|
print(
|
||||||
|
|
|
@ -1,23 +1,29 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -x
|
set -x
|
||||||
|
|
||||||
|
|
||||||
if [ -z "${IMAGE}" ]; then
|
if [ -z "${IMAGE}" ]; then
|
||||||
IMAGE="kanidm/radius:devel"
|
IMAGE="kanidm/radius:devel"
|
||||||
fi
|
fi
|
||||||
echo "Running docker container: ${IMAGE}"
|
echo "Running docker container: ${IMAGE}"
|
||||||
|
|
||||||
|
if [ ! -z "${IMAGE_ARCH}" ]; then
|
||||||
|
IMAGE_ARCH="--platform ${IMAGE_ARCH}"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "${CONFIG_FILE}" ]; then
|
if [ -z "${CONFIG_FILE}" ]; then
|
||||||
CONFIG_FILE="$(pwd)/../examples/kanidm"
|
CONFIG_FILE="$(pwd)/../examples/kanidm"
|
||||||
fi
|
fi
|
||||||
echo "Using config file: ${CONFIG_FILE}"
|
echo "Using config file: ${CONFIG_FILE}"
|
||||||
|
|
||||||
if [ ! -d "/tmp/kanidm/" ]; then
|
if [ ! -d "/tmp/kanidm/" ]; then
|
||||||
echo "Can't find /tmp/kanidm - you might need to run insecure_generate_tls.sh"
|
echo "Can't find /tmp/kanidm - you may need to run run_insecure_dev_server"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Starting the dev container..."
|
echo "Starting the dev container..."
|
||||||
#shellcheck disable=SC2068
|
#shellcheck disable=SC2068
|
||||||
docker run --rm -it \
|
docker run --rm -it \
|
||||||
|
${IMAGE_ARCH} \
|
||||||
--network host \
|
--network host \
|
||||||
--name radiusd \
|
--name radiusd \
|
||||||
-v /tmp/kanidm/:/data/ \
|
-v /tmp/kanidm/:/data/ \
|
||||||
|
|
|
@ -1,11 +1,28 @@
|
||||||
//! This module contains cryptographic setup code, a long with what policy
|
//! This module contains cryptographic setup code, a long with what policy
|
||||||
//! and ciphers we accept.
|
//! and ciphers we accept.
|
||||||
|
|
||||||
|
use openssl::ec::{EcGroup, EcKey};
|
||||||
use openssl::error::ErrorStack;
|
use openssl::error::ErrorStack;
|
||||||
|
use openssl::nid::Nid;
|
||||||
use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod};
|
use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod};
|
||||||
|
use openssl::x509::{
|
||||||
|
extension::{
|
||||||
|
AuthorityKeyIdentifier, BasicConstraints, KeyUsage, SubjectAlternativeName,
|
||||||
|
SubjectKeyIdentifier,
|
||||||
|
},
|
||||||
|
X509NameBuilder, X509ReqBuilder, X509,
|
||||||
|
};
|
||||||
|
use openssl::{asn1, bn, hash, pkey};
|
||||||
|
|
||||||
use crate::config::Configuration;
|
use crate::config::Configuration;
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
const CA_VALID_DAYS: u32 = 30;
|
||||||
|
const CERT_VALID_DAYS: u32 = 5;
|
||||||
|
|
||||||
/// From the server configuration, generate an OpenSSL acceptor that we can use
|
/// From the server configuration, generate an OpenSSL acceptor that we can use
|
||||||
/// to build our sockets for https/ldaps.
|
/// to build our sockets for https/ldaps.
|
||||||
pub fn setup_tls(config: &Configuration) -> Result<Option<SslAcceptorBuilder>, ErrorStack> {
|
pub fn setup_tls(config: &Configuration) -> Result<Option<SslAcceptorBuilder>, ErrorStack> {
|
||||||
|
@ -20,3 +37,264 @@ pub fn setup_tls(config: &Configuration) -> Result<Option<SslAcceptorBuilder>, E
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_group() -> Result<EcGroup, ErrorStack> {
|
||||||
|
EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct CaHandle {
|
||||||
|
key: pkey::PKey<pkey::Private>,
|
||||||
|
cert: X509,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn write_ca(
|
||||||
|
key_ar: impl AsRef<Path>,
|
||||||
|
cert_ar: impl AsRef<Path>,
|
||||||
|
handle: &CaHandle,
|
||||||
|
) -> Result<(), ()> {
|
||||||
|
let key_path: &Path = key_ar.as_ref();
|
||||||
|
let cert_path: &Path = cert_ar.as_ref();
|
||||||
|
|
||||||
|
let key_pem = handle.key.private_key_to_pem_pkcs8().map_err(|e| {
|
||||||
|
error!(err = ?e, "Failed to convert key to PEM");
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let cert_pem = handle.cert.to_pem().map_err(|e| {
|
||||||
|
error!(err = ?e, "Failed to convert cert to PEM");
|
||||||
|
})?;
|
||||||
|
|
||||||
|
File::create(key_path)
|
||||||
|
.and_then(|mut file| file.write_all(&key_pem))
|
||||||
|
.map_err(|e| {
|
||||||
|
error!(err = ?e, "Failed to create {:?}", key_path);
|
||||||
|
})?;
|
||||||
|
|
||||||
|
File::create(cert_path)
|
||||||
|
.and_then(|mut file| file.write_all(&cert_pem))
|
||||||
|
.map_err(|e| {
|
||||||
|
error!(err = ?e, "Failed to create {:?}", cert_path);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn build_ca() -> Result<CaHandle, ErrorStack> {
|
||||||
|
let ecgroup = get_group()?;
|
||||||
|
let eckey = EcKey::generate(&ecgroup)?;
|
||||||
|
let ca_key = pkey::PKey::from_ec_key(eckey)?;
|
||||||
|
let mut x509_name = X509NameBuilder::new()?;
|
||||||
|
|
||||||
|
x509_name.append_entry_by_text("C", "AU")?;
|
||||||
|
x509_name.append_entry_by_text("ST", "QLD")?;
|
||||||
|
x509_name.append_entry_by_text("O", "Kanidm")?;
|
||||||
|
x509_name.append_entry_by_text("CN", "Kanidm Generated CA")?;
|
||||||
|
x509_name.append_entry_by_text("OU", "Development and Evaluation - NOT FOR PRODUCTION")?;
|
||||||
|
let x509_name = x509_name.build();
|
||||||
|
|
||||||
|
let mut cert_builder = X509::builder()?;
|
||||||
|
// Yes, 2 actually means 3 here ...
|
||||||
|
cert_builder.set_version(2)?;
|
||||||
|
|
||||||
|
let serial_number = bn::BigNum::from_u32(1).and_then(|serial| serial.to_asn1_integer())?;
|
||||||
|
|
||||||
|
cert_builder.set_serial_number(&serial_number)?;
|
||||||
|
cert_builder.set_subject_name(&x509_name)?;
|
||||||
|
cert_builder.set_issuer_name(&x509_name)?;
|
||||||
|
|
||||||
|
let not_before = asn1::Asn1Time::days_from_now(0)?;
|
||||||
|
cert_builder.set_not_before(¬_before)?;
|
||||||
|
let not_after = asn1::Asn1Time::days_from_now(CA_VALID_DAYS)?;
|
||||||
|
cert_builder.set_not_after(¬_after)?;
|
||||||
|
|
||||||
|
cert_builder.append_extension(BasicConstraints::new().critical().ca().pathlen(0).build()?)?;
|
||||||
|
cert_builder.append_extension(
|
||||||
|
KeyUsage::new()
|
||||||
|
.critical()
|
||||||
|
.key_cert_sign()
|
||||||
|
.crl_sign()
|
||||||
|
.build()?,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let subject_key_identifier =
|
||||||
|
SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?;
|
||||||
|
cert_builder.append_extension(subject_key_identifier)?;
|
||||||
|
|
||||||
|
cert_builder.set_pubkey(&ca_key)?;
|
||||||
|
|
||||||
|
cert_builder.sign(&ca_key, hash::MessageDigest::sha256())?;
|
||||||
|
let ca_cert = cert_builder.build();
|
||||||
|
|
||||||
|
Ok(CaHandle {
|
||||||
|
key: ca_key,
|
||||||
|
cert: ca_cert,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn load_ca(
|
||||||
|
ca_key_ar: impl AsRef<Path>,
|
||||||
|
ca_cert_ar: impl AsRef<Path>,
|
||||||
|
) -> Result<CaHandle, ()> {
|
||||||
|
let ca_key_path: &Path = ca_key_ar.as_ref();
|
||||||
|
let ca_cert_path: &Path = ca_cert_ar.as_ref();
|
||||||
|
|
||||||
|
let mut ca_key_pem = vec![];
|
||||||
|
File::open(ca_key_path)
|
||||||
|
.and_then(|mut file| file.read_to_end(&mut ca_key_pem))
|
||||||
|
.map_err(|e| {
|
||||||
|
error!(err = ?e, "Failed to read {:?}", ca_key_path);
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut ca_cert_pem = vec![];
|
||||||
|
File::open(ca_cert_path)
|
||||||
|
.and_then(|mut file| file.read_to_end(&mut ca_cert_pem))
|
||||||
|
.map_err(|e| {
|
||||||
|
error!(err = ?e, "Failed to read {:?}", ca_cert_path);
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let ca_key = pkey::PKey::private_key_from_pem(&ca_key_pem).map_err(|e| {
|
||||||
|
error!(err = ?e, "Failed to convert PEM to key");
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let ca_cert = X509::from_pem(&ca_cert_pem).map_err(|e| {
|
||||||
|
error!(err = ?e, "Failed to convert PEM to cert");
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(CaHandle {
|
||||||
|
key: ca_key,
|
||||||
|
cert: ca_cert,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct CertHandle {
|
||||||
|
key: pkey::PKey<pkey::Private>,
|
||||||
|
cert: X509,
|
||||||
|
chain: Vec<X509>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn write_cert(
|
||||||
|
key_ar: impl AsRef<Path>,
|
||||||
|
chain_ar: impl AsRef<Path>,
|
||||||
|
cert_ar: impl AsRef<Path>,
|
||||||
|
handle: &CertHandle,
|
||||||
|
) -> Result<(), ()> {
|
||||||
|
let key_path: &Path = key_ar.as_ref();
|
||||||
|
let chain_path: &Path = chain_ar.as_ref();
|
||||||
|
let cert_path: &Path = cert_ar.as_ref();
|
||||||
|
|
||||||
|
let key_pem = handle.key.private_key_to_pem_pkcs8().map_err(|e| {
|
||||||
|
error!(err = ?e, "Failed to convert key to PEM");
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let cert_pem = handle.cert.to_pem().map_err(|e| {
|
||||||
|
error!(err = ?e, "Failed to convert cert to PEM");
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut chain_pem = cert_pem.clone();
|
||||||
|
|
||||||
|
// Build the chain PEM.
|
||||||
|
for ca_cert in &handle.chain {
|
||||||
|
match ca_cert.to_pem() {
|
||||||
|
Ok(c) => {
|
||||||
|
chain_pem.extend_from_slice(&c);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!(err = ?e, "Failed to convert cert to PEM");
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File::create(key_path)
|
||||||
|
.and_then(|mut file| file.write_all(&key_pem))
|
||||||
|
.map_err(|e| {
|
||||||
|
error!(err = ?e, "Failed to create {:?}", key_path);
|
||||||
|
})?;
|
||||||
|
|
||||||
|
File::create(chain_path)
|
||||||
|
.and_then(|mut file| file.write_all(&chain_pem))
|
||||||
|
.map_err(|e| {
|
||||||
|
error!(err = ?e, "Failed to create {:?}", chain_path);
|
||||||
|
})?;
|
||||||
|
|
||||||
|
File::create(cert_path)
|
||||||
|
.and_then(|mut file| file.write_all(&cert_pem))
|
||||||
|
.map_err(|e| {
|
||||||
|
error!(err = ?e, "Failed to create {:?}", cert_path);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn build_cert(
|
||||||
|
domain_name: &str,
|
||||||
|
ca_handle: &CaHandle,
|
||||||
|
) -> Result<CertHandle, ErrorStack> {
|
||||||
|
let ecgroup = get_group()?;
|
||||||
|
let eckey = EcKey::generate(&ecgroup)?;
|
||||||
|
let int_key = pkey::PKey::from_ec_key(eckey)?;
|
||||||
|
|
||||||
|
//
|
||||||
|
let mut req_builder = X509ReqBuilder::new()?;
|
||||||
|
req_builder.set_pubkey(&int_key)?;
|
||||||
|
|
||||||
|
let mut x509_name = X509NameBuilder::new()?;
|
||||||
|
x509_name.append_entry_by_text("C", "AU")?;
|
||||||
|
x509_name.append_entry_by_text("ST", "QLD")?;
|
||||||
|
x509_name.append_entry_by_text("O", "Kanidm")?;
|
||||||
|
x509_name.append_entry_by_text("CN", domain_name)?;
|
||||||
|
// Requirement of packed attestation.
|
||||||
|
x509_name.append_entry_by_text("OU", "Development and Evaluation - NOT FOR PRODUCTION")?;
|
||||||
|
let x509_name = x509_name.build();
|
||||||
|
|
||||||
|
req_builder.set_subject_name(&x509_name)?;
|
||||||
|
req_builder.sign(&int_key, hash::MessageDigest::sha256())?;
|
||||||
|
let req = req_builder.build();
|
||||||
|
// ==
|
||||||
|
|
||||||
|
let mut cert_builder = X509::builder()?;
|
||||||
|
// Yes, 2 actually means 3 here ...
|
||||||
|
cert_builder.set_version(2)?;
|
||||||
|
let serial_number = bn::BigNum::from_u32(2).and_then(|serial| serial.to_asn1_integer())?;
|
||||||
|
|
||||||
|
cert_builder.set_pubkey(&int_key)?;
|
||||||
|
|
||||||
|
cert_builder.set_serial_number(&serial_number)?;
|
||||||
|
cert_builder.set_subject_name(req.subject_name())?;
|
||||||
|
cert_builder.set_issuer_name(ca_handle.cert.subject_name())?;
|
||||||
|
|
||||||
|
let not_before = asn1::Asn1Time::days_from_now(0)?;
|
||||||
|
cert_builder.set_not_before(¬_before)?;
|
||||||
|
let not_after = asn1::Asn1Time::days_from_now(CERT_VALID_DAYS)?;
|
||||||
|
cert_builder.set_not_after(¬_after)?;
|
||||||
|
|
||||||
|
cert_builder.append_extension(BasicConstraints::new().build()?)?;
|
||||||
|
|
||||||
|
cert_builder.append_extension(
|
||||||
|
KeyUsage::new()
|
||||||
|
.critical()
|
||||||
|
.non_repudiation()
|
||||||
|
.digital_signature()
|
||||||
|
.key_encipherment()
|
||||||
|
.build()?,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let subject_key_identifier = SubjectKeyIdentifier::new()
|
||||||
|
.build(&cert_builder.x509v3_context(Some(&ca_handle.cert), None))?;
|
||||||
|
cert_builder.append_extension(subject_key_identifier)?;
|
||||||
|
|
||||||
|
let auth_key_identifier = AuthorityKeyIdentifier::new()
|
||||||
|
.keyid(false)
|
||||||
|
.issuer(false)
|
||||||
|
.build(&cert_builder.x509v3_context(Some(&ca_handle.cert), None))?;
|
||||||
|
cert_builder.append_extension(auth_key_identifier)?;
|
||||||
|
|
||||||
|
let subject_alt_name = SubjectAlternativeName::new()
|
||||||
|
.dns(domain_name)
|
||||||
|
.build(&cert_builder.x509v3_context(Some(&ca_handle.cert), None))?;
|
||||||
|
cert_builder.append_extension(subject_alt_name)?;
|
||||||
|
|
||||||
|
cert_builder.sign(&ca_handle.key, hash::MessageDigest::sha256())?;
|
||||||
|
let int_cert = cert_builder.build();
|
||||||
|
|
||||||
|
Ok(CertHandle {
|
||||||
|
key: int_key,
|
||||||
|
cert: int_cert,
|
||||||
|
chain: vec![ca_handle.cert.clone()],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ pub mod https;
|
||||||
mod interval;
|
mod interval;
|
||||||
mod ldaps;
|
mod ldaps;
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use compact_jwt::JwsSigner;
|
use compact_jwt::JwsSigner;
|
||||||
|
@ -51,7 +52,6 @@ use tokio::sync::broadcast;
|
||||||
use crate::actors::v1_read::QueryServerReadV1;
|
use crate::actors::v1_read::QueryServerReadV1;
|
||||||
use crate::actors::v1_write::QueryServerWriteV1;
|
use crate::actors::v1_write::QueryServerWriteV1;
|
||||||
use crate::config::Configuration;
|
use crate::config::Configuration;
|
||||||
use crate::crypto::setup_tls;
|
|
||||||
use crate::interval::IntervalActor;
|
use crate::interval::IntervalActor;
|
||||||
|
|
||||||
// === internal setup helpers
|
// === internal setup helpers
|
||||||
|
@ -553,6 +553,98 @@ pub async fn recover_account_core(config: &Configuration, name: &str) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cert_generate_core(config: &Configuration) {
|
||||||
|
// Get the cert root
|
||||||
|
|
||||||
|
let (tls_key_path, tls_chain_path) = match &config.tls_config {
|
||||||
|
Some(tls_config) => (
|
||||||
|
Path::new(tls_config.key.as_str()),
|
||||||
|
Path::new(tls_config.chain.as_str()),
|
||||||
|
),
|
||||||
|
None => {
|
||||||
|
error!("Unable to find tls configuration");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if tls_key_path.exists() && tls_chain_path.exists() {
|
||||||
|
info!(
|
||||||
|
"tls key and chain already exist - remove them first if you intend to regenerate these"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let origin = match Url::parse(&config.origin) {
|
||||||
|
Ok(url) => url,
|
||||||
|
Err(e) => {
|
||||||
|
error!(err = ?e, "Unable to parse origin URL - refusing to start. You must correct the value for origin. {:?}", config.origin);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let origin_domain = if let Some(d) = origin.domain() {
|
||||||
|
d
|
||||||
|
} else {
|
||||||
|
error!("origin does not contain a valid domain");
|
||||||
|
std::process::exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
let cert_root = match tls_key_path.parent() {
|
||||||
|
Some(parent) => parent,
|
||||||
|
None => {
|
||||||
|
error!("Unable to find parent directory of {:?}", tls_key_path);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ca_cert = cert_root.join("ca.pem");
|
||||||
|
let ca_key = cert_root.join("cakey.pem");
|
||||||
|
let tls_cert_path = cert_root.join("cert.pem");
|
||||||
|
|
||||||
|
let ca_handle = if !ca_cert.exists() || !ca_key.exists() {
|
||||||
|
// Generate the CA again.
|
||||||
|
let ca_handle = match crypto::build_ca() {
|
||||||
|
Ok(ca_handle) => ca_handle,
|
||||||
|
Err(e) => {
|
||||||
|
error!(err = ?e, "Failed to build CA");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if crypto::write_ca(ca_key, ca_cert, &ca_handle).is_err() {
|
||||||
|
error!("Failed to write CA");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ca_handle
|
||||||
|
} else {
|
||||||
|
match crypto::load_ca(ca_key, ca_cert) {
|
||||||
|
Ok(ca_handle) => ca_handle,
|
||||||
|
Err(_) => {
|
||||||
|
error!("Failed to load CA");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !tls_key_path.exists() || !tls_chain_path.exists() || !tls_cert_path.exists() {
|
||||||
|
// Generate the cert from the ca.
|
||||||
|
let cert_handle = match crypto::build_cert(origin_domain, &ca_handle) {
|
||||||
|
Ok(cert_handle) => cert_handle,
|
||||||
|
Err(e) => {
|
||||||
|
error!(err = ?e, "Failed to build certificate");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if crypto::write_cert(tls_key_path, tls_chain_path, tls_cert_path, &cert_handle).is_err() {
|
||||||
|
error!("Failed to write certificates");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info!("certificate generation complete");
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum CoreAction {
|
pub enum CoreAction {
|
||||||
Shutdown,
|
Shutdown,
|
||||||
|
@ -626,7 +718,7 @@ pub async fn create_server_core(
|
||||||
let status_ref = StatusActor::start();
|
let status_ref = StatusActor::start();
|
||||||
|
|
||||||
// Setup TLS (if any)
|
// Setup TLS (if any)
|
||||||
let _opt_tls_params = match setup_tls(&config) {
|
let _opt_tls_params = match crypto::setup_tls(&config) {
|
||||||
Ok(opt_tls_params) => opt_tls_params,
|
Ok(opt_tls_params) => opt_tls_params,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to configure TLS parameters -> {:?}", e);
|
error!("Failed to configure TLS parameters -> {:?}", e);
|
||||||
|
@ -784,7 +876,7 @@ pub async fn create_server_core(
|
||||||
// If we have been requested to init LDAP, configure it now.
|
// If we have been requested to init LDAP, configure it now.
|
||||||
let maybe_ldap_acceptor_handle = match &config.ldapaddress {
|
let maybe_ldap_acceptor_handle = match &config.ldapaddress {
|
||||||
Some(la) => {
|
Some(la) => {
|
||||||
let opt_ldap_tls_params = match setup_tls(&config) {
|
let opt_ldap_tls_params = match crypto::setup_tls(&config) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to configure LDAP TLS parameters -> {:?}", e);
|
error!("Failed to configure LDAP TLS parameters -> {:?}", e);
|
||||||
|
|
|
@ -2,10 +2,6 @@
|
||||||
|
|
||||||
# This script based on the developer readme and allows you to run a test server.
|
# This script based on the developer readme and allows you to run a test server.
|
||||||
|
|
||||||
if [ -z "$KANI_TMP" ]; then
|
|
||||||
KANI_TMP=/tmp/kanidm
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$KANI_CARGO_OPTS" ]; then
|
if [ -z "$KANI_CARGO_OPTS" ]; then
|
||||||
KANI_CARGO_OPTS=""
|
KANI_CARGO_OPTS=""
|
||||||
fi
|
fi
|
||||||
|
@ -17,14 +13,9 @@ if [ ! -f "${CONFIG_FILE}" ]; then
|
||||||
echo "Couldn't find configuration file at ${CONFIG_FILE}, please ensure you're running this script from its base directory (${SCRIPT_DIR})."
|
echo "Couldn't find configuration file at ${CONFIG_FILE}, please ensure you're running this script from its base directory (${SCRIPT_DIR})."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ ! -f "${KANI_TMP}/chain.pem" ]; then
|
|
||||||
echo "Couldn't find certificate at /tmp/kanidm/chain.pem, quitting"
|
#shellcheck disable=SC2086
|
||||||
exit 1
|
cargo run ${KANI_CARGO_OPTS} --bin kanidmd -- cert-generate -c "${CONFIG_FILE}"
|
||||||
fi
|
|
||||||
if [ ! -f "${KANI_TMP}/key.pem" ]; then
|
|
||||||
echo "Couldn't find key file at /tmp/kanidm/key.pem, quitting"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
COMMAND="server"
|
COMMAND="server"
|
||||||
if [ -n "${1}" ]; then
|
if [ -n "${1}" ]; then
|
||||||
|
|
|
@ -25,10 +25,10 @@ use std::process::ExitCode;
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
use kanidmd_core::config::{Configuration, ServerConfig};
|
use kanidmd_core::config::{Configuration, ServerConfig};
|
||||||
use kanidmd_core::{
|
use kanidmd_core::{
|
||||||
backup_server_core, create_server_core, dbscan_get_id2entry_core, dbscan_list_id2entry_core,
|
backup_server_core, cert_generate_core, create_server_core, dbscan_get_id2entry_core,
|
||||||
dbscan_list_index_analysis_core, dbscan_list_index_core, dbscan_list_indexes_core,
|
dbscan_list_id2entry_core, dbscan_list_index_analysis_core, dbscan_list_index_core,
|
||||||
domain_rename_core, recover_account_core, reindex_server_core, restore_server_core,
|
dbscan_list_indexes_core, domain_rename_core, recover_account_core, reindex_server_core,
|
||||||
vacuum_server_core, verify_server_core,
|
restore_server_core, vacuum_server_core, verify_server_core,
|
||||||
};
|
};
|
||||||
use sketching::tracing_forest::traits::*;
|
use sketching::tracing_forest::traits::*;
|
||||||
use sketching::tracing_forest::util::*;
|
use sketching::tracing_forest::util::*;
|
||||||
|
@ -44,6 +44,7 @@ impl KanidmdOpt {
|
||||||
fn commonopt(&self) -> &CommonOpt {
|
fn commonopt(&self) -> &CommonOpt {
|
||||||
match self {
|
match self {
|
||||||
KanidmdOpt::Server(sopt)
|
KanidmdOpt::Server(sopt)
|
||||||
|
| KanidmdOpt::CertGenerate(sopt)
|
||||||
| KanidmdOpt::ConfigTest(sopt)
|
| KanidmdOpt::ConfigTest(sopt)
|
||||||
| KanidmdOpt::DbScan {
|
| KanidmdOpt::DbScan {
|
||||||
commands: DbScanOpt::ListIndexes(sopt),
|
commands: DbScanOpt::ListIndexes(sopt),
|
||||||
|
@ -148,7 +149,18 @@ async fn main() -> ExitCode {
|
||||||
// Check the permissions are OK.
|
// Check the permissions are OK.
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
{
|
{
|
||||||
let cfg_path = &opt.commands.commonopt().config_path;
|
let cfg_path = &opt.commands.commonopt().config_path; // TODO: this needs to be pulling from the default or something?
|
||||||
|
if format!("{}", cfg_path.display()) == "".to_string() {
|
||||||
|
error!("Refusing to run - config file path is empty");
|
||||||
|
return ExitCode::FAILURE
|
||||||
|
}
|
||||||
|
if !cfg_path.exists() {
|
||||||
|
error!(
|
||||||
|
"Refusing to run - config file {} does not exist",
|
||||||
|
cfg_path.to_str().unwrap_or("invalid file path")
|
||||||
|
);
|
||||||
|
return ExitCode::FAILURE
|
||||||
|
}
|
||||||
let cfg_meta = match metadata(cfg_path) {
|
let cfg_meta = match metadata(cfg_path) {
|
||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -381,8 +393,11 @@ async fn main() -> ExitCode {
|
||||||
}
|
}
|
||||||
info!("Stopped 🛑 ");
|
info!("Stopped 🛑 ");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
KanidmdOpt::CertGenerate(_sopt) => {
|
||||||
|
info!("Running in certificate generate mode ...");
|
||||||
|
config.update_config_for_server_mode(&sconfig);
|
||||||
|
cert_generate_core(&config);
|
||||||
}
|
}
|
||||||
KanidmdOpt::Database {
|
KanidmdOpt::Database {
|
||||||
commands: DbCommands::Backup(bopt),
|
commands: DbCommands::Backup(bopt),
|
||||||
|
|
|
@ -136,6 +136,11 @@ enum KanidmdOpt {
|
||||||
#[clap(name = "configtest")]
|
#[clap(name = "configtest")]
|
||||||
/// Test the IDM Server configuration, without starting network listeners.
|
/// Test the IDM Server configuration, without starting network listeners.
|
||||||
ConfigTest(CommonOpt),
|
ConfigTest(CommonOpt),
|
||||||
|
#[clap(name = "cert-generate")]
|
||||||
|
/// Create a self-signed ca and tls certificate in the locations listed from the
|
||||||
|
/// configuration. These certificates should *not* be used in production, they
|
||||||
|
/// are for testing and evaluation only!
|
||||||
|
CertGenerate(CommonOpt),
|
||||||
#[clap(name = "recover-account")]
|
#[clap(name = "recover-account")]
|
||||||
/// Recover an account's password
|
/// Recover an account's password
|
||||||
RecoverAccount(RecoverAccountOpt),
|
RecoverAccount(RecoverAccountOpt),
|
||||||
|
|
Loading…
Reference in a new issue