Improve server hardening

This adds a number of warnings to the server to help administrators make
better informed decisions about the security of their environment.
This commit is contained in:
William Brown 2020-07-28 16:55:58 +10:00 committed by Firstyear
parent cdd7e0e49a
commit c4805d2915
17 changed files with 471 additions and 45 deletions

13
Cargo.lock generated
View file

@ -1566,6 +1566,7 @@ dependencies = [
"kanidm_proto",
"lazy_static",
"ldap3_server",
"libc",
"libsqlite3-sys",
"log",
"num_cpus",
@ -1587,6 +1588,7 @@ dependencies = [
"tokio-openssl",
"tokio-util 0.2.0",
"toml",
"users",
"uuid",
"zxcvbn",
]
@ -1666,6 +1668,7 @@ dependencies = [
"tokio",
"tokio-util 0.3.1",
"toml",
"users",
]
[[package]]
@ -3301,6 +3304,16 @@ dependencies = [
"percent-encoding 2.1.0",
]
[[package]]
name = "users"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa4227e95324a443c9fcb06e03d4d85e91aabe9a5a02aa818688b6918b6af486"
dependencies = [
"libc",
"log",
]
[[package]]
name = "uuid"
version = "0.8.1"

View file

@ -1,7 +1,8 @@
[profile.release]
debug = true
lto = true
# Have to disable to allow aarch64 to build
# lto = true
[workspace]
members = [

View file

@ -9,7 +9,8 @@ help:
buildx/kanidmd: ## build multiarch images
buildx/kanidmd:
@docker buildx build --progress plain --platform linux/arm64 -f kanidmd/Dockerfile -t $(IMAGE_BASE)/server:$(IMAGE_VERSION) .
@docker buildx build --push --platform linux/amd64,linux/arm64 -f kanidmd/Dockerfile -t $(IMAGE_BASE)/server:$(IMAGE_VERSION) .
@docker buildx imagetools inspect $(IMAGE_BASE)/server:$(IMAGE_VERSION)
build/kanidmd: ## build kanidmd images
build/kanidmd:

View file

@ -12,6 +12,7 @@
- [Password Quality and Badlisting](./password_quality.md)
- [Recycle Bin](./recycle_bin.md)
- [Legacy Applications -- LDAP](./ldap.md)
- [Security Hardening](./security_hardening.md)
-----------
[Why TLS?](./why_tls.md)

View file

@ -0,0 +1,117 @@
# Security Hardening
Kanidm ships with a secure-by-default configuration, however that is only as strong
as the platform that Kanidm operates in. This could be your container environment
or your Unix-like system.
This chapter will detail a number of warnings and security practices you should
follow to ensure that Kanidm operates in a secure environment.
The main server is a high-value target for a potential attack, as Kanidm serves as
the authority on identity and authorisation in a network. Compromise of the Kanidm
server is equivalent to a full-network take over, AKA "game over".
The unixd resolver is also a high value target as it can be accessed to allow unauthorised
access to a server, to intercept communications to the server, or more. This also must be protected
carfully.
For this reason the kanidm's components must be protected carefully. Kanidm avoids many classic
attacks by being developed in a memory safe language, but risks still exist.
At startup Kanidm will warn you if the environment it is running in is suspicious or
has risks. For example:
kanidmd server -c /tmp/server.toml
WARNING: permissions on /tmp/server.toml may not be secure. Should be readonly to running uid. This could be a security risk ...
WARNING: /tmp/server.toml has 'everyone' permission bits in the mode. This could be a security risk ...
WARNING: /tmp/server.toml owned by the current uid, which may allow file permission changes. This could be a security risk ...
WARNING: permissions on ../insecure/ca.pem may not be secure. Should be readonly to running uid. This could be a security risk ...
WARNING: permissions on ../insecure/cert.pem may not be secure. Should be readonly to running uid. This could be a security risk ...
WARNING: permissions on ../insecure/key.pem may not be secure. Should be readonly to running uid. This could be a security risk ...
WARNING: ../insecure/key.pem has 'everyone' permission bits in the mode. This could be a security risk ...
WARNING: DB folder /tmp has 'everyone' permission bits in the mode. This could be a security risk ...
Each warning highlights an issue that may exist in your environment. It is not possible for us to
prescribe an exact configuration that may secure your system. This is why we only present
possible risks.
### Should be readonly to running uid
Files such as configurations should be readonly to this UID/GID. This is so that if an attacker is
able to gain code execution, they are unable to modify the configuration to write or over-write
files in other locations, or to tamper with the systems configuration.
This can be prevented by changing the files ownership to another user, or removing "write" bits
from the group.
### 'everyone' permission bits in the mode
This means that given a permission mask, "everyone" or all users of the system can read, write or
execute the content of this file. This may mean that if an account on the system is compromised the
attacker can read Kanidm content and may be able to further attack the system as a result.
This can be prevented by removed everyone execute bits from parent directories containing the
configuration, and removing everyone bits from the files in question.
### owned by the current uid, which may allow file permission changes
File permissions in unix systems are a discrestionary access control system, which means the
named uid owner is able to further modify the access of a file regardless of the current
settings. For example:
[william@amethyst 12:25] /tmp > touch test
[william@amethyst 12:25] /tmp > ls -al test
-rw-r--r-- 1 william wheel 0 29 Jul 12:25 test
[william@amethyst 12:25] /tmp > chmod 400 test
[william@amethyst 12:25] /tmp > ls -al test
-r-------- 1 william wheel 0 29 Jul 12:25 test
[william@amethyst 12:25] /tmp > chmod 644 test
[william@amethyst 12:26] /tmp > ls -al test
-rw-r--r-- 1 william wheel 0 29 Jul 12:25 test
Notice that even though the file was set to "read only" to william, and no permission to any
other users, that as "william" I can change the bits to add write permissions back or permissions
for other users.
This can be prevent by making the file owner a different UID to the running process for kanidm.
### A secure example
Between these three issues it can be hard to see a possible strategy to secure files, however
one way exists - group read permissions. The most effective method to secure resources for kanidm
is to set configurations to:
[william@amethyst 12:26] /etc/kanidm > ls -al server.toml
-r--r----- 1 root kanidm 212 28 Jul 16:53 server.toml
The kanidm server should be run as "kanidm:kanidm" with the appropriate user and user private
group created on your system. This applies to unixd configuration as well.
For the database your data folder should be:
[root@amethyst 12:38] /data/kanidm > ls -al .
total 1064
drwxrwx--- 3 root kanidm 96 29 Jul 12:38 .
-rw-r----- 1 kanidm kanidm 544768 29 Jul 12:38 kanidm.db
IE this means 770 root:kanidm. This allows kanidm to create new files in the folder, but prevents
kanidm being able to change the permissions of the folder. Because the folder does not have
everyone mode bits, the content of the database is secure because users can now cd/read
from the directory.
Configurations for clients such as /etc/kanidm/config should be secured with the permissions
as:
[william@amethyst 12:26] /etc/kanidm > ls -al config
-r--r--r-- 1 root wheel 38 10 Jul 10:10 config
This file should be everyone-readable which is why the bits are defined as such.
> NOTE: Why do you use 440 or 444 modes?
>
> A bug exists in the implementation of readonly() in rust that checks this as "does a write
> bit exist for any user" vs "can the current uid write the file?". This distinction is subtle
> but it affects the check. We don't believe this is a significant issue though because
> setting these to 440 and 444 helps to prevent accidental changes by an administrator anyway

View file

@ -19,6 +19,7 @@ serde_json = "1.0"
serde_derive = "1.0"
toml = "0.5"
uuid = { version = "0.8", features = ["serde", "v4"] }
# users = "0.10"
[dev-dependencies]
tokio = "0.2"

View file

@ -9,11 +9,13 @@ use serde::de::DeserializeOwned;
use serde::Serialize;
use serde_derive::Deserialize;
use std::collections::BTreeMap;
use std::fs::File;
use std::fs::{metadata, File, Metadata};
use std::io::Read;
use std::os::unix::fs::MetadataExt;
use std::path::Path;
use std::time::Duration;
use uuid::Uuid;
// use users::{get_current_uid, get_effective_uid};
use kanidm_proto::v1::{
AccountUnixExtend, AuthCredential, AuthRequest, AuthResponse, AuthState, AuthStep,
@ -60,6 +62,16 @@ pub struct KanidmClientBuilder {
connect_timeout: Option<u64>,
}
fn read_file_metadata<P: AsRef<Path>>(path: &P) -> Result<Metadata, ()> {
metadata(path).map_err(|e| {
error!(
"Unable to read metadata for {} - {:?}",
path.as_ref().to_str().unwrap(),
e
);
})
}
impl KanidmClientBuilder {
pub fn new() -> Self {
KanidmClientBuilder {
@ -73,6 +85,21 @@ impl KanidmClientBuilder {
fn parse_certificate(ca_path: &str) -> Result<reqwest::Certificate, ()> {
let mut buf = Vec::new();
// Is the CA secure?
let path = Path::new(ca_path);
let ca_meta = read_file_metadata(&path)?;
if !ca_meta.permissions().readonly() {
warn!("permissions on {} may not be secure. Should be readonly to running uid. This could be a security risk ...", ca_path);
}
if ca_meta.uid() != 0 || ca_meta.gid() != 0 {
warn!(
"{} should be owned be root:root to prevent tampering",
ca_path
);
}
// TODO #253: Handle these errors better, or at least provide diagnostics?
let mut f = File::open(ca_path).map_err(|_| ())?;
f.read_to_end(&mut buf).map_err(|_| ())?;
@ -186,6 +213,23 @@ impl KanidmClientBuilder {
})
}
fn display_warnings(&self, address: &str) {
// Check for problems now
if !self.verify_ca {
warn!("verify_ca set to false - this may allow network interception of passwords!");
}
if !self.verify_hostnames {
warn!(
"verify_hostnames set to false - this may allow network interception of passwords!"
);
}
if !address.starts_with("https://") {
warn!("address does not start with 'https://' - this may allow network interception of passwords!");
}
}
// Consume self and return a client.
pub fn build(self) -> Result<KanidmClient, reqwest::Error> {
// Errghh, how to handle this cleaner.
@ -197,6 +241,8 @@ impl KanidmClientBuilder {
}
};
self.display_warnings(address.as_str());
let client_builder = reqwest::blocking::Client::builder()
.cookie_store(true)
.danger_accept_invalid_hostnames(!self.verify_hostnames)
@ -233,6 +279,8 @@ impl KanidmClientBuilder {
}
};
self.display_warnings(address.as_str());
let client_builder = reqwest::Client::builder()
.cookie_store(true)
.danger_accept_invalid_hostnames(!self.verify_hostnames)

View file

@ -65,6 +65,8 @@ r2d2_sqlite = "0.16"
reqwest = { version = "0.10" }
users = "0.10"
[features]
default = [ "libsqlite3-sys/bundled" ]

View file

@ -2,6 +2,12 @@
#[macro_use]
extern crate log;
use users::{get_current_gid, get_current_uid, get_effective_gid, get_effective_uid};
use std::fs::metadata;
use std::os::unix::fs::MetadataExt;
use std::path::{Path, PathBuf};
use bytes::{BufMut, BytesMut};
use futures::SinkExt;
use futures::StreamExt;
@ -61,7 +67,7 @@ impl ClientCodec {
fn rm_if_exist(p: &str) {
let _ = std::fs::remove_file(p).map_err(|e| {
error!("attempting to remove {:?} -> {:?}", p, e);
warn!("attempting to remove {:?} -> {:?}", p, e);
});
}
@ -201,17 +207,85 @@ async fn handle_client(
#[tokio::main]
async fn main() {
let cuid = get_current_uid();
let ceuid = get_effective_uid();
let cgid = get_current_gid();
let cegid = get_effective_gid();
if cuid == 0 || ceuid == 0 || cgid == 0 || cegid == 0 {
eprintln!("Refusing to run - this process must not operate as root.");
std::process::exit(1);
}
// ::std::env::set_var("RUST_LOG", "kanidm=debug,kanidm_client=debug");
env_logger::init();
// setup
let cb = KanidmClientBuilder::new()
.read_options_from_optional_config("/etc/kanidm/config")
.expect("Failed to parse /etc/kanidm/config");
let cfg_path = Path::new("/etc/kanidm/config");
if cfg_path.exists() {
let cfg_meta = match metadata(&cfg_path) {
Ok(v) => v,
Err(e) => {
error!(
"Unable to read metadata for {} - {:?}",
cfg_path.to_str().unwrap(),
e
);
std::process::exit(1);
}
};
if !cfg_meta.permissions().readonly() {
warn!("permissions on {} may not be secure. Should be readonly to running uid. This could be a security risk ...",
cfg_path.to_str().unwrap());
}
let cfg = KanidmUnixdConfig::new()
.read_options_from_optional_config("/etc/kanidm/unixd")
.expect("Failed to parse /etc/kanidm/unixd");
if cfg_meta.uid() == cuid || cfg_meta.uid() == ceuid {
warn!("WARNING: {} owned by the current uid, which may allow file permission changes. This could be a security risk ...",
cfg_path.to_str().unwrap()
);
}
}
let unixd_path = Path::new("/etc/kanidm/config");
if unixd_path.exists() {
let unixd_meta = match metadata(&unixd_path) {
Ok(v) => v,
Err(e) => {
error!(
"Unable to read metadata for {} - {:?}",
unixd_path.to_str().unwrap(),
e
);
std::process::exit(1);
}
};
if !unixd_meta.permissions().readonly() {
warn!("permissions on {} may not be secure. Should be readonly to running uid. This could be a security risk ...",
unixd_path.to_str().unwrap());
}
if unixd_meta.uid() == cuid || unixd_meta.uid() == ceuid {
warn!("WARNING: {} owned by the current uid, which may allow file permission changes. This could be a security risk ...",
unixd_path.to_str().unwrap()
);
}
}
// setup
let cb = match KanidmClientBuilder::new().read_options_from_optional_config(cfg_path) {
Ok(v) => v,
Err(_) => {
error!("Failed to parse {}", cfg_path.to_str().unwrap());
std::process::exit(1);
}
};
let cfg = match KanidmUnixdConfig::new().read_options_from_optional_config(unixd_path) {
Ok(v) => v,
Err(_) => {
error!("Failed to parse {}", unixd_path.to_str().unwrap());
std::process::exit(1);
}
};
rm_if_exist(cfg.sock_path.as_str());
@ -219,6 +293,51 @@ async fn main() {
let rsclient = cb.build_async().expect("Failed to build async client");
// Check the pb path will be okay.
if cfg.db_path != "" {
let db_path = PathBuf::from(cfg.db_path.as_str());
// We only need to check the parent folder path permissions as the db itself may not
// exist yet.
if let Some(db_parent_path) = db_path.parent() {
if !db_parent_path.exists() {
error!(
"Refusing to run, DB folder {} does not exist",
db_parent_path.to_str().unwrap()
);
std::process::exit(1);
}
let db_par_path_buf = db_parent_path.to_path_buf();
let i_meta = match metadata(&db_par_path_buf) {
Ok(v) => v,
Err(e) => {
error!(
"Unable to read metadata for {} - {:?}",
db_par_path_buf.to_str().unwrap(),
e
);
std::process::exit(1);
}
};
if !i_meta.is_dir() {
error!(
"Refusing to run - DB folder {} may not be a directory",
db_par_path_buf.to_str().unwrap()
);
std::process::exit(1);
}
if i_meta.permissions().readonly() {
warn!("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());
}
if i_meta.mode() & 0o007 != 0 {
warn!("WARNING: DB folder {} has 'everyone' permission bits in the mode. This could be a security risk ...", db_par_path_buf.to_str().unwrap());
}
}
}
let cachelayer = Arc::new(
CacheLayer::new(
cfg.db_path.as_str(), // The sqlite db path

View file

@ -83,6 +83,9 @@ futures-util = "0.3"
tokio-util = { version = "0.2", features = ["codec"] }
tokio-openssl = "0.4"
libc = "0.2"
users = "0.10"
[features]
default = [ "libsqlite3-sys/bundled", "openssl/vendored" ]

View file

@ -2,39 +2,29 @@ ARG BASE_IMAGE=opensuse/tumbleweed:latest
FROM ${BASE_IMAGE} AS builder
LABEL mantainer william@blackhats.net.au
RUN zypper mr -d repo-non-oss && \
zypper mr -d repo-oss && \
zypper mr -d repo-update && \
zypper ar https://download.opensuse.org/update/tumbleweed/ repo-update-https && \
zypper ar https://download.opensuse.org/tumbleweed/repo/oss/ repo-oss-https && \
zypper ar https://download.opensuse.org/tumbleweed/repo/non-oss/ repo-non-oss-https && \
zypper ref && \
RUN zypper ref && \
zypper install -y \
cargo \
rust \
gcc \
automake \
autoconf \
make \
libopenssl-devel \
pam-devel && \
clang lld \
make automake autoconf \
libopenssl-devel pam-devel && \
zypper clean -a
COPY . /usr/src/kanidm
WORKDIR /usr/src/kanidm/kanidmd
RUN RUSTC_BOOTSTRAP=1 cargo build --features=concread/simd_support --release
RUN ln -s -f /usr/bin/clang /usr/bin/cc && \
ln -s -f /usr/bin/ld.lld /usr/bin/ld
RUN CC=/usr/bin/clang RUSTC_BOOTSTRAP=1 cargo build --features=concread/simd_support --release
FROM ${BASE_IMAGE}
LABEL mantainer william@blackhats.net.au
RUN zypper mr -d repo-non-oss && \
zypper mr -d repo-oss && \
zypper mr -d repo-update && \
zypper ar https://download.opensuse.org/update/tumbleweed/ repo-update-https && \
zypper ar https://download.opensuse.org/tumbleweed/repo/oss/ repo-oss-https && \
zypper ar https://download.opensuse.org/tumbleweed/repo/non-oss/ repo-non-oss-https && \
zypper ref && \
RUN zypper ref && \
zypper install -y \
timezone \
pam && \

View file

@ -715,6 +715,7 @@ pub trait AccessControlsTransaction {
})
}
#[allow(clippy::cognitive_complexity)]
fn modify_allow_operation(
&self,
audit: &mut AuditScope,
@ -913,6 +914,7 @@ pub trait AccessControlsTransaction {
})
}
#[allow(clippy::cognitive_complexity)]
fn create_allow_operation(
&self,
audit: &mut AuditScope,

View file

@ -11,6 +11,8 @@ use uuid::Uuid;
use std::str::FromStr;
pub const AUDIT_LINE_SIZE: usize = 512;
#[derive(Debug, Serialize, Deserialize)]
#[repr(u32)]
pub enum LogTag {
@ -97,18 +99,23 @@ impl fmt::Display for LogTag {
macro_rules! lqueue {
($au:expr, $tag:expr, $($arg:tt)*) => ({
use crate::audit::LogTag;
use crate::audit::{LogTag, AUDIT_LINE_SIZE};
if cfg!(test) {
println!($($arg)*)
}
if ($au.level & $tag as u32) == $tag as u32 {
use std::fmt;
$au.log_event(
$tag,
fmt::format(
format_args!($($arg)*)
)
)
// We have to buffer the string to over-alloc it.
let mut output = String::with_capacity(AUDIT_LINE_SIZE);
match fmt::write(&mut output, format_args!($($arg)*)) {
Ok(_) => $au.log_event($tag, output),
Err(e) => {
$au.log_event(
LogTag::AdminError,
format!("CRITICAL UNABLE TO WRITE LOG EVENT - {:?}", e)
)
}
}
}
})
}

View file

@ -1,6 +1,7 @@
mod ctx;
mod ldaps;
// use actix_files as fs;
use libc::umask;
use actix::prelude::*;
use actix_session::{CookieSession, Session};
use actix_web::web::{self, Data, HttpResponse, Json, Path};
@ -1626,9 +1627,11 @@ pub async fn create_server_core(config: Configuration) -> Result<ServerCtx, ()>
}
info!("Starting kanidm with configuration: {}", config);
// Setup umask, so that every we touch or create is secure.
let _ = unsafe { umask(0o0027) };
// The log server is started on it's own thread, and is contacted
// asynchronously.
let (log_tx, log_rx) = unbounded();
let log_thread = thread::spawn(move || async_log::run(log_rx));

View file

@ -7,7 +7,6 @@ use uuid::Uuid;
use kanidm_proto::v1::{OperationError, UserAuthToken};
#[derive(Debug)]
pub struct PasswordChangeEvent {
pub event: Event,
pub target: Uuid,
@ -60,7 +59,6 @@ impl PasswordChangeEvent {
}
}
#[derive(Debug)]
pub struct UnixPasswordChangeEvent {
pub event: Event,
pub target: Uuid,
@ -223,13 +221,21 @@ impl UnixGroupTokenEvent {
}
}
#[derive(Debug)]
pub struct UnixUserAuthEvent {
pub event: Event,
pub target: Uuid,
pub cleartext: String,
}
impl std::fmt::Debug for UnixUserAuthEvent {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("UnixUserAuthEvent")
.field("event", &self.event)
.field("target", &self.target)
.finish()
}
}
impl UnixUserAuthEvent {
#[cfg(test)]
pub fn new_internal(target: &Uuid, cleartext: &str) -> Self {
@ -333,7 +339,6 @@ impl VerifyTOTPEvent {
}
}
#[derive(Debug)]
pub struct LdapAuthEvent {
// pub event: Event,
pub target: Uuid,

View file

@ -977,6 +977,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
})
}
#[allow(clippy::cognitive_complexity)]
pub fn delete(&self, au: &mut AuditScope, de: &DeleteEvent) -> Result<(), OperationError> {
lperf_segment!(au, "server::delete", || {
// Do you have access to view all the set members? Reduce based on your
@ -1279,6 +1280,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
})
}
#[allow(clippy::cognitive_complexity)]
pub fn modify(&self, au: &mut AuditScope, me: &ModifyEvent) -> Result<(), OperationError> {
lperf_segment!(au, "server::modify", || {
// Get the candidates.

View file

@ -1,8 +1,11 @@
#![deny(warnings)]
use users::{get_current_gid, get_current_uid, get_effective_gid, get_effective_uid};
use serde_derive::Deserialize;
use std::fs::File;
use std::fs::{metadata, File, Metadata};
use std::io::Read;
use std::os::unix::fs::MetadataExt;
use std::path::Path;
use std::path::PathBuf;
use std::str::FromStr;
@ -127,13 +130,63 @@ impl Opt {
}
}
fn read_file_metadata(path: &PathBuf) -> Metadata {
match metadata(path) {
Ok(m) => m,
Err(e) => {
eprintln!(
"Unable to read metadata for {} - {:?}",
path.to_str().unwrap(),
e
);
std::process::exit(1);
}
}
}
#[actix_rt::main]
async fn main() {
// Get info about who we are.
let cuid = get_current_uid();
let ceuid = get_effective_uid();
let cgid = get_current_gid();
let cegid = get_effective_gid();
if cuid == 0 || ceuid == 0 || cgid == 0 || cegid == 0 {
eprintln!("ERROR: Refusing to run - this process must not operate as root.");
std::process::exit(1);
}
if cuid != ceuid || cgid != cegid {
eprintln!("{} != {} || {} != {}", cuid, ceuid, cgid, cegid);
eprintln!("ERROR: Refusing to run - uid and euid OR gid and egid must be consistent.");
std::process::exit(1);
}
// Read cli args, determine if we should backup/restore
let opt = Opt::from_args();
// Read our config
let mut config = Configuration::new();
// Check the permissions are sane.
let cfg_meta = read_file_metadata(&(opt.commonopt().config_path));
if !cfg_meta.permissions().readonly() {
eprintln!("WARNING: permissions on {} may not be secure. Should be readonly to running uid. This could be a security risk ...",
opt.commonopt().config_path.to_str().unwrap());
}
if cfg_meta.mode() & 0o007 != 0 {
eprintln!("WARNING: {} has 'everyone' permission bits in the mode. This could be a security risk ...",
opt.commonopt().config_path.to_str().unwrap()
);
}
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.commonopt().config_path.to_str().unwrap()
);
}
// Read our config
let sconfig = match ServerConfig::new(&(opt.commonopt().config_path)) {
Ok(c) => c,
Err(e) => {
@ -152,6 +205,64 @@ async fn main() {
}
});
// Check the permissions of the files from the configuration.
if let Some(i_str) = &(sconfig.tls_ca) {
let i_path = PathBuf::from(i_str.as_str());
let i_meta = read_file_metadata(&i_path);
if !i_meta.permissions().readonly() {
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_cert) {
let i_path = PathBuf::from(i_str.as_str());
let i_meta = read_file_metadata(&i_path);
if !i_meta.permissions().readonly() {
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());
let i_meta = read_file_metadata(&i_path);
if !i_meta.permissions().readonly() {
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 db_path = PathBuf::from(sconfig.db_path.as_str());
// We can't check the db_path permissions because it may note exist yet!
if let Some(db_parent_path) = db_path.parent() {
if !db_parent_path.exists() {
eprintln!(
"DB folder {} may not exist, server startup may FAIL!",
db_parent_path.to_str().unwrap()
);
}
let db_par_path_buf = db_parent_path.to_path_buf();
let i_meta = read_file_metadata(&db_par_path_buf);
if !i_meta.is_dir() {
eprintln!(
"ERROR: Refusing to run - DB folder {} may not be a directory",
db_par_path_buf.to_str().unwrap()
);
std::process::exit(1);
}
if i_meta.permissions().readonly() {
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());
}
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());
}
}
config.update_log_level(ll);
config.update_db_path(&sconfig.db_path.as_str());
config.update_tls(&sconfig.tls_ca, &sconfig.tls_cert, &sconfig.tls_key);