mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
Windows build support (#903)
`kanidmd` builds and runs in Windows now. Currently skipping file permissions checks on startup, but it's tested OK on a Windows 10 box.
This commit is contained in:
parent
4830479bd5
commit
fedc21ddca
4
.github/workflows/windows_build.yml
vendored
4
.github/workflows/windows_build.yml
vendored
|
@ -28,7 +28,7 @@ jobs:
|
|||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --release -p kanidm_client -p kanidm_tools -p orca
|
||||
args: --release -p kanidm_client -p kanidm_tools -p orca -p daemon
|
||||
windows_test_kanidm:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
|
@ -47,4 +47,4 @@ jobs:
|
|||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: -p kanidm_client -p kanidm_tools -p orca
|
||||
args: -p kanidm_client -p kanidm_tools -p orca -p daemon -p score
|
||||
|
|
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -1046,6 +1046,7 @@ dependencies = [
|
|||
"tokio",
|
||||
"toml",
|
||||
"users",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2069,6 +2070,7 @@ dependencies = [
|
|||
"validator",
|
||||
"webauthn-authenticator-rs",
|
||||
"webauthn-rs",
|
||||
"whoami",
|
||||
"zxcvbn",
|
||||
]
|
||||
|
||||
|
@ -2252,6 +2254,7 @@ version = "0.24.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
@ -4471,6 +4474,16 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "whoami"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524b58fa5a20a2fb3014dd6358b70e6579692a56ef6fce928834e488f42f65e8"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
|
|
@ -61,6 +61,25 @@ sudo apt-get install libsqlite3-dev libudev-dev libssl-dev pkg-config libpam0g-d
|
|||
|
||||
Tested with Ubuntu 20.04 and 22.04.
|
||||
|
||||
#### Windows
|
||||
|
||||
You need [rustup](https://rustup.rs/) to install a Rust toolchain.
|
||||
|
||||
An easy way to grab the dependencies is to install [vcpkg](https://vcpkg.io/en/getting-started.html).
|
||||
|
||||
This is how it works in the automated build:
|
||||
|
||||
1. Enable use of installed packages for the user system-wide:
|
||||
```shell
|
||||
vcpkg integrate install
|
||||
```
|
||||
2. Install the openssl dependency, which compiles it from source. This downloads all sorts of dependencies, including perl for the build.
|
||||
```shell
|
||||
vcpkg install openssl:x64-windows-static-md
|
||||
```
|
||||
|
||||
There's a powershell script in the root directory of the repository which, in concert with `openssl` will generate a config file and certs for testing.
|
||||
|
||||
### Get Involved
|
||||
|
||||
To get started, you'll need to fork or branch, and we'll merge based on pull
|
||||
|
@ -140,8 +159,11 @@ Once you have the source code, you need encryption certificates to use with the
|
|||
because without certificates, authentication will fail.
|
||||
|
||||
We recommend using [Let's Encrypt](https://letsencrypt.org), but if this is not
|
||||
possible, please use our insecure certificate tool (`insecure_generate_tls.sh`). The
|
||||
insecure certificate tool creates `/tmp/kanidm` and puts some self-signed certificates there.
|
||||
possible, please use our insecure certificate tool (`insecure_generate_tls.sh`).
|
||||
|
||||
__NOTE:__ Windows developers can use `insecure_generate_tls.ps1`, which puts everything (including a templated confi gfile) in `$TEMP\kanidm`. Please adjust paths below to suit.
|
||||
|
||||
The insecure certificate tool creates `/tmp/kanidm` and puts some self-signed certificates there.
|
||||
|
||||
You can now build and run the server with the commands below. It will use a database
|
||||
in `/tmp/kanidm.db`.
|
||||
|
|
106
insecure_generate_tls.ps1
Normal file
106
insecure_generate_tls.ps1
Normal file
|
@ -0,0 +1,106 @@
|
|||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$KANI_TMP="$Env:TEMP\kanidm\"
|
||||
|
||||
$ALTNAME_FILE="${KANI_TMP}altnames.cnf"
|
||||
$CACERT="${KANI_TMP}ca.pem"
|
||||
$CAKEY="${KANI_TMP}cakey.pem"
|
||||
|
||||
$KEYFILE="${KANI_TMP}key.pem"
|
||||
$CERTFILE="${KANI_TMP}cert.pem"
|
||||
$CSRFILE="${KANI_TMP}cert.csr"
|
||||
$CHAINFILE="${KANI_TMP}chain.pem"
|
||||
# $DHFILE="${KANI_TMP}dh.pem"
|
||||
$CONFIG_FILE="${KANI_TMP}server.toml"
|
||||
|
||||
|
||||
if (Test-Path -Path "$KANI_TMP" ) {
|
||||
Write-Output "Output dir exists at $KANI_TMP"
|
||||
} else {
|
||||
Write-Warning "Output dir missing at $KANI_TMP"
|
||||
$result = New-Item -Path "$KANI_TMP" -ItemType Directory
|
||||
}
|
||||
|
||||
|
||||
if ( $(Test-Path -Path "examples\insecure_server.toml") -eq $false ) {
|
||||
Write-Error "You need to run this from the base dir of the repo!"
|
||||
exit 1
|
||||
}
|
||||
# Building the config file
|
||||
$CONFIG = Get-Content "examples\insecure_server.toml"
|
||||
$CONFIG = $CONFIG -replace "/tmp/kanidm/", "$KANI_TMP"
|
||||
$CONFIG = $CONFIG -replace "\\", "/"
|
||||
|
||||
$CONFIG | Set-Content "${CONFIG_FILE}" -Force
|
||||
|
||||
$ALTNAME_FILE_CONTENTS = @'
|
||||
[req]
|
||||
nsComment = "Certificate"
|
||||
distinguished_name = req_distinguished_name
|
||||
req_extensions = v3_req
|
||||
|
||||
[ req_distinguished_name ]
|
||||
|
||||
countryName = Country Name (2 letter code)
|
||||
countryName_default = AU
|
||||
countryName_min = 2
|
||||
countryName_max = 2
|
||||
|
||||
stateOrProvinceName = State or Province Name (full name)
|
||||
stateOrProvinceName_default = Queensland
|
||||
|
||||
localityName = Locality Name (eg, city)
|
||||
localityName_default = Brisbane
|
||||
|
||||
0.organizationName = Organization Name (eg, company)
|
||||
0.organizationName_default = INSECURE EXAMPLE
|
||||
|
||||
organizationalUnitName = Organizational Unit Name (eg, section)
|
||||
organizationalUnitName_default = kanidm
|
||||
|
||||
commonName = Common Name (eg, your name or your servers hostname)
|
||||
commonName_max = 64
|
||||
commonName_default = localhost
|
||||
|
||||
[ v3_req ]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = localhost
|
||||
IP.1 = 127.0.0.1
|
||||
'@
|
||||
|
||||
Write-Output "Creating cert template"
|
||||
$result = New-Item -Path "$ALTNAME_FILE" -ItemType File -Value "$ALTNAME_FILE_CONTENTS" -Force
|
||||
|
||||
write-debug $result
|
||||
|
||||
Write-Output "Generate the CA"
|
||||
openssl req -x509 -new -newkey rsa:4096 -sha256 -keyout "${CAKEY}" -out "${CACERT}" -days 31 -subj "/C=AU/ST=Queensland/L=Brisbane/O=INSECURE/CN=insecure.ca.localhost" -nodes
|
||||
if ( $LastExitCode -ne 0 ){
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Output "Generating the private key"
|
||||
openssl genrsa -out "${KEYFILE}" 4096
|
||||
if ( $LastExitCode -ne 0 ){
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Output "Generating the certficate signing request"
|
||||
openssl req -sha256 -config "${ALTNAME_FILE}" -days 31 -new -extensions v3_req -key "${KEYFILE}" -out "${CSRFILE}"
|
||||
if ( $LastExitCode -ne 0 ){
|
||||
exit 1
|
||||
}
|
||||
Write-Output "Signing the certificate"
|
||||
openssl x509 -req -days 31 -extfile "${ALTNAME_FILE}" -CA "${CACERT}" -CAkey "${CAKEY}" -CAcreateserial -in "${CSRFILE}" -out "${CERTFILE}" -extensions v3_req -sha256
|
||||
|
||||
Write-Output "Creating the certificate chain"
|
||||
Get-Content "${CERTFILE}" ,"${CACERT}" | Set-Content "${CHAINFILE}" -Force
|
||||
|
||||
Write-Output "Certificate chain is at: ${CHAINFILE}"
|
||||
Write-Output "Private key is at: ${KEYFILE}"
|
||||
Write-Output "The configuration file is at: ${CONFIG_FILE}"
|
|
@ -11,7 +11,7 @@ fi
|
|||
echo "Using config file: ${CONFIG_FILE}"
|
||||
|
||||
if [ ! -d "/tmp/kanidm/" ]; then
|
||||
echo "Can't find /tmp/kanidm - you might need to run insecure_generate_certs.sh"
|
||||
echo "Can't find /tmp/kanidm - you might need to run insecure_generate_tls.sh"
|
||||
fi
|
||||
|
||||
echo "Starting the dev container..."
|
||||
|
|
|
@ -71,8 +71,6 @@ r2d2_sqlite = "^0.20.0"
|
|||
reqwest = "^0.11.11"
|
||||
|
||||
users = "^0.11.0"
|
||||
#async-std = { version = "^1.11.0", features = ["tokio1"] }
|
||||
|
||||
|
||||
lru = "^0.7.7"
|
||||
|
||||
|
|
|
@ -21,12 +21,15 @@ kanidm = { path = "../idm" }
|
|||
kanidm_proto = { path = "../../kanidm_proto" }
|
||||
score = { path = "../score" }
|
||||
clap = { version = "^3.2", features = ["derive", "env"] }
|
||||
users = "^0.11.0"
|
||||
serde = { version = "^1.0.138", features = ["derive"] }
|
||||
tokio = { version = "^1.19.1", features = ["rt-multi-thread", "macros", "signal"] }
|
||||
toml = "0.5.9"
|
||||
|
||||
[target.'cfg(target_family = "windows")'.dependencies]
|
||||
whoami = "^1.2.1"
|
||||
|
||||
[target.'cfg(not(target_family = "windows"))'.dependencies]
|
||||
users = "^0.11.0"
|
||||
tikv-jemallocator = "0.5"
|
||||
|
||||
[build-dependencies]
|
||||
|
|
|
@ -14,7 +14,10 @@
|
|||
#[global_allocator]
|
||||
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||
|
||||
#[cfg(not(target_family = "windows"))] // not needed for windows builds
|
||||
use users::{get_current_gid, get_current_uid, get_effective_gid, get_effective_uid};
|
||||
#[cfg(target_family = "windows")] // for windows builds
|
||||
use whoami;
|
||||
|
||||
use serde::Deserialize;
|
||||
use std::fs::{metadata, File, Metadata};
|
||||
|
@ -30,6 +33,7 @@ use std::str::FromStr;
|
|||
use kanidm::audit::LogLevel;
|
||||
use kanidm::config::{Configuration, OnlineBackup, ServerRole};
|
||||
use kanidm::tracing_tree;
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
use kanidm::utils::file_permissions_readonly;
|
||||
use score::{
|
||||
backup_server_core, create_server_core, dbscan_get_id2entry_core, dbscan_list_id2entry_core,
|
||||
|
@ -120,11 +124,9 @@ fn read_file_metadata(path: &PathBuf) -> Metadata {
|
|||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tracing_tree::main_init();
|
||||
|
||||
// Get info about who we are.
|
||||
/// Gets the user details if we're running in unix-land
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
fn get_user_details_unix() -> (u32, u32) {
|
||||
let cuid = get_current_uid();
|
||||
let ceuid = get_effective_uid();
|
||||
let cgid = get_current_gid();
|
||||
|
@ -141,29 +143,59 @@ async fn main() {
|
|||
eprintln!("ERROR: Refusing to run - uid and euid OR gid and egid must be consistent.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
(cuid, ceuid)
|
||||
}
|
||||
|
||||
/// Get information on the windows username
|
||||
#[cfg(target_family = "windows")]
|
||||
fn get_user_details_windows() {
|
||||
eprintln!(
|
||||
"Running on windows, current username is: {:?}",
|
||||
whoami::username()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tracing_tree::main_init();
|
||||
|
||||
// Get information on the windows username
|
||||
#[cfg(target_family = "windows")]
|
||||
get_user_details_windows();
|
||||
|
||||
// Get info about who we are.
|
||||
#[cfg(target_family = "unix")]
|
||||
let (cuid, ceuid) = get_user_details_unix();
|
||||
|
||||
// Read cli args, determine if we should backup/restore
|
||||
let opt = KanidmdParser::parse();
|
||||
|
||||
let mut config = Configuration::new();
|
||||
// Check the permissions are sane.
|
||||
// Check the permissions are OK.
|
||||
#[cfg(target_family = "unix")]
|
||||
{
|
||||
let cfg_meta = read_file_metadata(&(opt.commands.commonopt().config_path));
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
if !file_permissions_readonly(&cfg_meta) {
|
||||
eprintln!("WARNING: permissions on {} may not be secure. Should be readonly to running uid. This could be a security risk ...",
|
||||
opt.commands.commonopt().config_path.to_str().unwrap_or("invalid file path"));
|
||||
}
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
if cfg_meta.mode() & 0o007 != 0 {
|
||||
eprintln!("WARNING: {} has 'everyone' permission bits in the mode. This could be a security risk ...",
|
||||
opt.commands.commonopt().config_path.to_str().unwrap_or("invalid file path")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
if cfg_meta.uid() == cuid || cfg_meta.uid() == ceuid {
|
||||
eprintln!("WARNING: {} owned by the current uid, which may allow file permission changes. This could be a security risk ...",
|
||||
opt.commands.commonopt().config_path.to_str().unwrap_or("invalid file path")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Read our config
|
||||
let sconfig = match ServerConfig::new(&(opt.commands.commonopt().config_path)) {
|
||||
|
@ -205,14 +237,18 @@ async fn main() {
|
|||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
// TODO: windows support for DB folder permissions checks
|
||||
#[cfg(target_family = "unix")]
|
||||
{
|
||||
if !file_permissions_readonly(&i_meta) {
|
||||
eprintln!("WARNING: DB folder permissions on {} indicate it may not be RW. This could cause the server start up to fail!", db_par_path_buf.to_str().unwrap_or("invalid file path"));
|
||||
}
|
||||
|
||||
if i_meta.mode() & 0o007 != 0 {
|
||||
eprintln!("WARNING: DB folder {} has 'everyone' permission bits in the mode. This could be a security risk ...", db_par_path_buf.to_str().unwrap_or("invalid file path"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config.update_log_level(ll);
|
||||
config.update_db_path(&sconfig.db_path.as_str());
|
||||
|
@ -251,23 +287,37 @@ async fn main() {
|
|||
|
||||
if let Some(i_str) = &(sconfig.tls_chain) {
|
||||
let i_path = PathBuf::from(i_str.as_str());
|
||||
// TODO: windows support for DB folder permissions checks
|
||||
#[cfg(not(target_family = "unix"))]
|
||||
eprintln!("WARNING: permissions checks on windows aren't implemented, cannot check TLS Key at {:?}", i_path);
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
{
|
||||
let i_meta = read_file_metadata(&i_path);
|
||||
if !file_permissions_readonly(&i_meta) {
|
||||
eprintln!("WARNING: permissions on {} may not be secure. Should be readonly to running uid. This could be a security risk ...", i_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(i_str) = &(sconfig.tls_key) {
|
||||
let i_path = PathBuf::from(i_str.as_str());
|
||||
// TODO: windows support for DB folder permissions checks
|
||||
#[cfg(not(target_family = "unix"))]
|
||||
eprintln!("WARNING: permissions checks on windows aren't implemented, cannot check TLS Key at {:?}", i_path);
|
||||
|
||||
// TODO: windows support for DB folder permissions checks
|
||||
#[cfg(target_family = "unix")]
|
||||
{
|
||||
let i_meta = read_file_metadata(&i_path);
|
||||
if !file_permissions_readonly(&i_meta) {
|
||||
eprintln!("WARNING: permissions on {} may not be secure. Should be readonly to running uid. This could be a security risk ...", i_str);
|
||||
}
|
||||
|
||||
if i_meta.mode() & 0o007 != 0 {
|
||||
eprintln!("WARNING: {} has 'everyone' permission bits in the mode. This could be a security risk ...", i_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let sctx = create_server_core(config, config_test).await;
|
||||
if !config_test {
|
||||
|
|
|
@ -40,7 +40,6 @@ r2d2 = "^0.8.9"
|
|||
r2d2_sqlite = "^0.20.0"
|
||||
rand = "^0.8.5"
|
||||
regex = "^1.5.6"
|
||||
rusqlite = "^0.27.0"
|
||||
saffron = "^0.1.0"
|
||||
serde = { version = "^1.0.138", features = ["derive"] }
|
||||
serde_cbor = "^0.11.2"
|
||||
|
@ -58,12 +57,27 @@ tracing = { version = "^0.1.35", features = ["attributes"] }
|
|||
tracing-serde = "^0.1.3"
|
||||
tracing-subscriber = { version = "^0.3.14", features = ["env-filter"] }
|
||||
url = { version = "^2.2.2", features = ["serde"] }
|
||||
users = "^0.11.0"
|
||||
uuid = { version = "^1.1.2", features = ["serde", "v4" ] }
|
||||
validator = { version = "^0.15.0", features = ["phone"] }
|
||||
webauthn-rs = "^0.3.2"
|
||||
zxcvbn = "^2.2.1"
|
||||
|
||||
# because windows really can't build without the bundled one
|
||||
[target.'cfg(target_family = "windows")'.dependencies.rusqlite]
|
||||
version = "^0.27.0"
|
||||
features = ["bundled"]
|
||||
|
||||
[target.'cfg(not(target_family = "windows"))'.dependencies.rusqlite]
|
||||
version = "^0.27.0"
|
||||
|
||||
[target.'cfg(target_family = "windows")'.dependencies]
|
||||
whoami = "^1.2.1"
|
||||
|
||||
|
||||
[target.'cfg(not(target_family = "windows"))'.dependencies]
|
||||
users = "^0.11.0"
|
||||
|
||||
|
||||
[features]
|
||||
# default = [ "libsqlite3-sys/bundled", "openssl/vendored" ]
|
||||
|
||||
|
|
|
@ -10,14 +10,16 @@ use uuid::{Builder, Uuid};
|
|||
use rand::distributions::Distribution;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
use std::fs::Metadata;
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
#[cfg(target_os = "macos")]
|
||||
use std::os::macos::fs::MetadataExt;
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::os::windows::fs::MetadataExt;
|
||||
// #[cfg(target_os = "windows")]
|
||||
// use std::os::windows::fs::MetadataExt;
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
use users::{get_current_gid, get_current_uid};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -32,6 +32,7 @@ mod ldaps;
|
|||
use async_std::task;
|
||||
use compact_jwt::JwsSigner;
|
||||
use kanidm::prelude::*;
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
use libc::umask;
|
||||
|
||||
use kanidm::actors::v1_read::QueryServerReadV1;
|
||||
|
@ -551,7 +552,10 @@ pub async fn create_server_core(config: Configuration, config_test: bool) -> Res
|
|||
config
|
||||
);
|
||||
// Setup umask, so that every we touch or create is secure.
|
||||
let _ = unsafe { umask(0o0027) };
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
unsafe {
|
||||
umask(0o0027)
|
||||
};
|
||||
|
||||
// Similar, create a stats task which aggregates statistics from the
|
||||
// server as they come in.
|
||||
|
|
Loading…
Reference in a new issue