1
1
use std:: {
2
2
fs:: Permissions ,
3
3
os:: unix:: prelude:: PermissionsExt ,
4
- path:: { Path , PathBuf } ,
4
+ path:: { Component , Path , PathBuf } ,
5
5
} ;
6
6
7
7
use openssl:: sha:: Sha256 ;
8
8
use serde:: { de:: IntoDeserializer , Deserialize } ;
9
- use snafu:: { ResultExt , Snafu } ;
9
+ use snafu:: { ensure , ResultExt , Snafu } ;
10
10
use stackable_operator:: {
11
11
builder:: meta:: ObjectMetaBuilder ,
12
12
k8s_openapi:: api:: core:: v1:: Pod ,
@@ -23,9 +23,15 @@ use tonic::{Request, Response, Status};
23
23
use super :: controller:: TOPOLOGY_NODE ;
24
24
use crate :: {
25
25
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 ,
27
34
} ,
28
- format:: { self , well_known:: CompatibilityOptions , SecretFormat } ,
29
35
grpc:: csi:: v1:: {
30
36
node_server:: Node , NodeExpandVolumeRequest , NodeExpandVolumeResponse ,
31
37
NodeGetCapabilitiesRequest , NodeGetCapabilitiesResponse , NodeGetInfoRequest ,
@@ -59,13 +65,13 @@ enum PublishError {
59
65
#[ snafu( display( "backend failed to get secret data" ) ) ]
60
66
BackendGetSecretData { source : backend:: dynamic:: DynError } ,
61
67
62
- #[ snafu( display( "failed to create secret parent dir {}" , path . display ( ) ) ) ]
68
+ #[ snafu( display( "failed to create secret parent dir {path:?}" ) ) ]
63
69
CreateDir {
64
70
source : std:: io:: Error ,
65
71
path : PathBuf ,
66
72
} ,
67
73
68
- #[ snafu( display( "failed to mount volume mount directory {}" , path . display ( ) ) ) ]
74
+ #[ snafu( display( "failed to mount volume mount directory {path:?}" ) ) ]
69
75
Mount {
70
76
source : std:: io:: Error ,
71
77
path : PathBuf ,
@@ -74,24 +80,30 @@ enum PublishError {
74
80
#[ snafu( display( "failed to convert secret data into desired format" ) ) ]
75
81
FormatData { source : format:: IntoFilesError } ,
76
82
77
- #[ snafu( display( "failed to set volume permissions for {}" , path . display ( ) ) ) ]
83
+ #[ snafu( display( "failed to set volume permissions for {path:?}" ) ) ]
78
84
SetDirPermissions {
79
85
source : std:: io:: Error ,
80
86
path : PathBuf ,
81
87
} ,
82
88
83
- #[ snafu( display( "failed to create secret file {}" , path . display ( ) ) ) ]
89
+ #[ snafu( display( "failed to create secret file {path:?}" ) ) ]
84
90
CreateFile {
85
91
source : std:: io:: Error ,
86
92
path : PathBuf ,
87
93
} ,
88
94
89
- #[ snafu( display( "failed to write secret file {}" , path . display ( ) ) ) ]
95
+ #[ snafu( display( "failed to write secret file {path:?}" ) ) ]
90
96
WriteFile {
91
97
source : std:: io:: Error ,
92
98
path : PathBuf ,
93
99
} ,
94
100
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
+
95
107
#[ snafu( display( "failed to tag pod with expiry metadata" ) ) ]
96
108
TagPod {
97
109
source : stackable_operator:: client:: Error ,
@@ -120,6 +132,8 @@ impl From<PublishError> for Status {
120
132
PublishError :: SetDirPermissions { .. } => Status :: unavailable ( full_msg) ,
121
133
PublishError :: CreateFile { .. } => Status :: unavailable ( full_msg) ,
122
134
PublishError :: WriteFile { .. } => Status :: unavailable ( full_msg) ,
135
+ PublishError :: InvalidComponents { .. } => Status :: unavailable ( full_msg) ,
136
+ PublishError :: InvalidAbsolutePath { .. } => Status :: unavailable ( full_msg) ,
123
137
PublishError :: TagPod { .. } => Status :: unavailable ( full_msg) ,
124
138
PublishError :: BuildAnnotation { .. } => Status :: unavailable ( full_msg) ,
125
139
}
@@ -209,7 +223,8 @@ impl SecretProvisionerNode {
209
223
target_path : & Path ,
210
224
data : SecretContents ,
211
225
format : Option < SecretFormat > ,
212
- compat : & CompatibilityOptions ,
226
+ names : NamingOptions ,
227
+ compat : CompatibilityOptions ,
213
228
) -> Result < ( ) , PublishError > {
214
229
let create_secret = {
215
230
let mut opts = OpenOptions :: new ( ) ;
@@ -223,10 +238,37 @@ impl SecretProvisionerNode {
223
238
} ;
224
239
for ( k, v) in data
225
240
. data
226
- . into_files ( format, compat)
241
+ . into_files ( format, names , compat)
227
242
. context ( publish_error:: FormatDataSnafu ) ?
228
243
{
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
+
230
272
if let Some ( item_path_parent) = item_path. parent ( ) {
231
273
create_dir_all ( item_path_parent)
232
274
. await
@@ -383,10 +425,10 @@ impl Node for SecretProvisionerNode {
383
425
self . save_secret_data (
384
426
& target_path,
385
427
data,
428
+ // NOTE (@Techassi): At this point, we might want to pass the whole selector instead
386
429
selector. format ,
387
- & CompatibilityOptions {
388
- tls_pkcs12_password : selector. compat_tls_pkcs12_password ,
389
- } ,
430
+ selector. names ,
431
+ selector. compat ,
390
432
)
391
433
. await ?;
392
434
Ok ( Response :: new ( NodePublishVolumeResponse { } ) )
0 commit comments