Skip to content

Commit 399046f

Browse files
author
Nils Bars
committed
Fix ssh-reverse-proxy log filter and migrate startup logs to tracing
The RUST_LOG filter targeted ref_ssh_proxy, which is not the crate name (ssh_reverse_proxy), so every info!/error! from the binary was filtered out. Update the compose env var and the in-source fallback. Replace the remaining eprintln! debug prints with appropriately-leveled tracing calls so the SSH-proxy actually surfaces auth, provision, and connect events.
1 parent 2b8542d commit 399046f

3 files changed

Lines changed: 40 additions & 111 deletions

File tree

docker-compose.template.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ services:
213213
- API_BASE_URL=http://web:8000
214214
- SSH_TO_WEB_KEY=${SSH_TO_WEB_KEY:?SSH_TO_WEB_KEY not set}
215215
- CONTAINER_SSH_PORT=13370
216-
- RUST_LOG=ref_ssh_proxy=info,russh=warn
216+
- RUST_LOG=ssh_reverse_proxy=info,russh=warn
217217
volumes:
218218
- ./container-keys:/keys:ro
219219
- ./data/ssh-proxy:/data

ssh-reverse-proxy/src/main.rs

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,64 +11,44 @@ mod server;
1111
use anyhow::Result;
1212
use config::Config;
1313
use std::io::Write;
14-
use tracing::{error, info};
14+
use tracing::{debug, error, info};
1515
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
1616

1717
#[tokio::main]
1818
async fn main() -> Result<()> {
19-
// Force stdout to be line-buffered (important for Docker container logs)
20-
// This ensures logs appear immediately in docker logs output
19+
// Pre-init eprintln: tracing isn't up yet, so we cannot use info! here.
20+
// Kept so a misconfigured RUST_LOG can't fully silence the binary at startup.
2121
eprintln!("[SSH-PROXY] Starting initialization...");
2222
std::io::stderr().flush().ok();
2323

24-
// Initialize logging with line-buffered output
2524
tracing_subscriber::registry()
2625
.with(
2726
tracing_subscriber::EnvFilter::try_from_default_env()
28-
.unwrap_or_else(|_| "ref_ssh_proxy=info,russh=warn".into()),
27+
.unwrap_or_else(|_| "ssh_reverse_proxy=info,russh=warn".into()),
2928
)
3029
.with(tracing_subscriber::fmt::layer().with_writer(std::io::stderr))
3130
.init();
3231

33-
eprintln!("[SSH-PROXY] Tracing initialized");
34-
std::io::stderr().flush().ok();
3532
info!("REF SSH Proxy starting...");
3633

37-
// Load configuration
38-
eprintln!("[SSH-PROXY] Loading configuration...");
39-
std::io::stderr().flush().ok();
40-
4134
let config = match std::env::args().nth(1) {
4235
Some(config_path) => {
43-
eprintln!("[SSH-PROXY] Loading config from file: {}", config_path);
44-
std::io::stderr().flush().ok();
36+
info!("Loading config from file: {}", config_path);
4537
Config::load(&config_path)?
4638
}
4739
None => {
48-
eprintln!("[SSH-PROXY] Loading config from environment");
49-
std::io::stderr().flush().ok();
40+
info!("Loading config from environment");
5041
Config::from_env()?
5142
}
5243
};
5344

54-
eprintln!("[SSH-PROXY] Config loaded:");
55-
eprintln!("[SSH-PROXY] Listen: {}", config.server.listen_addr);
56-
eprintln!("[SSH-PROXY] API: {}", config.api.base_url);
57-
eprintln!("[SSH-PROXY] Container port: {}", config.container.ssh_port);
58-
std::io::stderr().flush().ok();
59-
6045
info!("Configuration loaded:");
6146
info!(" Listen address: {}", config.server.listen_addr);
6247
info!(" API base URL: {}", config.api.base_url);
6348
info!(" Container SSH port: {}", config.container.ssh_port);
6449

65-
// Run the server
66-
eprintln!("[SSH-PROXY] Starting server...");
67-
std::io::stderr().flush().ok();
68-
50+
debug!("Starting server task");
6951
if let Err(e) = server::run_server(config).await {
70-
eprintln!("[SSH-PROXY] Server error: {}", e);
71-
std::io::stderr().flush().ok();
7252
error!("Server error: {}", e);
7353
return Err(e);
7454
}

ssh-reverse-proxy/src/server.rs

Lines changed: 32 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -348,116 +348,88 @@ impl server::Handler for SshConnection {
348348
user: &str,
349349
public_key: &russh::keys::PublicKey,
350350
) -> Result<Auth, Self::Error> {
351-
use std::io::Write;
352-
eprintln!("[SSH-PROXY] auth_publickey called: user={}", user);
353-
std::io::stderr().flush().ok();
354351
info!("[AUTH] Auth attempt started: user={}", user);
355352

356353
// Store the exercise name from the username
357354
self.state.exercise_name = user.to_string();
358355

359-
// Format the public key for comparison
360-
eprintln!("[SSH-PROXY] Formatting public key...");
361-
std::io::stderr().flush().ok();
362356
let key_str = Self::format_pubkey(public_key);
363-
eprintln!("[SSH-PROXY] Client public key: {}", key_str);
364-
std::io::stderr().flush().ok();
365357
info!("[AUTH] Client public key: {}", key_str);
366358

367359
// Helper to check if key is in cache
368360
let check_key_in_cache = |cache: &[String], key: &str| -> bool {
369361
let key_parts: Vec<&str> = key.split_whitespace().collect();
370-
eprintln!("[SSH-PROXY] Client key parts count: {}", key_parts.len());
371-
std::io::stderr().flush().ok();
372362
if key_parts.len() >= 2 {
373-
eprintln!("[SSH-PROXY] Client key type: {}, data (first 40): {}...",
363+
debug!(
364+
"Client key type={}, data prefix={}",
374365
key_parts[0],
375-
&key_parts[1][..std::cmp::min(40, key_parts[1].len())]);
376-
std::io::stderr().flush().ok();
366+
&key_parts[1][..std::cmp::min(40, key_parts[1].len())]
367+
);
368+
} else {
369+
debug!("Client key has unexpected parts count: {}", key_parts.len());
377370
}
378371

379372
for (i, k) in cache.iter().enumerate() {
380373
let cached_parts: Vec<&str> = k.split_whitespace().collect();
381374
if cached_parts.len() >= 2 {
382-
eprintln!("[SSH-PROXY] Cached key {}: type={}, data (first 40): {}...",
383-
i, cached_parts[0],
384-
&cached_parts[1][..std::cmp::min(40, cached_parts[1].len())]);
385-
std::io::stderr().flush().ok();
386375
if key_parts.len() >= 2 && key_parts[1] == cached_parts[1] {
387-
eprintln!("[SSH-PROXY] Found matching key at index {}", i);
388-
std::io::stderr().flush().ok();
376+
debug!("Found matching cached key at index {}", i);
389377
return true;
390378
}
391379
} else {
392-
eprintln!("[SSH-PROXY] Cached key {} has {} parts: {:?}", i, cached_parts.len(), k);
393-
std::io::stderr().flush().ok();
380+
debug!(
381+
"Cached key {} has unexpected parts count: {}",
382+
i,
383+
cached_parts.len()
384+
);
394385
}
395386
}
396-
eprintln!("[SSH-PROXY] No matching key found in cache");
397-
std::io::stderr().flush().ok();
387+
debug!("No matching key found in cache");
398388
false
399389
};
400390

401-
// Check if the key is in our valid keys cache
402-
eprintln!("[SSH-PROXY] Checking key against cache...");
403-
std::io::stderr().flush().ok();
404391
let mut is_valid = {
405392
let cache = self.valid_keys.lock().await;
406-
eprintln!("[SSH-PROXY] Cache has {} keys", cache.len());
407-
std::io::stderr().flush().ok();
408393
info!("[AUTH] Checking key against {} cached keys", cache.len());
409394
check_key_in_cache(&cache, &key_str)
410395
};
411396

412397
// If not found, refresh keys and try again (for newly registered users)
413398
if !is_valid {
414-
eprintln!("[SSH-PROXY] Key not in cache, refreshing on-demand...");
415-
std::io::stderr().flush().ok();
416399
info!("[AUTH] Key not in cache, refreshing keys on-demand");
417400
match self.api_client.get_keys().await {
418401
Ok(keys) => {
419402
let mut cache = self.valid_keys.lock().await;
420-
eprintln!("[SSH-PROXY] On-demand refresh got {} keys", keys.len());
421-
std::io::stderr().flush().ok();
422403
info!("[AUTH] On-demand refresh got {} keys", keys.len());
423404
*cache = keys;
424405
is_valid = check_key_in_cache(&cache, &key_str);
425406
}
426407
Err(e) => {
427-
eprintln!("[SSH-PROXY] Failed to refresh keys: {}", e);
428-
std::io::stderr().flush().ok();
429408
error!("[AUTH] Failed to refresh keys on-demand: {}", e);
430409
}
431410
}
432411
}
433412

434413
if !is_valid {
435-
eprintln!("[SSH-PROXY] REJECTED: Invalid public key for user {}", user);
436-
std::io::stderr().flush().ok();
437414
error!("[AUTH] REJECTED: Invalid public key for user {}", user);
438415
return Ok(Auth::Reject {
439416
proceed_with_methods: None,
440417
partial_success: false,
441418
});
442419
}
443-
eprintln!("[SSH-PROXY] Key validation passed for user {}", user);
444-
std::io::stderr().flush().ok();
445420
info!("[AUTH] Key validation passed for user {}", user);
446421

447422
// Store the authenticated key
448423
self.state.pubkey = Some(key_str.clone());
449424

450425
// Get user permissions from API
451-
eprintln!("[SSH-PROXY] Calling ssh_authenticated API...");
452-
std::io::stderr().flush().ok();
426+
debug!("[AUTH] Calling ssh_authenticated API");
453427
match self
454428
.api_client
455429
.ssh_authenticated(&self.state.exercise_name, &key_str)
456430
.await
457431
{
458432
Ok(auth_response) => {
459-
eprintln!("[SSH-PROXY] ssh_authenticated succeeded: instance_id={}", auth_response.instance_id);
460-
std::io::stderr().flush().ok();
461433
// TODO: Use API response for permissions when webapp supports it
462434
// For now, mock all permissions as allowed (per user request)
463435
self.state.tcp_forwarding_allowed = true; // Mocked: always allow
@@ -476,16 +448,13 @@ impl server::Handler for SshConnection {
476448

477449
// Provision the container (skip if we already recorded a startup error)
478450
if self.state.startup_error.is_none() {
479-
eprintln!("[SSH-PROXY] Calling provision API...");
480-
std::io::stderr().flush().ok();
451+
debug!("[AUTH] Calling provision API");
481452
match self
482453
.api_client
483454
.provision(&self.state.exercise_name, &key_str)
484455
.await
485456
{
486457
Ok(provision) => {
487-
eprintln!("[SSH-PROXY] Provisioned container at {} (as_root={})", provision.ip, provision.as_root);
488-
std::io::stderr().flush().ok();
489458
self.state.container_ip = Some(provision.ip.clone());
490459
self.state.as_root = provision.as_root;
491460
self.state.welcome_message = provision.welcome_message;
@@ -502,8 +471,7 @@ impl server::Handler for SshConnection {
502471
}
503472
}
504473

505-
eprintln!("[SSH-PROXY] Auth complete - returning Accept");
506-
std::io::stderr().flush().ok();
474+
debug!("[AUTH] Auth complete - returning Accept");
507475
Ok(Auth::Accept)
508476
}
509477

@@ -1127,52 +1095,39 @@ fn spawn_key_refresh_task(
11271095

11281096
/// Run the SSH server.
11291097
pub async fn run_server(config: Config) -> Result<()> {
1130-
use std::io::Write;
1131-
eprintln!("[SSH-PROXY] run_server: Creating API client...");
1132-
std::io::stderr().flush().ok();
1133-
1098+
debug!("run_server: Creating API client");
11341099
let api_client = ApiClient::from_env(
11351100
config.api.base_url.clone(),
11361101
&config.api.signing_key_env,
11371102
)?;
11381103

1139-
eprintln!("[SSH-PROXY] run_server: Loading container keys...");
1140-
std::io::stderr().flush().ok();
1141-
1142-
// Load container keys
1104+
debug!("run_server: Loading container keys");
11431105
let container_keys = ContainerKeys::load(&config.container.keys_dir)?;
11441106

1145-
eprintln!("[SSH-PROXY] run_server: Creating server...");
1146-
std::io::stderr().flush().ok();
1147-
11481107
let mut server = SshServer::new(config.clone(), api_client.clone(), container_keys);
11491108

1150-
// Initial key refresh with retries (web server may not be ready yet)
1151-
eprintln!("[SSH-PROXY] run_server: Initial key refresh...");
1152-
std::io::stderr().flush().ok();
1153-
1109+
info!("Performing initial key refresh");
11541110
let max_retries = 30;
11551111
let mut retry_count = 0;
11561112
loop {
11571113
match server.refresh_keys().await {
11581114
Ok(_) => {
1159-
eprintln!("[SSH-PROXY] run_server: Keys refreshed successfully");
1160-
std::io::stderr().flush().ok();
1115+
info!("Initial key refresh succeeded");
11611116
break;
11621117
}
11631118
Err(e) => {
11641119
retry_count += 1;
11651120
if retry_count >= max_retries {
1166-
eprintln!("[SSH-PROXY] run_server: Failed to fetch keys after {} retries: {}", max_retries, e);
1167-
std::io::stderr().flush().ok();
1121+
error!(
1122+
"Failed to fetch keys after {} retries: {}",
1123+
max_retries, e
1124+
);
11681125
return Err(anyhow::anyhow!(
11691126
"Failed to fetch keys after {} retries: {}",
11701127
max_retries,
11711128
e
11721129
));
11731130
}
1174-
eprintln!("[SSH-PROXY] run_server: Key refresh attempt {} failed: {}. Retrying...", retry_count, e);
1175-
std::io::stderr().flush().ok();
11761131
warn!(
11771132
"Failed to fetch keys (attempt {}/{}): {}. Retrying in 1s...",
11781133
retry_count, max_retries, e
@@ -1182,21 +1137,20 @@ pub async fn run_server(config: Config) -> Result<()> {
11821137
}
11831138
}
11841139

1185-
// Spawn background task to periodically refresh keys (every 60 seconds)
1186-
eprintln!("[SSH-PROXY] run_server: Spawning key refresh task...");
1187-
std::io::stderr().flush().ok();
1140+
debug!("Spawning periodic key refresh task");
11881141
spawn_key_refresh_task(api_client, Arc::clone(&server.valid_keys), 60);
11891142

11901143
// Load or generate host key (persisted across restarts)
11911144
let key_path = &config.server.host_key_path;
11921145
let key = if key_path.exists() {
1193-
eprintln!("[SSH-PROXY] run_server: Loading host key from {:?}", key_path);
1194-
std::io::stderr().flush().ok();
1146+
info!("Loading host key from {:?}", key_path);
11951147
russh::keys::PrivateKey::read_openssh_file(key_path)
11961148
.context(format!("Failed to load host key from {:?}", key_path))?
11971149
} else {
1198-
eprintln!("[SSH-PROXY] run_server: Generating new host key (path {:?} does not exist)", key_path);
1199-
std::io::stderr().flush().ok();
1150+
info!(
1151+
"Generating new host key (path {:?} does not exist)",
1152+
key_path
1153+
);
12001154
let key = russh::keys::PrivateKey::random(
12011155
&mut rand::thread_rng(),
12021156
russh::keys::Algorithm::Ed25519,
@@ -1208,8 +1162,7 @@ pub async fn run_server(config: Config) -> Result<()> {
12081162
}
12091163
key.write_openssh_file(key_path, russh::keys::ssh_key::LineEnding::LF)
12101164
.context(format!("Failed to save host key to {:?}", key_path))?;
1211-
eprintln!("[SSH-PROXY] run_server: Saved host key to {:?}", key_path);
1212-
std::io::stderr().flush().ok();
1165+
info!("Saved host key to {:?}", key_path);
12131166
key
12141167
};
12151168

@@ -1222,14 +1175,10 @@ pub async fn run_server(config: Config) -> Result<()> {
12221175
};
12231176

12241177
let addr: std::net::SocketAddr = config.server.listen_addr.parse()?;
1225-
eprintln!("[SSH-PROXY] run_server: Starting SSH server on {}...", addr);
1226-
std::io::stderr().flush().ok();
12271178
info!("Starting SSH server on {}", addr);
12281179

12291180
server.run_on_address(Arc::new(russh_config), addr).await?;
12301181

1231-
eprintln!("[SSH-PROXY] run_server: Server terminated");
1232-
std::io::stderr().flush().ok();
1233-
1182+
info!("SSH server terminated");
12341183
Ok(())
12351184
}

0 commit comments

Comments
 (0)