(
listen_path: &Path,
ca_file_paths: &Option<(PathBuf, PathBuf)>,
)
| 40 | ); |
| 41 | |
| 42 | fn ssh_server_init( |
| 43 | listen_path: &Path, |
| 44 | ca_file_paths: &Option<(PathBuf, PathBuf)>, |
| 45 | ) -> Result<SshServerInit> { |
| 46 | let mut rng = OsRng; |
| 47 | let host_key = PrivateKey::random(&mut rng, Algorithm::Ed25519).into_diagnostic()?; |
| 48 | |
| 49 | let mut config = russh::server::Config { |
| 50 | auth_rejection_time: Duration::from_secs(1), |
| 51 | ..Default::default() |
| 52 | }; |
| 53 | config.keys.push(host_key); |
| 54 | |
| 55 | let config = Arc::new(config); |
| 56 | let ca_paths = ca_file_paths.as_ref().map(|p| Arc::new(p.clone())); |
| 57 | |
| 58 | // Ensure the parent directory exists and is root-owned with 0700 |
| 59 | // permissions. The sandbox entrypoint runs as an unprivileged user; it |
| 60 | // must not be able to enter this directory and connect to the socket. |
| 61 | if let Some(parent) = listen_path.parent() { |
| 62 | std::fs::create_dir_all(parent).into_diagnostic()?; |
| 63 | #[cfg(unix)] |
| 64 | { |
| 65 | use std::os::unix::fs::PermissionsExt; |
| 66 | let perms = std::fs::Permissions::from_mode(0o700); |
| 67 | std::fs::set_permissions(parent, perms).into_diagnostic()?; |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | // Remove any stale socket from a previous run before binding. |
| 72 | if listen_path.exists() { |
| 73 | std::fs::remove_file(listen_path).into_diagnostic()?; |
| 74 | } |
| 75 | let listener = UnixListener::bind(listen_path).into_diagnostic()?; |
| 76 | |
| 77 | // Tighten permissions so only the supervisor (root) can connect. The |
| 78 | // sandbox entrypoint runs as an unprivileged user and must not be able to |
| 79 | // dial the SSH daemon directly — all access goes through the relay from |
| 80 | // the gateway. |
| 81 | #[cfg(unix)] |
| 82 | { |
| 83 | use std::os::unix::fs::PermissionsExt; |
| 84 | let perms = std::fs::Permissions::from_mode(0o600); |
| 85 | std::fs::set_permissions(listen_path, perms).into_diagnostic()?; |
| 86 | } |
| 87 | |
| 88 | ocsf_emit!( |
| 89 | SshActivityBuilder::new(openshell_ocsf::ctx::ctx()) |
| 90 | .activity(ActivityId::Listen) |
| 91 | .severity(SeverityId::Informational) |
| 92 | .status(StatusId::Success) |
| 93 | .message(format!("SSH server listening on {}", listen_path.display())) |
| 94 | .build() |
| 95 | ); |
| 96 | |
| 97 | Ok((listener, config, ca_paths)) |
| 98 | } |
| 99 |
no test coverage detected