@@ -34,6 +34,7 @@ pub enum AdminBackend {
3434 generate_sam_account_name : Option < ActiveDirectorySamAccountNameRules > ,
3535 } ,
3636}
37+
3738#[ derive( Serialize , Deserialize , Debug ) ]
3839pub 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