-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathmain.rs
More file actions
150 lines (125 loc) · 4.67 KB
/
Copy pathmain.rs
File metadata and controls
150 lines (125 loc) · 4.67 KB
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
149
150
mod config;
mod observability;
mod policy;
mod proxy;
mod registry;
mod tls;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use anyhow::Context;
use clap::{Parser, Subcommand};
use hyper_util::rt::TokioExecutor;
use sqlx::postgres::{PgConnectOptions, PgPool, PgPoolOptions};
use tokio::net::TcpListener;
use tracing::{error, info};
use crate::policy::PostgresPolicyEngine;
use crate::proxy::MakeProxyService;
#[derive(Parser)]
#[command(name = "agent_gateway", about = "mTLS HTTP/2 CONNECT proxy")]
struct Cli {
/// Path to the TOML configuration file
#[arg(short, long, default_value = "config.toml")]
config: PathBuf,
#[command(subcommand)]
command: Option<CliCommand>,
}
#[derive(Clone, Copy, Subcommand)]
enum CliCommand {
/// Run authorization registry database migrations and exit
Migrate,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
rustls::crypto::aws_lc_rs::default_provider()
.install_default()
.expect("failed to install default crypto provider");
let cli = Cli::parse();
let config = config::Config::load(&cli.config)
.with_context(|| format!("loading config from {}", cli.config.display()))?;
match cli.command {
None => serve(config).await,
Some(CliCommand::Migrate) => migrate(config).await,
}
}
async fn serve(config: config::Config) -> anyhow::Result<()> {
observability::init(&config.observability)?;
let server_tls = tls::build_server_config(&config.server)?;
let tls_acceptor = tls::TlsAcceptor::from(server_tls);
let db_pool = build_pg_pool(&config.policy).await?;
registry::RegistryStore::verify_schema_version(&db_pool).await?;
let registry = registry::RegistryStore::new(db_pool, config.policy.query_timeout());
let policy_engine: Arc<dyn policy::PolicyEngine> = Arc::new(PostgresPolicyEngine::new(
&config.policy.client_ext_oid,
registry,
)?);
let make_service = Arc::new(MakeProxyService::new(policy_engine));
let listen_addr: std::net::SocketAddr = config.server.listen_addr.parse()?;
let listener = TcpListener::bind(listen_addr).await?;
info!(%listen_addr, "listening");
tokio::select! {
result = serve_loop(&listener, &tls_acceptor, &make_service) => {
result?;
}
_ = tokio::signal::ctrl_c() => {
info!("received shutdown signal");
}
}
observability::shutdown();
Ok(())
}
async fn migrate(config: config::Config) -> anyhow::Result<()> {
let db_pool = build_pg_pool(&config.policy).await?;
registry::RegistryStore::run_migrations(&db_pool).await?;
Ok(())
}
async fn build_pg_pool(policy: &config::PolicyConfig) -> anyhow::Result<PgPool> {
let database_url = policy.database_url()?;
let connect_options = PgConnectOptions::from_str(&database_url)
.context("parsing authorization registry database URL")?;
let connect = PgPoolOptions::new()
.max_connections(policy.max_connections())
.acquire_timeout(policy.pool_acquire_timeout())
.connect_with(connect_options);
tokio::time::timeout(policy.connect_timeout(), connect)
.await
.context("authorization registry database connect timed out")?
.context("connecting to authorization registry database")
}
async fn serve_loop(
listener: &TcpListener,
tls_acceptor: &tls::TlsAcceptor,
make_service: &Arc<MakeProxyService>,
) -> anyhow::Result<()> {
loop {
let (tcp_stream, peer_addr) = match listener.accept().await {
Ok(conn) => conn,
Err(e) => {
error!(error = %e, "TCP accept failed");
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
continue;
}
};
let acceptor = tls_acceptor.clone();
let make_svc = make_service.clone();
tokio::spawn(async move {
let tls_stream = match acceptor.accept(tcp_stream).await {
Ok(s) => s,
Err(e) => {
error!(source_peer_addr = %peer_addr, error = %e, "TLS handshake failed");
return;
}
};
let peer_certs = proxy::extract_peer_certs(tls_stream.get_ref().1);
let service = make_svc.make_service(peer_certs, peer_addr);
let io = hyper_util::rt::TokioIo::new(tls_stream);
if let Err(e) = hyper_util::server::conn::auto::Builder::new(TokioExecutor::new())
.http2_only()
.serve_connection_with_upgrades(io, service)
.await
{
error!(source_peer_addr = %peer_addr, error = %e, "connection error");
}
});
}
}