Skip to content

Commit a414329

Browse files
Added comments based on my current understanding of the orchestration code for obtaining keytabs from AD (#602)
* Added comments based on my current understanding of the code. * rustfmt * added little detail * Replace unwrap with Snafu error type * Addressed comments * Removed unused import * Add suggestion from @sbernauer Co-authored-by: Sebastian Bernauer <[email protected]> * Added comment on possible error and wording in the expect message. * rustfmt --------- Co-authored-by: Sebastian Bernauer <[email protected]>
1 parent d2bafff commit a414329

File tree

1 file changed

+46
-5
lines changed
  • rust/krb5-provision-keytab/src

1 file changed

+46
-5
lines changed

rust/krb5-provision-keytab/src/lib.rs

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub enum AdminBackend {
3434
generate_sam_account_name: Option<ActiveDirectorySamAccountNameRules>,
3535
},
3636
}
37+
3738
#[derive(Serialize, Deserialize, Debug)]
3839
pub struct ActiveDirectorySamAccountNameRules {
3940
pub prefix: String,
@@ -71,25 +72,65 @@ pub async fn provision_keytab(krb5_config_path: &Path, req: &Request) -> Result<
7172
let req_str = serde_json::to_vec(&req).context(SerializeRequestSnafu)?;
7273

7374
let mut child = Command::new("stackable-krb5-provision-keytab")
75+
// make sure the process is killed if we error out of this fn somewhere due to
76+
// an error when writing to stdin or getting stdout
77+
// Usually we'd expect the process to terminate on its own, this is a fail safe to ensure
78+
// it gets killed in case it hangs for some reason.
7479
.kill_on_drop(true)
7580
.env("KRB5_CONFIG", krb5_config_path)
7681
// ldap3 uses the default client keytab to authenticate to the LDAP server
7782
.env("KRB5_CLIENT_KTNAME", &req.admin_keytab_path)
78-
// avoid leaking credentials between secret volumes/secretclasses
83+
// avoid leaking credentials between secret volumes/secretclasses by only storing the
84+
// TGT that is obtained for the operation in the memory of the short lives process
85+
// spawned by `Command::new` above - this way it'll be wiped from memory once this exits
86+
// With any shared or persistent ticket cache this might stick around and potentially be
87+
// reused by other volumes (which could cause privilege escalations and similar fun issues)
7988
.env("KRB5CCNAME", "MEMORY:")
8089
.stdin(Stdio::piped())
8190
.stdout(Stdio::piped())
8291
.spawn()
8392
.context(SpawnProvisionerSnafu)?;
84-
let mut stdin = child.stdin.take().unwrap();
93+
94+
// Get a `ChildStdin` object for the spawned process and write the serialized request
95+
// for a Principal into it in order for the child process to deserialize it and
96+
// process the request
97+
let mut stdin = child
98+
.stdin
99+
.take()
100+
// a failure here has some fundamental reason like us taking ownership of the pipe
101+
// earlier, which is most probably more a coding than a runtime error - this is
102+
// why this error is not handled here, but we panic instead
103+
.expect(
104+
"Failed to take ownership of stdin pipe of stackable-krb5-provision-keytab command! ",
105+
);
85106
stdin.write_all(&req_str).await.context(WriteRequestSnafu)?;
86107
stdin.flush().await.context(WriteRequestSnafu)?;
87108
drop(stdin);
109+
110+
// Wait for the process to finish and capture output
111+
// This will always return Ok(...) regardless of exit code or output of the child process
112+
// Failure here means that something went wrong with connecting to the process or obtaining
113+
// exit code or output
88114
let output = child
89115
.wait_with_output()
90116
.await
91117
.context(WaitProvisionerSnafu)?;
92-
serde_json::from_slice::<Result<Response, String>>(&output.stdout)
93-
.context(DeserializeResponseSnafu)?
94-
.map_err(|msg| Error::RunProvisioner { msg })
118+
119+
// Check if the spawned command returned 0 as return code
120+
if output.status.success() {
121+
// Check for success of the operation by deserializing stdout of the process to a `Response`
122+
// struct - since `Response` is an empty struct with no fields this effectively means that
123+
// any output will fail to deserialize and cause an `Error::RunProvisioner` to be propagated
124+
// with the output of the child process
125+
serde_json::from_slice::<Result<Response, String>>(&output.stdout)
126+
.context(DeserializeResponseSnafu)?
127+
.map_err(|msg| Error::RunProvisioner { msg })
128+
} else {
129+
Err(Error::RunProvisioner {
130+
msg: format!(
131+
"Got non zero return code from stackable-krb5-provision-keytab: [{:?}]",
132+
output.status.code()
133+
),
134+
})
135+
}
95136
}

0 commit comments

Comments
 (0)