Store the PKI bundle's client materials (ca.crt, tls.crt, tls.key) to the local filesystem so the CLI can use them for mTLS connections. Files are written atomically: temp dir -> validate -> rename over target. Directories are created with `0o700` and `tls.key` is set to `0o600`.
(name: &str, bundle: &PkiBundle)
| 13 | /// Files are written atomically: temp dir -> validate -> rename over target. |
| 14 | /// Directories are created with `0o700` and `tls.key` is set to `0o600`. |
| 15 | pub fn store_pki_bundle(name: &str, bundle: &PkiBundle) -> Result<()> { |
| 16 | let dir = cli_mtls_dir(name)?; |
| 17 | let temp_dir = cli_mtls_temp_dir(name)?; |
| 18 | let backup_dir = cli_mtls_backup_dir(name)?; |
| 19 | |
| 20 | if temp_dir.exists() { |
| 21 | std::fs::remove_dir_all(&temp_dir) |
| 22 | .into_diagnostic() |
| 23 | .map_err(|e| e.wrap_err(format!("failed to remove {}", temp_dir.display())))?; |
| 24 | } |
| 25 | |
| 26 | // Create the temp dir with restricted permissions so the private key |
| 27 | // is never world-readable, even momentarily. |
| 28 | create_dir_restricted(&temp_dir)?; |
| 29 | |
| 30 | std::fs::write(temp_dir.join("ca.crt"), &bundle.ca_cert_pem) |
| 31 | .into_diagnostic() |
| 32 | .map_err(|e| e.wrap_err("failed to write ca.crt"))?; |
| 33 | std::fs::write(temp_dir.join("tls.crt"), &bundle.client_cert_pem) |
| 34 | .into_diagnostic() |
| 35 | .map_err(|e| e.wrap_err("failed to write tls.crt"))?; |
| 36 | std::fs::write(temp_dir.join("tls.key"), &bundle.client_key_pem) |
| 37 | .into_diagnostic() |
| 38 | .map_err(|e| e.wrap_err("failed to write tls.key"))?; |
| 39 | |
| 40 | // Restrict the private key to owner-only. |
| 41 | set_file_owner_only(&temp_dir.join("tls.key"))?; |
| 42 | |
| 43 | validate_cli_mtls_bundle_dir(&temp_dir)?; |
| 44 | |
| 45 | let had_backup = if dir.exists() { |
| 46 | if backup_dir.exists() { |
| 47 | std::fs::remove_dir_all(&backup_dir) |
| 48 | .into_diagnostic() |
| 49 | .map_err(|e| e.wrap_err(format!("failed to remove {}", backup_dir.display())))?; |
| 50 | } |
| 51 | std::fs::rename(&dir, &backup_dir) |
| 52 | .into_diagnostic() |
| 53 | .map_err(|e| e.wrap_err(format!("failed to rename {}", dir.display())))?; |
| 54 | true |
| 55 | } else { |
| 56 | false |
| 57 | }; |
| 58 | |
| 59 | if let Err(err) = std::fs::rename(&temp_dir, &dir) |
| 60 | .into_diagnostic() |
| 61 | .map_err(|e| e.wrap_err(format!("failed to move {}", temp_dir.display()))) |
| 62 | { |
| 63 | if had_backup { |
| 64 | let _ = std::fs::rename(&backup_dir, &dir); |
| 65 | } |
| 66 | return Err(err); |
| 67 | } |
| 68 | |
| 69 | // Ensure the final directory also has restricted permissions after rename. |
| 70 | create_dir_restricted(&dir)?; |
| 71 | |
| 72 | if had_backup { |
no test coverage detected