use std::os::unix::fs::MetadataExt; use std::path::Path; use std::str::FromStr; use rsa::RsaPrivateKey; use crate::utils::crypto_rsa::{ deserialize_private_key, generate_rsa_key, serialize_private_key, }; use crate::utils::files::{set_file_permissions, write_file}; use super::environment::Environment; use super::main::{Config, RegistrationType}; struct EnvConfig { config_path: String, environment: Option, } #[cfg(feature = "production")] const DEFAULT_CONFIG_PATH: &str = "/etc/mitra/config.yaml"; #[cfg(not(feature = "production"))] const DEFAULT_CONFIG_PATH: &str = "config.yaml"; fn parse_env() -> EnvConfig { dotenv::from_filename(".env.local").ok(); dotenv::dotenv().ok(); let config_path = std::env::var("CONFIG_PATH") .unwrap_or(DEFAULT_CONFIG_PATH.to_string()); let environment = std::env::var("ENVIRONMENT").ok() .map(|val| Environment::from_str(&val).expect("invalid environment type")); EnvConfig { config_path, environment, } } extern "C" { fn geteuid() -> u32; } fn check_directory_owner(path: &Path) -> () { let metadata = std::fs::metadata(path) .expect("can't read file metadata"); let owner_uid = metadata.uid(); let current_uid = unsafe { geteuid() }; if owner_uid != current_uid { panic!( "{} owner ({}) is different from the current user ({})", path.display(), owner_uid, current_uid, ); }; } /// Generates new instance RSA key or returns existing key fn read_instance_rsa_key(storage_dir: &Path) -> RsaPrivateKey { let private_key_path = storage_dir.join("instance_rsa_key"); if private_key_path.exists() { let private_key_str = std::fs::read_to_string(&private_key_path) .expect("failed to read instance RSA key"); let private_key = deserialize_private_key(&private_key_str) .expect("failed to read instance RSA key"); private_key } else { let private_key = generate_rsa_key() .expect("failed to generate RSA key"); let private_key_str = serialize_private_key(&private_key) .expect("failed to serialize RSA key"); write_file(private_key_str.as_bytes(), &private_key_path) .expect("failed to write instance RSA key"); set_file_permissions(&private_key_path, 0o600) .expect("failed to set permissions on RSA key file"); private_key } } pub fn parse_config() -> (Config, Vec<&'static str>) { let env = parse_env(); let config_yaml = std::fs::read_to_string(&env.config_path) .expect("failed to load config file"); let mut config = serde_yaml::from_str::(&config_yaml) .expect("invalid yaml data"); let mut warnings = vec![]; // Set parameters from environment config.config_path = env.config_path; if let Some(environment) = env.environment { // Overwrite default only if ENVIRONMENT variable is set config.environment = environment; }; // Validate config if !config.storage_dir.exists() { panic!("storage directory does not exist"); }; check_directory_owner(&config.storage_dir); config.try_instance_url().expect("invalid instance URI"); if let Some(blockchain_config) = config.blockchain() { if let Some(ethereum_config) = blockchain_config.ethereum_config() { ethereum_config.try_ethereum_chain_id().unwrap(); if !ethereum_config.contract_dir.exists() { panic!("contract directory does not exist"); }; }; }; if config.ipfs_api_url.is_some() != config.ipfs_gateway_url.is_some() { panic!("both ipfs_api_url and ipfs_gateway_url must be set"); }; if let Some(registrations_open) = config.registrations_open { // Change type if 'registrations_open' parameter is used warnings.push("'registrations_open' setting is deprecated, use 'registration' instead"); if registrations_open { config.registration.registration_type = RegistrationType::Open; } else { config.registration.registration_type = RegistrationType::Invite; }; }; // Insert instance RSA key config.instance_rsa_key = Some(read_instance_rsa_key(&config.storage_dir)); (config, warnings) }