only set the SSH key permissions on Windows once
@@ -9,12 +9,22 @@ use tokio::{fs, io::AsyncWriteExt, process::Command};
#[cfg(target_os = "windows")]
use std::{env, path::PathBuf};
+#[cfg(target_os = "windows")]
+use tokio::sync::{OnceCell, SetError};
+
#[cfg(target_os = "linux")]
use users::{get_user_by_name, os::unix::UserExt};
#[cfg(target_os = "linux")]
const ONEFUZZ_SERVICE_USER: &str = "onefuzz";
+// On Windows, removing permissions that have already been removed fails. As
+// such, this needs to happen once and only once. NOTE: SSH keys are added as
+// node commands, which are processed serially. As such, this should never get
+// called concurrently.
+#[cfg(target_os = "windows")]
+static SET_PERMISSION_ONCE: OnceCell<()> = OnceCell::const_new();
+
#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct SshKeyInfo {
pub public_key: Secret<String>,
@@ -43,6 +53,8 @@ pub async fn add_ssh_key(key_info: SshKeyInfo) -> Result<()> {
.await?;
}
+ match SET_PERMISSION_ONCE.set(()) {
+ Ok(_) => {
debug!("removing Authenticated Users permissions from administrators_authorized_keys");
let result = Command::new("icacls.exe")
.arg(&admin_auth_keys_path)
@@ -58,7 +70,7 @@ pub async fn add_ssh_key(key_info: SshKeyInfo) -> Result<()> {
.context("icalcs failed to run")?;
if !result.status.success() {
bail!(
- "set authorized_keys ({}) permissions failed: {:?}",
+ "removing 'NT AUTHORITY/Authenticated Users' permissions to '{}' failed: {:?}",
admin_auth_keys_path.display(),
result
);
@@ -78,7 +90,7 @@ pub async fn add_ssh_key(key_info: SshKeyInfo) -> Result<()> {
.context("icacls failed to run")?;
if !result.status.success() {
bail!(
- "set authorized_keys ({}) permissions failed: {:?}",
+ "removing permission inheretence to '{}' failed: {:?}",
admin_auth_keys_path.display(),
result
);
@@ -89,8 +101,8 @@ pub async fn add_ssh_key(key_info: SshKeyInfo) -> Result<()> {
.args(&["-ExecutionPolicy", "Unrestricted", "-Command"])
.arg(format!(
"Get-Acl \"{}\" | Set-Acl \"{}\"",
+ host_key_path.display(),
admin_auth_keys_path.display(),
- host_key_path.display()
))
.stdin(Stdio::null())
.stdout(Stdio::piped())
@@ -102,11 +114,19 @@ pub async fn add_ssh_key(key_info: SshKeyInfo) -> Result<()> {
.context("Powershell Get-ACL | Set-ACL failed to run")?;
if !result.status.success() {
bail!(
- "set authorized_keys ({}) permissions failed: {:?}",
+ "copying ACL from '{}' to '{}' permissions failed: {:?}",
+ host_key_path.display(),
admin_auth_keys_path.display(),
result
);
}
+ }
+ Err(SetError::InitializingError(())) => {
+ bail!("add_ssh_key must not be called concurrently");
+ }
+ // do nothing if already initialized
+ Err(SetError::AlreadyInitializedError(())) => {}
+ }
info!("ssh key written: {}", admin_auth_keys_path.display());
