11use std:: {
22 fs:: Permissions ,
33 os:: unix:: prelude:: PermissionsExt ,
4- path:: { Path , PathBuf } ,
4+ path:: { Component , Path , PathBuf } ,
55} ;
66
77use openssl:: sha:: Sha256 ;
88use serde:: { de:: IntoDeserializer , Deserialize } ;
9- use snafu:: { ResultExt , Snafu } ;
9+ use snafu:: { ensure , ResultExt , Snafu } ;
1010use stackable_operator:: {
1111 builder:: meta:: ObjectMetaBuilder ,
1212 k8s_openapi:: api:: core:: v1:: Pod ,
@@ -23,9 +23,15 @@ use tonic::{Request, Response, Status};
2323use super :: controller:: TOPOLOGY_NODE ;
2424use crate :: {
2525 backend:: {
26- self , pod_info, pod_info:: PodInfo , SecretBackendError , SecretContents , SecretVolumeSelector ,
26+ self ,
27+ pod_info:: { self , PodInfo } ,
28+ SecretBackendError , SecretContents , SecretVolumeSelector ,
29+ } ,
30+ format:: {
31+ self ,
32+ well_known:: { CompatibilityOptions , NamingOptions } ,
33+ SecretFormat ,
2734 } ,
28- format:: { self , well_known:: CompatibilityOptions , SecretFormat } ,
2935 grpc:: csi:: v1:: {
3036 node_server:: Node , NodeExpandVolumeRequest , NodeExpandVolumeResponse ,
3137 NodeGetCapabilitiesRequest , NodeGetCapabilitiesResponse , NodeGetInfoRequest ,
@@ -59,13 +65,13 @@ enum PublishError {
5965 #[ snafu( display( "backend failed to get secret data" ) ) ]
6066 BackendGetSecretData { source : backend:: dynamic:: DynError } ,
6167
62- #[ snafu( display( "failed to create secret parent dir {}" , path . display ( ) ) ) ]
68+ #[ snafu( display( "failed to create secret parent dir {path:?}" ) ) ]
6369 CreateDir {
6470 source : std:: io:: Error ,
6571 path : PathBuf ,
6672 } ,
6773
68- #[ snafu( display( "failed to mount volume mount directory {}" , path . display ( ) ) ) ]
74+ #[ snafu( display( "failed to mount volume mount directory {path:?}" ) ) ]
6975 Mount {
7076 source : std:: io:: Error ,
7177 path : PathBuf ,
@@ -74,24 +80,30 @@ enum PublishError {
7480 #[ snafu( display( "failed to convert secret data into desired format" ) ) ]
7581 FormatData { source : format:: IntoFilesError } ,
7682
77- #[ snafu( display( "failed to set volume permissions for {}" , path . display ( ) ) ) ]
83+ #[ snafu( display( "failed to set volume permissions for {path:?}" ) ) ]
7884 SetDirPermissions {
7985 source : std:: io:: Error ,
8086 path : PathBuf ,
8187 } ,
8288
83- #[ snafu( display( "failed to create secret file {}" , path . display ( ) ) ) ]
89+ #[ snafu( display( "failed to create secret file {path:?}" ) ) ]
8490 CreateFile {
8591 source : std:: io:: Error ,
8692 path : PathBuf ,
8793 } ,
8894
89- #[ snafu( display( "failed to write secret file {}" , path . display ( ) ) ) ]
95+ #[ snafu( display( "failed to write secret file {path:?}" ) ) ]
9096 WriteFile {
9197 source : std:: io:: Error ,
9298 path : PathBuf ,
9399 } ,
94100
101+ #[ snafu( display( "file path {path:?} must only contain normal components" ) ) ]
102+ InvalidComponents { path : PathBuf } ,
103+
104+ #[ snafu( display( "file path {path:?} must not be absolute" ) ) ]
105+ InvalidAbsolutePath { path : PathBuf } ,
106+
95107 #[ snafu( display( "failed to tag pod with expiry metadata" ) ) ]
96108 TagPod {
97109 source : stackable_operator:: client:: Error ,
@@ -120,6 +132,8 @@ impl From<PublishError> for Status {
120132 PublishError :: SetDirPermissions { .. } => Status :: unavailable ( full_msg) ,
121133 PublishError :: CreateFile { .. } => Status :: unavailable ( full_msg) ,
122134 PublishError :: WriteFile { .. } => Status :: unavailable ( full_msg) ,
135+ PublishError :: InvalidComponents { .. } => Status :: unavailable ( full_msg) ,
136+ PublishError :: InvalidAbsolutePath { .. } => Status :: unavailable ( full_msg) ,
123137 PublishError :: TagPod { .. } => Status :: unavailable ( full_msg) ,
124138 PublishError :: BuildAnnotation { .. } => Status :: unavailable ( full_msg) ,
125139 }
@@ -209,7 +223,8 @@ impl SecretProvisionerNode {
209223 target_path : & Path ,
210224 data : SecretContents ,
211225 format : Option < SecretFormat > ,
212- compat : & CompatibilityOptions ,
226+ names : NamingOptions ,
227+ compat : CompatibilityOptions ,
213228 ) -> Result < ( ) , PublishError > {
214229 let create_secret = {
215230 let mut opts = OpenOptions :: new ( ) ;
@@ -223,10 +238,37 @@ impl SecretProvisionerNode {
223238 } ;
224239 for ( k, v) in data
225240 . data
226- . into_files ( format, compat)
241+ . into_files ( format, names , compat)
227242 . context ( publish_error:: FormatDataSnafu ) ?
228243 {
229- let item_path = target_path. join ( k) ;
244+ // The following few lines of code do some basic checks against
245+ // unwanted path traversals. In the future, we want to leverage
246+ // capability based filesystem operations (openat) to prevent these
247+ // traversals.
248+
249+ // First, let's turn the (potentially custom) file path into a path.
250+ let file_path = PathBuf :: from ( k) ;
251+
252+ // Next, ensure the path is not absolute (does not contain root),
253+ // because joining an absolute path with a different path will
254+ // replace the exiting path entirely.
255+ ensure ! (
256+ !file_path. has_root( ) ,
257+ publish_error:: InvalidAbsolutePathSnafu { path: & file_path }
258+ ) ;
259+
260+ // Ensure that the file path only contains normal components. This
261+ // prevents any path traversals up the path using '..'.
262+ ensure ! (
263+ file_path
264+ . components( )
265+ . all( |c| matches!( c, Component :: Normal ( _) ) ) ,
266+ publish_error:: InvalidComponentsSnafu { path: & file_path }
267+ ) ;
268+
269+ // Now, we can join the base and file path
270+ let item_path = target_path. join ( file_path) ;
271+
230272 if let Some ( item_path_parent) = item_path. parent ( ) {
231273 create_dir_all ( item_path_parent)
232274 . await
@@ -383,10 +425,10 @@ impl Node for SecretProvisionerNode {
383425 self . save_secret_data (
384426 & target_path,
385427 data,
428+ // NOTE (@Techassi): At this point, we might want to pass the whole selector instead
386429 selector. format ,
387- & CompatibilityOptions {
388- tls_pkcs12_password : selector. compat_tls_pkcs12_password ,
389- } ,
430+ selector. names ,
431+ selector. compat ,
390432 )
391433 . await ?;
392434 Ok ( Response :: new ( NodePublishVolumeResponse { } ) )
0 commit comments