Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add configuration for file permission #751

Merged
merged 26 commits into from
Jan 19, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b7b16ef
feat: implements outbound proxy support for arc extension (#695)
nilekhc Nov 1, 2021
a3b5b8b
Merge branch 'master' of https://github.com/Azure/secrets-store-csi-d…
nilekhc Nov 1, 2021
807929d
Merge branch 'master' of https://github.com/Azure/secrets-store-csi-d…
nilekhc Nov 2, 2021
0add8b0
Merge branch 'master' of https://github.com/Azure/secrets-store-csi-d…
nilekhc Nov 9, 2021
90a96de
Merge branch 'master' of https://github.com/Azure/secrets-store-csi-d…
nilekhc Jan 11, 2022
46ca80e
feat: adds support for secret file permission
nilekhc Jan 12, 2022
338e5d2
feat: adds support for secret file permission
nilekhc Jan 12, 2022
64e2cf3
chore: fixes typo
nilekhc Jan 13, 2022
9d496ed
fix: fixes validation logic and adds tests
nilekhc Jan 14, 2022
d2761c5
chore: gofmt
nilekhc Jan 14, 2022
e718e5b
chore: gofmt
nilekhc Jan 14, 2022
d635846
chore: fixes comment
nilekhc Jan 14, 2022
1973557
Merge branch 'master' of https://github.com/Azure/secrets-store-csi-d…
nilekhc Jan 14, 2022
4539643
Merge branch 'master' of https://github.com/nilekhc/secrets-store-csi…
nilekhc Jan 14, 2022
9a44b9f
chore: updates comment
nilekhc Jan 19, 2022
f58e7bb
chore: go fmt
nilekhc Jan 19, 2022
b4f1a89
test: adds e2e test for file permission
nilekhc Jan 19, 2022
2cde058
chore: updates import
nilekhc Jan 19, 2022
2e3b53e
test: updates var name
nilekhc Jan 19, 2022
324d701
test: fixes test
nilekhc Jan 19, 2022
78c1948
test: enables all tests
nilekhc Jan 19, 2022
08dbaf7
Merge branch 'master' of https://github.com/Azure/secrets-store-csi-d…
nilekhc Jan 19, 2022
f845e82
Merge branch 'master' of https://github.com/nilekhc/secrets-store-csi…
nilekhc Jan 19, 2022
0d0cf19
chore: runs test for KinD cluster
nilekhc Jan 19, 2022
4f75e7c
docs: updates docs with file permission
nilekhc Jan 19, 2022
e874a24
docs: updates docs
nilekhc Jan 19, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 37 additions & 4 deletions pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ type KeyVaultObject struct {
// The encoding of the object in KeyVault
// Supported encodings are Base64, Hex, Utf-8
ObjectEncoding string `json:"objectEncoding" yaml:"objectEncoding"`
// FilePermission is the file permissions
FilePermission string `json:"filePermission" yaml:"filePermission"`
}

// SecretFile holds content and metadata of a secret file
type SecretFile struct {
Content []byte
Path string
FileMode int32
}

// StringArray ...
Expand Down Expand Up @@ -175,7 +184,7 @@ func (mc *mountConfig) GetServicePrincipalToken(resource string) (*adal.ServiceP
}

// MountSecretsStoreObjectContent mounts content of the secrets store object to target path
func (p *Provider) MountSecretsStoreObjectContent(ctx context.Context, attrib map[string]string, secrets map[string]string, targetPath string, permission os.FileMode) (map[string][]byte, map[string]string, error) {
func (p *Provider) MountSecretsStoreObjectContent(ctx context.Context, attrib map[string]string, secrets map[string]string, targetPath string, defaultFilePermission os.FileMode) ([]SecretFile, map[string]string, error) {
keyvaultName := strings.TrimSpace(attrib["keyvaultName"])
cloudName := strings.TrimSpace(attrib["cloudName"])
usePodIdentityStr := strings.TrimSpace(attrib["usePodIdentity"])
Expand Down Expand Up @@ -257,7 +266,7 @@ func (p *Provider) MountSecretsStoreObjectContent(ctx context.Context, attrib ma
klog.V(5).InfoS("unmarshaled key vault objects", "keyVaultObjects", keyVaultObjects, "count", len(keyVaultObjects), "pod", klog.ObjectRef{Namespace: podNamespace, Name: podName})

if len(keyVaultObjects) == 0 {
return make(map[string][]byte), make(map[string]string), nil
return nil, make(map[string]string), nil
}

vaultURL, err := mc.getVaultURL()
Expand All @@ -273,7 +282,7 @@ func (p *Provider) MountSecretsStoreObjectContent(ctx context.Context, attrib ma
}

objectVersionMap := make(map[string]string)
files := make(map[string][]byte)
files := []SecretFile{}
for _, keyVaultObject := range keyVaultObjects {
klog.V(5).InfoS("fetching object from key vault", "objectName", keyVaultObject.ObjectName, "objectType", keyVaultObject.ObjectType, "keyvault", mc.keyvaultName, "pod", klog.ObjectRef{Namespace: podNamespace, Name: podName})
if err := validateObjectFormat(keyVaultObject.ObjectFormat, keyVaultObject.ObjectType); err != nil {
Expand All @@ -290,6 +299,11 @@ func (p *Provider) MountSecretsStoreObjectContent(ctx context.Context, attrib ma
return nil, nil, wrapObjectTypeError(err, keyVaultObject.ObjectType, keyVaultObject.ObjectName, keyVaultObject.ObjectVersion)
}

filePermission, err := validateFilePermission(keyVaultObject.FilePermission, defaultFilePermission)
if err != nil {
return nil, nil, err
}

// fetch the object from Key Vault
content, newObjectVersion, err := p.GetKeyVaultObjectContent(ctx, kvClient, keyVaultObject, *vaultURL)
if err != nil {
Expand All @@ -307,7 +321,11 @@ func (p *Provider) MountSecretsStoreObjectContent(ctx context.Context, attrib ma
}

// these files will be returned to the CSI driver as part of gRPC response
files[fileName] = objectContent
files = append(files, SecretFile{
Path: fileName,
Content: objectContent,
FileMode: filePermission,
})
klog.V(5).InfoS("added file to the gRPC response", "file", fileName, "pod", klog.ObjectRef{Namespace: podNamespace, Name: podName})
}

Expand Down Expand Up @@ -783,3 +801,18 @@ func fetchCertChains(data []byte) ([]byte, error) {
}
return pemData, nil
}

// validateFilePermission checks if the given file permission is correct octal number, returns decimal equivalent
nilekhc marked this conversation as resolved.
Show resolved Hide resolved
// and error if it's not valid.
func validateFilePermission(filePermission string, defaultFilePermission os.FileMode) (int32, error) {
if filePermission == "" {
return int32(defaultFilePermission), nil
}

permission, err := strconv.ParseInt(filePermission, 8, 32)
if err != nil {
return 0, fmt.Errorf("file permission must be a valid octal number. - %w", err)
nilekhc marked this conversation as resolved.
Show resolved Hide resolved
}

return int32(permission), nil
}
45 changes: 45 additions & 0 deletions pkg/provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -983,3 +983,48 @@ func TestGetObjectVersion(t *testing.T) {
actual := getObjectVersion(id)
assert.Equal(t, expectedVersion, actual)
}

func TestValidateFilePermisssion(t *testing.T) {
cases := []struct {
desc string
filePermission string
defaultFilePermission os.FileMode
isErrorExpected bool
}{
{
desc: "valid file permission",
filePermission: "0600",
defaultFilePermission: os.FileMode(0644),
isErrorExpected: false,
},
{
desc: "empty file permission",
filePermission: "",
defaultFilePermission: os.FileMode(0644),
isErrorExpected: false,
},
{
desc: "invalid file permission",
filePermission: "0900",
defaultFilePermission: os.FileMode(0644),
isErrorExpected: true,
},
{
desc: "invalid octal number",
filePermission: "900",
defaultFilePermission: os.FileMode(0644),
isErrorExpected: true,
},
}

for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
_, err := validateFilePermission(tc.filePermission, tc.defaultFilePermission)
if tc.isErrorExpected {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
14 changes: 7 additions & 7 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func New() *CSIDriverProviderServer {
// writes the contents to the pod mount and returns the object versions as part of MountResponse
func (s *CSIDriverProviderServer) Mount(ctx context.Context, req *v1alpha1.MountRequest) (*v1alpha1.MountResponse, error) {
var attrib, secret map[string]string
var filePermission os.FileMode
var defaultFilePermission os.FileMode
var err error

err = json.Unmarshal([]byte(req.GetAttributes()), &attrib)
Expand All @@ -47,13 +47,13 @@ func (s *CSIDriverProviderServer) Mount(ctx context.Context, req *v1alpha1.Mount
klog.ErrorS(err, "failed to unmarshal node publish secrets ref")
return &v1alpha1.MountResponse{}, fmt.Errorf("failed to unmarshal secrets, error: %w", err)
}
err = json.Unmarshal([]byte(req.GetPermission()), &filePermission)
err = json.Unmarshal([]byte(req.GetPermission()), &defaultFilePermission)
if err != nil {
klog.ErrorS(err, "failed to unmarshal file permission")
return &v1alpha1.MountResponse{}, fmt.Errorf("failed to unmarshal file permission, error: %w", err)
}

files, objectVersions, err := s.Provider.MountSecretsStoreObjectContent(ctx, attrib, secret, req.GetTargetPath(), filePermission)
files, objectVersions, err := s.Provider.MountSecretsStoreObjectContent(ctx, attrib, secret, req.GetTargetPath(), defaultFilePermission)
if err != nil {
klog.ErrorS(err, "failed to process mount request")
return &v1alpha1.MountResponse{}, fmt.Errorf("failed to mount objects, error: %w", err)
Expand All @@ -66,11 +66,11 @@ func (s *CSIDriverProviderServer) Mount(ctx context.Context, req *v1alpha1.Mount
f := []*v1alpha1.File{}
// CSI driver v0.0.21+ will write to the filesystem if the files are in the response.
// No files in the response translates to "not implemented" in the CSI driver.
for k, v := range files {
for _, file := range files {
f = append(f, &v1alpha1.File{
Path: k,
Contents: v,
Mode: int32(filePermission),
Path: file.Path,
Contents: file.Content,
Mode: file.FileMode,
})
}

Expand Down