use std::path::Path;
use telelog_proto::auth::connect;
use telelog_proto::v1::ListTargetsRequest;
use telelog_server::{Config, TlsFiles};
use telelog_sources::{DockerSource, Sources};
use tokio::net::TcpListener;
use tokio::sync::oneshot;
const TOKEN: &str = "test-token-0123456789abcdef";
struct Running {
port: u16,
_stop: oneshot::Sender<()>,
}
async fn start(config: Config) -> Running {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let port = listener.local_addr().unwrap().port();
let (stop, stopped) = oneshot::channel::<()>();
let sources = Sources {
docker: Some(DockerSource::connect_http("tcp://127.0.0.1:9").unwrap()),
kubernetes: None,
};
tokio::spawn(async move {
telelog_server::serve(listener, config, sources, async {
let _ = stopped.await;
})
.await
.unwrap();
});
Running { port, _stop: stop }
}
async fn list_targets(endpoint: &str, token: Option<&str>, ca: Option<&[u8]>) -> tonic::Code {
let mut client = connect(endpoint, token, ca).await.expect("transport connects");
match client.list_targets(ListTargetsRequest { all: false }).await {
Ok(_) => tonic::Code::Ok,
Err(status) => status.code(),
}
}
fn passed_auth(code: tonic::Code) -> bool {
matches!(code, tonic::Code::Ok | tonic::Code::Unavailable)
}
#[tokio::test]
async fn plaintext_server_requires_the_token() {
let server = start(Config {
token: Some(TOKEN.into()),
tls: None,
archive: None,
})
.await;
let endpoint = format!("http://127.0.0.1:{}", server.port);
assert_eq!(list_targets(&endpoint, None, None).await, tonic::Code::Unauthenticated);
assert_eq!(
list_targets(&endpoint, Some("wrong-token-0000000000"), None).await,
tonic::Code::Unauthenticated
);
assert!(passed_auth(list_targets(&endpoint, Some(TOKEN), None).await));
}
#[tokio::test]
async fn server_without_token_is_open() {
let server = start(Config {
token: None,
tls: None,
archive: None,
})
.await;
let endpoint = format!("http://127.0.0.1:{}", server.port);
assert!(passed_auth(list_targets(&endpoint, None, None).await));
}
fn write_self_signed(dir: &Path) -> (TlsFiles, Vec<u8>) {
let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_string()]).unwrap();
let cert_pem = cert.cert.pem();
let (cert_path, key_path) = (dir.join("cert.pem"), dir.join("key.pem"));
std::fs::write(&cert_path, &cert_pem).unwrap();
std::fs::write(&key_path, cert.signing_key.serialize_pem()).unwrap();
(
TlsFiles {
cert: cert_path,
key: key_path,
},
cert_pem.into_bytes(),
)
}
#[tokio::test]
async fn tls_server_with_token() {
let dir = std::env::temp_dir().join(format!("telelog-tls-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let (tls, ca) = write_self_signed(&dir);
let server = start(Config {
token: Some(TOKEN.into()),
tls: Some(tls),
archive: None,
})
.await;
let endpoint = format!("https://localhost:{}", server.port);
assert_eq!(
list_targets(&endpoint, None, Some(&ca)).await,
tonic::Code::Unauthenticated
);
assert!(passed_auth(list_targets(&endpoint, Some(TOKEN), Some(&ca)).await));
let Err(untrusted) = connect(&endpoint, Some(TOKEN), None).await else {
panic!("untrusted certificate was accepted");
};
assert!(format!("{untrusted:#}").contains("UnknownIssuer"), "{untrusted:#}");
let plain = format!("http://localhost:{}", server.port);
if let Ok(mut client) = connect(&plain, Some(TOKEN), None).await {
assert!(client.list_targets(ListTargetsRequest { all: false }).await.is_err());
}
let _ = std::fs::remove_dir_all(&dir);
}