#[macro_use] extern crate anyhow; use futures::TryFutureExt; use std::net::SocketAddr; use structopt::StructOpt; mod cert; mod cert_store; mod consul; mod http; mod https; mod proxy_config; mod reverse_proxy; mod tls_util; use log::*; #[derive(StructOpt, Debug)] #[structopt(name = "tricot")] struct Opt { /// Address of consul server #[structopt( long = "consul-addr", env = "TRICOT_CONSUL_HOST", default_value = "http://127.0.0.1:8500" )] pub consul_addr: String, /// CA certificate for Consul server with TLS #[structopt(long = "consul-ca-cert", env = "TRICOT_CONSUL_CA_CERT")] pub consul_ca_cert: Option, /// Client certificate for Consul server with TLS #[structopt(long = "consul-client-cert", env = "TRICOT_CONSUL_CLIENT_CERT")] pub consul_client_cert: Option, /// Client key for Consul server with TLS #[structopt(long = "consul-client-key", env = "TRICOT_CONSUL_CLIENT_KEY")] pub consul_client_key: Option, /// Prefix of Tricot's entries in Consul KV space #[structopt( long = "consul-kv-prefix", env = "TRICOT_CONSUL_KV_PREFIX", default_value = "tricot/" )] pub consul_kv_prefix: String, /// Node name #[structopt(long = "node-name", env = "TRICOT_NODE_NAME", default_value = "")] pub node_name: String, /// Bind address for HTTP server #[structopt( long = "http-bind-addr", env = "TRICOT_HTTP_BIND_ADDR", default_value = "0.0.0.0:80" )] pub http_bind_addr: SocketAddr, /// Bind address for HTTPS server #[structopt( long = "https-bind-addr", env = "TRICOT_HTTPS_BIND_ADDR", default_value = "0.0.0.0:443" )] pub https_bind_addr: SocketAddr, /// E-mail address for Let's Encrypt certificate requests #[structopt(long = "letsencrypt-email", env = "TRICOT_LETSENCRYPT_EMAIL")] pub letsencrypt_email: String, /// Enable compression of responses #[structopt(long = "enable-compression", env = "TRICOT_ENABLE_COMPRESSION")] pub enable_compression: bool, /// Mime types for which to enable compression (comma-separated list) #[structopt( long = "compress-mime-types", env = "TRICOT_COMPRESS_MIME_TYPES", default_value = "text/html,text/plain,text/css,text/javascript,text/xml,application/javascript,application/json,application/xml,image/svg+xml,font/ttf" )] pub compress_mime_types: String, } #[tokio::main(flavor = "multi_thread", worker_threads = 10)] async fn main() { if std::env::var("RUST_LOG").is_err() { std::env::set_var("RUST_LOG", "tricot=info") } pretty_env_logger::init(); // Abort on panic (same behavior as in Go) std::panic::set_hook(Box::new(|panic_info| { error!("{}", panic_info.to_string()); std::process::abort(); })); let opt = Opt::from_args(); info!("Starting Tricot"); let consul_config = consul::ConsulConfig { addr: opt.consul_addr.clone(), ca_cert: opt.consul_ca_cert.clone(), client_cert: opt.consul_client_cert.clone(), client_key: opt.consul_client_key.clone(), }; let consul = consul::Consul::new(consul_config, &opt.consul_kv_prefix, &opt.node_name) .expect("Error creating Consul client"); let mut rx_proxy_config = proxy_config::spawn_proxy_config_task(consul.clone()); let cert_store = cert_store::CertStore::new( consul.clone(), rx_proxy_config.clone(), opt.letsencrypt_email.clone(), ); tokio::spawn(http::serve_http(opt.http_bind_addr, consul.clone()).map_err(exit_on_err)); let https_config = https::HttpsConfig { bind_addr: opt.https_bind_addr, enable_compression: opt.enable_compression, compress_mime_types: opt .compress_mime_types .split(',') .map(|x| x.to_string()) .collect(), }; tokio::spawn( https::serve_https(https_config, cert_store.clone(), rx_proxy_config.clone()) .map_err(exit_on_err), ); while rx_proxy_config.changed().await.is_ok() { println!("---- PROXY CONFIGURATION ----"); for ent in rx_proxy_config.borrow().entries.iter() { println!(" {}", ent); } println!(); } } fn exit_on_err(e: anyhow::Error) { error!("{}", e); std::process::exit(1); }