mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
65 cli options (#85)
Add support for command line options to the server. This supports server sid regeneration, persistence, address binding, tls configuration and more. This also improves TLS support in the client tools for CA cert addition to queries.
This commit is contained in:
parent
3c774dcf6c
commit
720fce732e
34
README.md
34
README.md
|
@ -31,7 +31,39 @@ See [CODE_OF_CONDUCT.md]
|
|||
|
||||
## Quick start
|
||||
|
||||
Details to come ...
|
||||
Today the server is still in a state of heavy development, and hasn't been packaged or setup for
|
||||
production usage.
|
||||
|
||||
However, we are able to run test or demo servers that are suitable for previews and testing.
|
||||
|
||||
After getting the code, you will need a rust environment. Please investigate rustup for your platform
|
||||
to establish this.
|
||||
|
||||
Once you have the source code, you need certificates to use with the server. I recommend using
|
||||
let's encrypt, but if this is not possible, please use our insecure cert tool:
|
||||
|
||||
mkdir insecure
|
||||
cd insecure
|
||||
../insecure_generate_tls.sh
|
||||
|
||||
You can now build and run the server with:
|
||||
|
||||
cd rsidmd
|
||||
cargo run -- server -D /tmp/kanidm.db -C ../insecure/ca.pem -c ../insecure/cert.pem -k ../insecure/key.pem --domain localhost --bindaddr 127.0.0.1:8080
|
||||
|
||||
In a new terminal, you can now build and run the client tools with:
|
||||
|
||||
cd rsidm_tools
|
||||
cargo run -- --help
|
||||
cargo run -- whoami -H https://localhost:8080 -D anonymous -C ../insecure/ca.pem
|
||||
|
||||
## Development and Testing
|
||||
|
||||
There are tests of various components through the various components of the project. When developing
|
||||
it't best if you test in the component you are working on, followed by the full server tests.
|
||||
|
||||
There are *no* prerequisites to running these tests or special configurations. cargo test should
|
||||
just work!
|
||||
|
||||
## Implemented/Planned features
|
||||
|
||||
|
|
52
insecure_generate_tls.sh
Executable file
52
insecure_generate_tls.sh
Executable file
|
@ -0,0 +1,52 @@
|
|||
#!/bin/sh
|
||||
|
||||
cat > ./altnames.cnf << DEVEOF
|
||||
[req]
|
||||
req_extensions = v3_req
|
||||
nsComment = "Certificate"
|
||||
distinguished_name = req_distinguished_name
|
||||
|
||||
[ 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 server\'s hostname)
|
||||
commonName_max = 64
|
||||
commonName_default = localhost
|
||||
|
||||
[ v3_req ]
|
||||
|
||||
# Extensions to add to a certificate request
|
||||
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = 127.0.0.1
|
||||
|
||||
DEVEOF
|
||||
|
||||
# Make the ca
|
||||
openssl req -x509 -new -newkey rsa:2048 -keyout cakey.pem -out ca.pem -days 31 -subj "/C=AU/ST=Queensland/L=Brisbane/O=INSECURE/CN=insecure.ca.localhost" -nodes
|
||||
openssl genrsa -out key.pem 2048
|
||||
openssl req -key key.pem -out cert.csr -days 31 -config altnames.cnf -new
|
||||
openssl x509 -req -days 31 -in cert.csr -CA ca.pem -CAkey cakey.pem -CAcreateserial -out cert.pem
|
||||
|
||||
echo use ca.pem, cert.pem, and key.pem
|
||||
|
|
@ -7,6 +7,8 @@ extern crate log;
|
|||
use serde_json;
|
||||
|
||||
use reqwest;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
use rsidm_proto::v1::{
|
||||
AuthCredential, AuthRequest, AuthResponse, AuthState, AuthStep, CreateRequest, Entry,
|
||||
|
@ -28,9 +30,24 @@ pub struct RsidmClient {
|
|||
}
|
||||
|
||||
impl RsidmClient {
|
||||
pub fn new(addr: &str) -> Self {
|
||||
let client = reqwest::Client::builder()
|
||||
.cookie_store(true)
|
||||
pub fn new(addr: &str, ca: Option<&str>) -> Self {
|
||||
let ca = ca.map(|ca_path| {
|
||||
//Okay we have a ca to add. Let's read it in and setup.
|
||||
let mut buf = Vec::new();
|
||||
// TODO: Better than expect?
|
||||
let mut f = File::open(ca_path).expect("Failed to open ca");
|
||||
f.read_to_end(&mut buf).expect("Failed to read ca");
|
||||
reqwest::Certificate::from_pem(&buf).expect("Failed to parse ca")
|
||||
});
|
||||
|
||||
let client_builder = reqwest::Client::builder().cookie_store(true);
|
||||
|
||||
let client_builder = match ca {
|
||||
Some(cert) => client_builder.add_root_certificate(cert),
|
||||
None => client_builder,
|
||||
};
|
||||
|
||||
let client = client_builder
|
||||
.build()
|
||||
.expect("Unexpected reqwest builder failure!");
|
||||
RsidmClient {
|
||||
|
|
|
@ -71,7 +71,7 @@ fn run_test(test_fn: fn(RsidmClient) -> ()) {
|
|||
|
||||
// Setup the client, and the address we selected.
|
||||
let addr = format!("http://127.0.0.1:{}", port);
|
||||
let rsclient = RsidmClient::new(addr.as_str());
|
||||
let rsclient = RsidmClient::new(addr.as_str(), None);
|
||||
|
||||
test_fn(rsclient);
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
extern crate structopt;
|
||||
use rsidm_client::RsidmClient;
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
|
@ -8,11 +9,14 @@ struct CommonOpt {
|
|||
addr: String,
|
||||
#[structopt(short = "D", long = "name")]
|
||||
username: String,
|
||||
#[structopt(parse(from_os_str), short = "C", long = "ca")]
|
||||
ca_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl CommonOpt {
|
||||
fn to_client(&self) -> RsidmClient {
|
||||
RsidmClient::new(self.addr.as_str())
|
||||
let ca_path: Option<&str> = self.ca_path.as_ref().map(|p| p.to_str().unwrap());
|
||||
RsidmClient::new(self.addr.as_str(), ca_path)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,8 @@ path = "src/server/main.rs"
|
|||
rsidm_proto = { path = "../rsidm_proto" }
|
||||
|
||||
actix = "0.7"
|
||||
actix-web = "0.7"
|
||||
actix-web = { version = "0.7", features = ["ssl"] }
|
||||
|
||||
bytes = "0.4"
|
||||
log = "0.4"
|
||||
env_logger = "0.6"
|
||||
|
@ -52,4 +53,5 @@ concread = "0.1"
|
|||
openssl = "0.10"
|
||||
|
||||
rpassword = "0.4"
|
||||
num_cpus = "1.10"
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use r2d2::Pool;
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use rand::prelude::*;
|
||||
use rusqlite::types::ToSql;
|
||||
use rusqlite::NO_PARAMS;
|
||||
use serde_cbor;
|
||||
|
@ -13,6 +14,7 @@ use crate::audit::AuditScope;
|
|||
use crate::be::dbentry::DbEntry;
|
||||
use crate::entry::{Entry, EntryCommitted, EntryNew, EntryValid};
|
||||
use crate::filter::{Filter, FilterValidResolved};
|
||||
use crate::utils::SID;
|
||||
use rsidm_proto::v1::{ConsistencyError, OperationError};
|
||||
|
||||
pub mod dbentry;
|
||||
|
@ -614,6 +616,46 @@ impl BackendWriteTransaction {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_db_sid(&self) -> SID {
|
||||
// Try to get a value.
|
||||
match self
|
||||
.conn
|
||||
.query_row_named("SELECT data FROM db_sid WHERE id = 1", &[], |row| {
|
||||
row.get(0)
|
||||
}) {
|
||||
Ok(e) => {
|
||||
let y: Vec<u8> = e;
|
||||
assert!(y.len() == 4);
|
||||
let mut sid: [u8; 4] = [0; 4];
|
||||
for i in 0..4 {
|
||||
sid[i] = y[i];
|
||||
}
|
||||
|
||||
sid
|
||||
}
|
||||
Err(_) => self.generate_db_sid(),
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_db_sid(&self) -> SID {
|
||||
// The value is missing. Generate a new one and store it.
|
||||
let mut nsid = [0; 4];
|
||||
let mut rng = StdRng::from_entropy();
|
||||
rng.fill(&mut nsid);
|
||||
|
||||
let mut data = Vec::new();
|
||||
data.extend_from_slice(&nsid);
|
||||
|
||||
self.conn
|
||||
.execute_named(
|
||||
"INSERT OR REPLACE INTO db_sid (id, data) VALUES(:id, :sid)",
|
||||
&[(":id", &1), (":sid", &data)],
|
||||
)
|
||||
.expect("Failed to allocate sid!");
|
||||
|
||||
nsid
|
||||
}
|
||||
|
||||
pub fn setup(&self, audit: &mut AuditScope) -> Result<(), OperationError> {
|
||||
{
|
||||
// Enable WAL mode, which is just faster and better.
|
||||
|
@ -679,6 +721,19 @@ impl BackendWriteTransaction {
|
|||
"sqlite error {:?}",
|
||||
OperationError::SQLiteError
|
||||
);
|
||||
try_audit!(
|
||||
audit,
|
||||
self.conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS db_sid (
|
||||
id INTEGER PRIMARY KEY ASC,
|
||||
data BLOB NOT NULL
|
||||
)
|
||||
",
|
||||
NO_PARAMS,
|
||||
),
|
||||
"sqlite error {:?}",
|
||||
OperationError::SQLiteError
|
||||
);
|
||||
dbv_id2entry = 1;
|
||||
audit_log!(audit, "dbv_id2entry migrated -> {}", dbv_id2entry);
|
||||
}
|
||||
|
@ -751,6 +806,20 @@ impl Backend {
|
|||
.expect("Unable to get connection from pool!!!");
|
||||
BackendWriteTransaction::new(conn)
|
||||
}
|
||||
|
||||
pub fn get_db_sid(&self) -> SID {
|
||||
let bwt = self.write();
|
||||
let s = bwt.get_db_sid();
|
||||
bwt.commit().expect("Failed to commit SID");
|
||||
s
|
||||
}
|
||||
|
||||
pub fn reset_db_sid(&self) -> SID {
|
||||
let bwt = self.write();
|
||||
let s = bwt.generate_db_sid();
|
||||
bwt.commit().expect("Failed to commit SID");
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Backend {
|
||||
|
@ -1050,4 +1119,17 @@ mod tests {
|
|||
.expect("Restore failed!");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sid_generation_and_reset() {
|
||||
run_test!(|_audit: &mut AuditScope, be: &BackendWriteTransaction| {
|
||||
let sid1 = be.get_db_sid();
|
||||
let sid2 = be.get_db_sid();
|
||||
assert!(sid1 == sid2);
|
||||
let sid3 = be.generate_db_sid();
|
||||
assert!(sid1 != sid3);
|
||||
let sid4 = be.get_db_sid();
|
||||
assert!(sid3 == sid4);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::utils::SID;
|
||||
use num_cpus;
|
||||
use rand::prelude::*;
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
@ -7,6 +8,13 @@ pub struct IntegrationTestConfig {
|
|||
pub admin_password: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct TlsConfiguration {
|
||||
pub ca: String,
|
||||
pub cert: String,
|
||||
pub key: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Configuration {
|
||||
pub address: String,
|
||||
|
@ -16,31 +24,48 @@ pub struct Configuration {
|
|||
pub db_path: String,
|
||||
pub maximum_request: usize,
|
||||
pub secure_cookies: bool,
|
||||
pub tls_config: Option<TlsConfiguration>,
|
||||
pub cookie_key: [u8; 32],
|
||||
pub server_id: SID,
|
||||
pub integration_test_config: Option<Box<IntegrationTestConfig>>,
|
||||
}
|
||||
|
||||
impl fmt::Display for Configuration {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "address: {}, ", self.address)
|
||||
.and_then(|_| write!(f, "domain: {}, ", self.domain))
|
||||
.and_then(|_| write!(f, "thread count: {}, ", self.threads))
|
||||
.and_then(|_| write!(f, "dbpath: {}, ", self.db_path))
|
||||
.and_then(|_| write!(f, "max request size: {}b, ", self.maximum_request))
|
||||
.and_then(|_| write!(f, "secure cookies: {}, ", self.secure_cookies))
|
||||
.and_then(|_| write!(f, "with TLS: {}, ", self.tls_config.is_some()))
|
||||
.and_then(|_| {
|
||||
write!(
|
||||
f,
|
||||
"integration mode: {}",
|
||||
self.integration_test_config.is_some()
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Configuration {
|
||||
pub fn new() -> Self {
|
||||
let mut c = Configuration {
|
||||
address: String::from("127.0.0.1:8080"),
|
||||
domain: String::from("localhost"),
|
||||
threads: 8,
|
||||
threads: num_cpus::get(),
|
||||
db_path: String::from(""),
|
||||
maximum_request: 262144, // 256k
|
||||
// log type
|
||||
// log path
|
||||
// TODO #63: default true in prd
|
||||
// secure_cookies: if cfg!(test) { false } else { true },
|
||||
secure_cookies: false,
|
||||
secure_cookies: if cfg!(test) { false } else { true },
|
||||
tls_config: None,
|
||||
cookie_key: [0; 32],
|
||||
server_id: [0; 4],
|
||||
integration_test_config: None,
|
||||
};
|
||||
let mut rng = StdRng::from_entropy();
|
||||
rng.fill(&mut c.cookie_key);
|
||||
rng.fill(&mut c.server_id);
|
||||
c
|
||||
}
|
||||
|
||||
|
@ -53,4 +78,54 @@ impl Configuration {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_bind(&mut self, b: &Option<String>) {
|
||||
self.address = b
|
||||
.as_ref()
|
||||
.map(|s| s.clone())
|
||||
.unwrap_or_else(|| String::from("127.0.0.1:8080"));
|
||||
}
|
||||
|
||||
pub fn update_tls(
|
||||
&mut self,
|
||||
ca: &Option<PathBuf>,
|
||||
cert: &Option<PathBuf>,
|
||||
key: &Option<PathBuf>,
|
||||
) {
|
||||
match (ca, cert, key) {
|
||||
(None, None, None) => {}
|
||||
(Some(cap), Some(certp), Some(keyp)) => {
|
||||
let cas = match cap.to_str() {
|
||||
Some(cav) => cav.to_string(),
|
||||
None => {
|
||||
error!("Invalid CA path");
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
let certs = match certp.to_str() {
|
||||
Some(certv) => certv.to_string(),
|
||||
None => {
|
||||
error!("Invalid Cert path");
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
let keys = match keyp.to_str() {
|
||||
Some(keyv) => keyv.to_string(),
|
||||
None => {
|
||||
error!("Invalid Key path");
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
self.tls_config = Some(TlsConfiguration {
|
||||
ca: cas,
|
||||
cert: certs,
|
||||
key: keys,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
error!("Invalid TLS configuration - must provide ca, cert and key!");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ use crate::actors::v1::{
|
|||
use crate::async_log;
|
||||
use crate::audit::AuditScope;
|
||||
use crate::be::{Backend, BackendTransaction};
|
||||
use crate::crypto::setup_tls;
|
||||
use crate::idm::server::IdmServer;
|
||||
use crate::interval::IntervalActor;
|
||||
use crate::schema::Schema;
|
||||
|
@ -357,6 +358,19 @@ pub fn restore_server_core(config: Configuration, dst_path: &str) {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn reset_sid_core(config: Configuration) {
|
||||
// Setup the be
|
||||
let be = match setup_backend(&config) {
|
||||
Ok(be) => be,
|
||||
Err(e) => {
|
||||
error!("Failed to setup BE: {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let nsid = be.reset_db_sid();
|
||||
info!("New Server ID: {:?}", nsid);
|
||||
}
|
||||
|
||||
pub fn verify_server_core(config: Configuration) {
|
||||
let mut audit = AuditScope::new("server_verify");
|
||||
// Setup the be
|
||||
|
@ -405,8 +419,9 @@ pub fn recover_account_core(config: Configuration, name: String, password: Strin
|
|||
return;
|
||||
}
|
||||
};
|
||||
let server_id = be.get_db_sid();
|
||||
// setup the qs - *with* init of the migrations and schema.
|
||||
let (_qs, idms) = match setup_qs_idms(&mut audit, be, config.server_id.clone()) {
|
||||
let (_qs, idms) = match setup_qs_idms(&mut audit, be, server_id) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
debug!("{}", audit);
|
||||
|
@ -443,11 +458,20 @@ pub fn create_server_core(config: Configuration) {
|
|||
warn!("IF YOU SEE THIS IN PRODUCTION YOU MUST CONTACT SUPPORT IMMEDIATELY.");
|
||||
}
|
||||
|
||||
info!("Starting rsidm with configuration: {:?}", config);
|
||||
info!("Starting rsidm with configuration: {}", config);
|
||||
// The log server is started on it's own thread, and is contacted
|
||||
// asynchronously.
|
||||
let log_addr = async_log::start();
|
||||
|
||||
// Setup TLS (if any)
|
||||
let opt_tls_params = match setup_tls(&config) {
|
||||
Ok(opt_tls_params) => opt_tls_params,
|
||||
Err(e) => {
|
||||
error!("Failed to configure TLS parameters -> {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Similar, create a stats thread which aggregates statistics from the
|
||||
// server as they come in.
|
||||
|
||||
|
@ -460,9 +484,12 @@ pub fn create_server_core(config: Configuration) {
|
|||
}
|
||||
};
|
||||
|
||||
let server_id = be.get_db_sid();
|
||||
info!("Server ID -> {:?}", server_id);
|
||||
|
||||
let mut audit = AuditScope::new("setup_qs_idms");
|
||||
// Start the IDM server.
|
||||
let (qs, idms) = match setup_qs_idms(&mut audit, be, config.server_id.clone()) {
|
||||
let (qs, idms) = match setup_qs_idms(&mut audit, be, server_id) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
debug!("{}", audit);
|
||||
|
@ -516,7 +543,7 @@ pub fn create_server_core(config: Configuration) {
|
|||
let cookie_key: [u8; 32] = config.cookie_key.clone();
|
||||
|
||||
// start the web server
|
||||
actix_web::server::new(move || {
|
||||
let aws_builder = actix_web::server::new(move || {
|
||||
App::with_state(AppState {
|
||||
qe: server_addr.clone(),
|
||||
max_size: max_size,
|
||||
|
@ -545,7 +572,6 @@ pub fn create_server_core(config: Configuration) {
|
|||
.secure(secure_cookies),
|
||||
))
|
||||
// .resource("/", |r| r.f(index))
|
||||
// curl --header ...?
|
||||
.resource("/v1/whoami", |r| {
|
||||
r.method(http::Method::GET).with_async(whoami)
|
||||
})
|
||||
|
@ -559,19 +585,15 @@ pub fn create_server_core(config: Configuration) {
|
|||
.resource("/v1/create", |r| {
|
||||
r.method(http::Method::POST).with_async(create)
|
||||
})
|
||||
// Should these actually be different method types?
|
||||
.resource("/v1/modify", |r| {
|
||||
r.method(http::Method::POST).with_async(modify)
|
||||
})
|
||||
.resource("/v1/delete", |r| {
|
||||
r.method(http::Method::POST).with_async(delete)
|
||||
})
|
||||
// curl --header "Content-Type: application/json" --request POST --data '{ "filter" : { "Eq": ["class", "user"] }}' http://127.0.0.1:8080/v1/search
|
||||
.resource("/v1/search", |r| {
|
||||
r.method(http::Method::POST).with_async(search)
|
||||
})
|
||||
// This is one of the times we need cookies :)
|
||||
// curl -b /tmp/cookie.jar -c /tmp/cookie.jar --header "Content-Type: application/json" --request POST --data '{ "state" : { "Init": ["Anonymous", []] }}' http://127.0.0.1:8080/v1/auth
|
||||
.resource("/v1/auth", |r| {
|
||||
r.method(http::Method::POST).with_async(auth)
|
||||
})
|
||||
|
@ -581,8 +603,17 @@ pub fn create_server_core(config: Configuration) {
|
|||
r.method(http::Method::GET).with(class_list)
|
||||
})
|
||||
*/
|
||||
})
|
||||
.bind(config.address)
|
||||
.expect("Failed to initialise server!")
|
||||
.start();
|
||||
});
|
||||
|
||||
let tls_aws_builder = match opt_tls_params {
|
||||
Some(tls_params) => aws_builder.bind_ssl(config.address, tls_params),
|
||||
None => {
|
||||
warn!("Starting WITHOUT TLS parameters. This may cause authentication to fail!");
|
||||
aws_builder.bind(config.address)
|
||||
}
|
||||
};
|
||||
|
||||
tls_aws_builder
|
||||
.expect("Failed to initialise server!")
|
||||
.start();
|
||||
}
|
||||
|
|
17
rsidmd/src/lib/crypto.rs
Normal file
17
rsidmd/src/lib/crypto.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use crate::config::Configuration;
|
||||
use openssl::error::ErrorStack;
|
||||
use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod};
|
||||
|
||||
pub fn setup_tls(config: &Configuration) -> Result<Option<SslAcceptorBuilder>, ErrorStack> {
|
||||
match &config.tls_config {
|
||||
Some(tls_config) => {
|
||||
let mut ssl_builder = SslAcceptor::mozilla_modern(SslMethod::tls())?;
|
||||
ssl_builder.set_ca_file(&tls_config.ca)?;
|
||||
ssl_builder.set_private_key_file(&tls_config.key, SslFiletype::PEM)?;
|
||||
ssl_builder.set_certificate_file(&tls_config.cert, SslFiletype::PEM)?;
|
||||
ssl_builder.check_private_key()?;
|
||||
Ok(Some(ssl_builder))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ extern crate lazy_static;
|
|||
// This has to be before be so the import order works
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod crypto;
|
||||
mod utils;
|
||||
#[macro_use]
|
||||
mod async_log;
|
||||
|
|
|
@ -11,27 +11,43 @@ extern crate log;
|
|||
|
||||
use rsidm::config::Configuration;
|
||||
use rsidm::core::{
|
||||
backup_server_core, create_server_core, recover_account_core, restore_server_core,
|
||||
verify_server_core,
|
||||
backup_server_core, create_server_core, recover_account_core, reset_sid_core,
|
||||
restore_server_core, verify_server_core,
|
||||
};
|
||||
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
struct ServerOpt {
|
||||
struct CommonOpt {
|
||||
#[structopt(short = "d", long = "debug")]
|
||||
debug: bool,
|
||||
#[structopt(parse(from_os_str), short = "D", long = "db_path")]
|
||||
db_path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
struct ServerOpt {
|
||||
#[structopt(parse(from_os_str), short = "C", long = "ca")]
|
||||
ca_path: Option<PathBuf>,
|
||||
#[structopt(parse(from_os_str), short = "c", long = "cert")]
|
||||
cert_path: Option<PathBuf>,
|
||||
#[structopt(parse(from_os_str), short = "k", long = "key")]
|
||||
key_path: Option<PathBuf>,
|
||||
#[structopt(short = "r", long = "domain")]
|
||||
domain: String,
|
||||
#[structopt(short = "b", long = "bindaddr")]
|
||||
bind: Option<String>,
|
||||
#[structopt(flatten)]
|
||||
commonopts: CommonOpt,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
struct BackupOpt {
|
||||
#[structopt(parse(from_os_str))]
|
||||
path: PathBuf,
|
||||
#[structopt(flatten)]
|
||||
serveropts: ServerOpt,
|
||||
commonopts: CommonOpt,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
|
@ -39,7 +55,7 @@ struct RestoreOpt {
|
|||
#[structopt(parse(from_os_str))]
|
||||
path: PathBuf,
|
||||
#[structopt(flatten)]
|
||||
serveropts: ServerOpt,
|
||||
commonopts: CommonOpt,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
|
@ -47,7 +63,7 @@ struct RecoverAccountOpt {
|
|||
#[structopt(short)]
|
||||
name: String,
|
||||
#[structopt(flatten)]
|
||||
serveropts: ServerOpt,
|
||||
commonopts: CommonOpt,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
|
@ -59,18 +75,21 @@ enum Opt {
|
|||
#[structopt(name = "restore")]
|
||||
Restore(RestoreOpt),
|
||||
#[structopt(name = "verify")]
|
||||
Verify(ServerOpt),
|
||||
Verify(CommonOpt),
|
||||
#[structopt(name = "recover_account")]
|
||||
RecoverAccount(RecoverAccountOpt),
|
||||
#[structopt(name = "reset_server_id")]
|
||||
ResetServerId(CommonOpt),
|
||||
}
|
||||
|
||||
impl Opt {
|
||||
fn debug(&self) -> bool {
|
||||
match self {
|
||||
Opt::Server(sopt) | Opt::Verify(sopt) => sopt.debug,
|
||||
Opt::Backup(bopt) => bopt.serveropts.debug,
|
||||
Opt::Restore(ropt) => ropt.serveropts.debug,
|
||||
Opt::RecoverAccount(ropt) => ropt.serveropts.debug,
|
||||
Opt::Server(sopt) => sopt.commonopts.debug,
|
||||
Opt::Verify(sopt) | Opt::ResetServerId(sopt) => sopt.debug,
|
||||
Opt::Backup(bopt) => bopt.commonopts.debug,
|
||||
Opt::Restore(ropt) => ropt.commonopts.debug,
|
||||
Opt::RecoverAccount(ropt) => ropt.commonopts.debug,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +115,10 @@ fn main() {
|
|||
Opt::Server(sopt) => {
|
||||
info!("Running in server mode ...");
|
||||
|
||||
config.update_db_path(&sopt.db_path);
|
||||
config.update_db_path(&sopt.commonopts.db_path);
|
||||
config.update_tls(&sopt.ca_path, &sopt.cert_path, &sopt.key_path);
|
||||
config.update_bind(&sopt.bind);
|
||||
config.domain = sopt.domain.clone();
|
||||
|
||||
let sys = actix::System::new("rsidm-server");
|
||||
create_server_core(config);
|
||||
|
@ -105,7 +127,7 @@ fn main() {
|
|||
Opt::Backup(bopt) => {
|
||||
info!("Running in backup mode ...");
|
||||
|
||||
config.update_db_path(&bopt.serveropts.db_path);
|
||||
config.update_db_path(&bopt.commonopts.db_path);
|
||||
|
||||
let p = match bopt.path.to_str() {
|
||||
Some(p) => p,
|
||||
|
@ -119,7 +141,7 @@ fn main() {
|
|||
Opt::Restore(ropt) => {
|
||||
info!("Running in restore mode ...");
|
||||
|
||||
config.update_db_path(&ropt.serveropts.db_path);
|
||||
config.update_db_path(&ropt.commonopts.db_path);
|
||||
|
||||
let p = match ropt.path.to_str() {
|
||||
Some(p) => p,
|
||||
|
@ -140,9 +162,15 @@ fn main() {
|
|||
info!("Running account recovery ...");
|
||||
|
||||
let password = rpassword::prompt_password_stderr("new password: ").unwrap();
|
||||
config.update_db_path(&raopt.serveropts.db_path);
|
||||
config.update_db_path(&raopt.commonopts.db_path);
|
||||
|
||||
recover_account_core(config, raopt.name, password);
|
||||
}
|
||||
Opt::ResetServerId(vopt) => {
|
||||
info!("Resetting server id. THIS MAY BREAK REPLICATION");
|
||||
|
||||
config.update_db_path(&vopt.db_path);
|
||||
reset_sid_core(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue