aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
blob: 857d24e793f25a7fc214972bca9c0a17e985a4cc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#[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<String>,

	/// Client certificate for Consul server with TLS
	#[structopt(long = "consul-client-cert", env = "TRICOT_CONSUL_CLIENT_CERT")]
	pub consul_client_cert: Option<String>,

	/// Client key for Consul server with TLS
	#[structopt(long = "consul-client-key", env = "TRICOT_CONSUL_CLIENT_KEY")]
	pub consul_client_key: Option<String>,

	/// 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 = "<none>")]
	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() {
		info!("Proxy config:");
		for ent in rx_proxy_config.borrow().entries.iter() {
			info!("   {}", ent);
		}
	}
}

fn exit_on_err(e: anyhow::Error) {
	error!("{}", e);
	std::process::exit(1);
}