307 lines
13 KiB
Rust
307 lines
13 KiB
Rust
#[macro_use] extern crate log;
|
|
|
|
use std::{env, fs};
|
|
use std::net::SocketAddr;
|
|
use std::path::Path;
|
|
|
|
use axum::extract::Extension;
|
|
use axum::Router;
|
|
use axum::routing::{delete, get, post};
|
|
use dotenv::dotenv;
|
|
use lapin::ConnectionProperties;
|
|
use picky::key::PrivateKey;
|
|
use picky::x509::Cert;
|
|
use sea_orm::{ActiveValue, ColumnTrait, ConnectOptions, Database, EntityTrait, QueryFilter};
|
|
use sea_orm_migration::prelude::*;
|
|
use tower::ServiceBuilder;
|
|
use ulid::Ulid;
|
|
use crate::cert::encrypt_priv_key;
|
|
use crate::cert::generate::{generate_inter_cert, generate_root_cert};
|
|
use crate::cert::generate::InterTarget::{CLIENT, PROXY};
|
|
use crate::cert::Types::{CLIENTINTER, PROXYINTER, ROOT};
|
|
use crate::certificate::Model;
|
|
use crate::entities::certificate;
|
|
|
|
mod entities;
|
|
mod dns;
|
|
mod util;
|
|
mod routes;
|
|
mod rpc;
|
|
mod cert;
|
|
|
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
console_subscriber::init();
|
|
|
|
dotenv().ok();
|
|
|
|
// Default to logging all info logs
|
|
if env::var("RUST_LOG").is_err() {
|
|
env::set_var("RUST_LOG", "info")
|
|
}
|
|
|
|
//pretty_env_logger::init();
|
|
|
|
info!("██████╗ ██████╗ ██╗██████╗ ████████╗ ██████╗ ██████╗ ██████╗██╗ ██╗");
|
|
info!("██╔══██╗██╔══██╗██║██╔══██╗╚══██╔══╝██╔═══██╗██╔══██╗██╔════╝██║ ██║");
|
|
info!("██║ ██║██████╔╝██║██████╔╝ ██║ ██║ ██║██████╔╝██║ ███████║");
|
|
info!("██║ ██║██╔══██╗██║██╔═══╝ ██║ ██║ ██║██╔══██╗██║ ██╔══██║");
|
|
info!("██████╔╝██║ ██║██║██║ ██║ ╚██████╔╝██║ ██║╚██████╗██║ ██║");
|
|
info!("╚═════╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝");
|
|
|
|
info!("._______ ._______ .______ _____._.______ ._______ .___ .___ ._______.______");
|
|
info!(":_. ___\\: .___ \\ : \\ \\__ _:|: __ \\ : .___ \\ | | | | : .____/: __ \\ ");
|
|
info!("| : |/\\ | : | || | | :|| \\____|| : | || | | | | : _/\\ | \\____|");
|
|
info!("| / \\| : || | | | || : \\ | : || |/\\ | |/\\ | / \\| : \\ ");
|
|
info!("|. _____/ \\_. ___/ |___| | | || |___\\ \\_. ___/ | / \\| / \\|_.: __/| |___\\");
|
|
info!(" :/ :/ |___| |___||___| :/ |______/|______/ :/ |___| ");
|
|
info!(" : : : ");
|
|
|
|
info!("Version {}", VERSION);
|
|
|
|
info!("Checking for supplemental files...");
|
|
if !Path::new(&env::var("UAP_REGEXES").unwrap_or(String::from("./regexes.yaml"))).exists(){
|
|
error!("Please download https://github.com/ua-parser/uap-core/blob/master/regexes.yaml either place it next to the executable or add it's path to env variable UAP_REGEXES! Halting start-up.");
|
|
std::process::exit(1);
|
|
}
|
|
if !Path::new(&env::var("RSA_KEY").expect("RSA_KEY must be set! Halting start-up.")).exists() {
|
|
error!("Please generate an RSA private key for creating certificates!")
|
|
}
|
|
if !Path::new(&env::var("XCC20_KEY").expect("XCC20_KEY must be set! Halting start-up.")).exists() {
|
|
error!("Please generate a 32 bits of randomness to encrypt private keys!")
|
|
}
|
|
|
|
let root_rsa_key = PrivateKey::from_pem_str(
|
|
&*fs::read_to_string(
|
|
Path::new(&env::var("RSA_KEY").expect("RSA_KEY must be set! Halting start-up."))
|
|
).expect("Failed to load the root RSA key! Halting start-up.")
|
|
).expect("Failed to load the root RSA key! Halting start-up.");
|
|
|
|
info!("Connecting to database...");
|
|
let database_url = env::var("DATABASE_URL")
|
|
.expect("DATABASE_URL must be set! Halting start-up.");
|
|
let connection = Database::connect(
|
|
ConnectOptions::new(database_url).sqlx_logging(false).to_owned())
|
|
.await
|
|
.expect("Failed to connect to the database! Halting start-up.");
|
|
|
|
info!("Running migrations...");
|
|
migration::Migrator::up(&connection, None)
|
|
.await
|
|
.expect("Failed to run migrations! Halting start-up.");
|
|
|
|
info!("Checking for root cert...");
|
|
let root_cert_model: Option<Model> = certificate::Entity::find()
|
|
.filter(certificate::Column::CertType.eq(cert::Types::ROOT.to_string()))
|
|
.one(&connection)
|
|
.await
|
|
.expect("Failed to retrieve the root cert from the database! Halting start-up.");
|
|
|
|
let root_cert: Cert;
|
|
|
|
match root_cert_model {
|
|
None => {
|
|
info!("Generating new root cert...");
|
|
|
|
let new_root_cert = generate_root_cert(&root_rsa_key)
|
|
.await
|
|
.expect("Failed to generate the root cert from private key! Halting start-up.");
|
|
|
|
root_cert = new_root_cert.clone();
|
|
|
|
let root_insert = certificate::Entity::insert(certificate::ActiveModel {
|
|
id: ActiveValue::Set(Ulid::new().to_string()),
|
|
data: ActiveValue::Set(
|
|
new_root_cert.to_der().expect("Failed to convert cert into der!")
|
|
),
|
|
key: ActiveValue::set(vec![145, 66, 62, 61, 56, 156, 145, 164]),
|
|
nonce: ActiveValue::set(vec![145, 71, 62, 66, 56, 156, 145, 164]),
|
|
cert_type: ActiveValue::Set(ROOT.to_string())
|
|
})
|
|
.exec(&connection)
|
|
.await
|
|
.expect("Failed to insert new root cert into database! Halting start-up.");
|
|
|
|
info!("Generated new root cert: {}", root_insert.last_insert_id);
|
|
}
|
|
Some(root_cert_model) => {
|
|
info!("Found root cert: {}", root_cert_model.id);
|
|
|
|
root_cert = Cert::from_der(&root_cert_model.data)
|
|
.expect("Failed to decode root cert!");
|
|
}
|
|
}
|
|
|
|
info!("Checking for proxy intermediate cert...");
|
|
let proxy_inter_model: Option<Model> = certificate::Entity::find()
|
|
.filter(certificate::Column::CertType.eq(PROXYINTER.to_string()))
|
|
.one(&connection)
|
|
.await
|
|
.expect("Failed to retrieve the proxy intermediate cert from the database! Halting start-up.");
|
|
|
|
let proxy_inter_cert: Cert;
|
|
|
|
match proxy_inter_model {
|
|
None => {
|
|
info!("Generating proxy intermediate cert...");
|
|
|
|
// Generate 4096 bit RSA private key
|
|
let priv_key = PrivateKey::generate_rsa(4096)
|
|
.expect("Failed to generate a key");
|
|
|
|
let encrypted_priv_key = encrypt_priv_key(priv_key
|
|
.clone()
|
|
.to_pkcs8()
|
|
.expect("Failed to convert generated private key to pkcs8!")
|
|
).await;
|
|
|
|
let new_proxy_inter_cert = generate_inter_cert(&priv_key, PROXY, (&root_cert, &root_rsa_key))
|
|
.await
|
|
.expect("Failed to generate new proxy intermediate cert");
|
|
|
|
proxy_inter_cert = new_proxy_inter_cert.clone();
|
|
|
|
let proxy_inter_insert = certificate::Entity::insert(certificate::ActiveModel {
|
|
id: ActiveValue::Set(Ulid::new().to_string()),
|
|
data: ActiveValue::Set(
|
|
new_proxy_inter_cert.to_der().expect("Failed to convert cert into der!")
|
|
),
|
|
key: ActiveValue::set(encrypted_priv_key.1),
|
|
nonce: ActiveValue::set(encrypted_priv_key.0),
|
|
cert_type: ActiveValue::Set(PROXYINTER.to_string())
|
|
})
|
|
.exec(&connection)
|
|
.await
|
|
.expect("Failed to insert new proxy intermediate cert into database! Halting start-up.");
|
|
|
|
info!("Generated new proxy intermediate cert: {}", proxy_inter_insert.last_insert_id)
|
|
}
|
|
Some(proxy_inter_model) => {
|
|
info!("Found proxy intermediate cert: {}", proxy_inter_model.id);
|
|
|
|
proxy_inter_cert = Cert::from_der(&proxy_inter_model.data)
|
|
.expect("Failed to decode proxy intermediate cert!");
|
|
}
|
|
}
|
|
|
|
info!("Checking for client intermediate cert...");
|
|
let client_inter_model: Option<Model> = certificate::Entity::find()
|
|
.filter(certificate::Column::CertType.eq(CLIENTINTER.to_string()))
|
|
.one(&connection)
|
|
.await
|
|
.expect("Failed to retrieve the client intermediate cert from the database! Halting start-up.");
|
|
|
|
let client_inter_cert: Cert;
|
|
|
|
match client_inter_model {
|
|
None => {
|
|
info!("Generating client intermediate cert...");
|
|
|
|
// Generate 4096 bit RSA private key
|
|
let priv_key = PrivateKey::generate_rsa(4096)
|
|
.expect("Failed to generate a key");
|
|
|
|
let encrypted_priv_key = encrypt_priv_key(priv_key
|
|
.clone()
|
|
.to_pkcs8()
|
|
.expect("Failed to convert generated private key to pkcs8!")
|
|
).await;
|
|
|
|
let new_client_inter_cert = generate_inter_cert(&priv_key, CLIENT, (&root_cert, &root_rsa_key))
|
|
.await
|
|
.expect("Failed to generate new client intermediate cert");
|
|
|
|
client_inter_cert = new_client_inter_cert.clone();
|
|
|
|
let client_inter_insert = certificate::Entity::insert(certificate::ActiveModel {
|
|
id: ActiveValue::Set(Ulid::new().to_string()),
|
|
data: ActiveValue::Set(
|
|
new_client_inter_cert.to_der().expect("Failed to convert cert into der!")
|
|
),
|
|
key: ActiveValue::set(encrypted_priv_key.1),
|
|
nonce: ActiveValue::set(encrypted_priv_key.0),
|
|
cert_type: ActiveValue::Set(CLIENTINTER.to_string())
|
|
})
|
|
.exec(&connection)
|
|
.await
|
|
.expect("Failed to insert new client intermediate cert into database! Halting start-up.");
|
|
|
|
info!("Generated new client intermediate cert: {}", client_inter_insert.last_insert_id)
|
|
}
|
|
Some(client_inter_model) => {
|
|
info!("Found client intermediate cert: {}", client_inter_model.id);
|
|
|
|
client_inter_cert = Cert::from_der(&client_inter_model.data)
|
|
.expect("Failed to decode client intermediate cert!");
|
|
}
|
|
}
|
|
|
|
info!("Connecting to message broker...");
|
|
let amqp_addr = env::var("AMQP_ADDR")
|
|
.expect("AMQP_ADDR mut be set! Halting start-up.");
|
|
let amqp_connection = lapin::Connection::connect(&amqp_addr, ConnectionProperties::default())
|
|
.await
|
|
.expect("Failed to connect to the message broker! Halting start-up.");
|
|
let amqp_channel = amqp_connection.create_channel()
|
|
.await
|
|
.expect("Failed to create a message broker channel! Halting start-up.");
|
|
|
|
info!("Starting web server...");
|
|
let app = Router::new()
|
|
// Users
|
|
//-- Auth
|
|
.route("/user/register", post(routes::users::register::register))
|
|
.route("/user/login", post(routes::users::login::login))
|
|
.route("/user/logout", post(routes::users::logout::logout))
|
|
.route("/user/delete", delete(routes::users::delete::delete))
|
|
//-- Information
|
|
.route("/user/list_sessions", get(routes::users::list_sessions::list_sessions))
|
|
//-- Settings
|
|
|
|
// Teams
|
|
|
|
// Zones
|
|
|
|
// Records
|
|
|
|
// Proxies
|
|
|
|
// Admin
|
|
|
|
// RPC
|
|
.route("/rpc", post(rpc::rpc))
|
|
|
|
// Misc
|
|
.route("/", get(routes::status::status))
|
|
|
|
.layer(
|
|
ServiceBuilder::new()
|
|
.layer(Extension(connection))
|
|
.layer(Extension(amqp_channel))
|
|
);
|
|
|
|
let addr = env::var("LISTEN_ADDR")
|
|
.unwrap_or("127.0.0.1:32204".to_string());
|
|
|
|
let socket_addr: SocketAddr = addr.parse().expect("Failed to parse LISTEN_ADDR! Halting start-up.");
|
|
|
|
let axum_builder = axum::Server::try_bind(&socket_addr);
|
|
|
|
match axum_builder {
|
|
Ok(axum_builder) => {
|
|
info!("Driptorch Controller v{} is now listening on {}!", VERSION, socket_addr);
|
|
|
|
axum_builder
|
|
.serve(app.into_make_service_with_connect_info::<SocketAddr>())
|
|
.await
|
|
.expect("Failed to bind to port! Halting start-up.");
|
|
}
|
|
Err(_) => {
|
|
error!("Driptorch Controller v{} failed to bind to {}! Halting start-up.", VERSION, socket_addr);
|
|
|
|
std::process::exit(1);
|
|
}
|
|
}
|
|
}
|