Skip to content

Commit e91ed1f

Browse files
phuhung273yerzhan7
andauthored
Add proxy support (HTTPS_PROXY, NO_PROXY) via volume attributes (#663)
*Issue #, if available:* Relates #533, #535 *Description of changes:* Support injecting NO_PROXY, HTTPS_PROXY to mount-s3 process By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. --------- Signed-off-by: phuhung273 <phuhung.tranpham@gmail.com> Co-authored-by: Yerzhan Mazhkenov <20302932+yerzhan7@users.noreply.github.com>
1 parent 58e7d12 commit e91ed1f

14 files changed

Lines changed: 497 additions & 25 deletions

File tree

docs/CONFIGURATION.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ spec:
6262
mountpointContainerResourcesLimitsCpu: 500m
6363
mountpointContainerResourcesLimitsMemory: 1Gi
6464

65+
# ----- ENVIRONMENT VARIABLE CONFIGURATION -----
66+
# Optional: Pass extra environment variables to the mount-s3 process. Default: none.
67+
# Supported variables: HTTPS_PROXY, NO_PROXY
68+
mountpointEnv.HTTPS_PROXY: "http://proxy.example.com:3128"
69+
mountpointEnv.NO_PROXY: "sts.us-east-1.amazonaws.com"
70+
6571
---
6672
apiVersion: v1
6773
kind: PersistentVolumeClaim

examples/kubernetes/static_provisioning/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ This example shows how to make a static provisioned Mountpoint for S3 persistent
1010
- `caching.yaml` - shows how to configure mountpoint to use a cache directory. See the [Mountpoint documentation](https://github.com/awslabs/mountpoint-s3/blob/main/doc/CONFIGURATION.md#caching-configuration) for more details on caching options. Please thumbs up [#11](https://github.com/awslabs/mountpoint-s3-csi-driver/issues/141) or add details about your use case if you want improvements in this area.
1111
- `kms_sse.yaml` - demonstrates using SSE-KMS encryption with a customer supplied key id. See the [Mountpoint documentation](https://github.com/awslabs/mountpoint-s3/blob/main/doc/CONFIGURATION.md#data-encryption) for more details.
1212
- `aws_max_attempts.yaml` - configure the number of retries for requests to S3. This option is passed to Mountpoint as the `AWS_MAX_ATTEMPTS` environment variable. See the [Mountpoint configuration documentation](https://github.com/awslabs/mountpoint-s3/blob/main/doc/CONFIGURATION.md#other-s3-bucket-configuration) for more details.
13+
- `proxy.yaml` - configure HTTPS_PROXY, NO_PROXY environment variables for mountpoint behind proxy. See the [Mountpoint documentation](https://github.com/awslabs/mountpoint-s3-csi-driver/blob/main/docs/CONFIGURATION.md#static-provisioning) for more details.
1314
## Configure
1415
### Edit [Persistent Volume](https://github.com/awslabs/mountpoint-s3-csi-driver/blob/main/examples/kubernetes/static_provisioning/static_provisioning.yaml)
1516
> Note: This example assumes your S3 bucket has already been created. If you need to create a bucket, follow the [S3 documentation](https://docs.aws.amazon.com/AmazonS3/latest/userguide/creating-bucket.html) for a general purpose bucket or the [S3 Express One Zone documentation](https://docs.aws.amazon.com/AmazonS3/latest/userguide/directory-bucket-create.html) for a directory bucket.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
apiVersion: v1
2+
kind: PersistentVolume
3+
metadata:
4+
name: s3-pv
5+
spec:
6+
capacity:
7+
storage: 1Gi
8+
accessModes:
9+
- ReadWriteMany # Supported options: ReadWriteMany / ReadOnlyMany
10+
storageClassName: "" # Required for static provisioning
11+
claimRef: # To ensure no other PVCs can claim this PV
12+
namespace: default # Namespace is required even though it's in "default" namespace.
13+
name: s3-pvc # Name of your PVC
14+
mountOptions:
15+
- allow-delete
16+
- region us-east-1
17+
csi:
18+
driver: s3.csi.aws.com # Required
19+
volumeHandle: s3-csi-driver-volume # Must be unique
20+
volumeAttributes:
21+
bucketName: s3-csi-driver
22+
# Supported variables: HTTPS_PROXY, NO_PROXY
23+
mountpointEnv.HTTPS_PROXY: "http://proxy.default.svc.cluster.local:3128"
24+
mountpointEnv.NO_PROXY: "sts.us-east-1.amazonaws.com"
25+
---
26+
apiVersion: v1
27+
kind: PersistentVolumeClaim
28+
metadata:
29+
name: s3-pvc
30+
spec:
31+
accessModes:
32+
- ReadWriteMany # Supported options: ReadWriteMany / ReadOnlyMany
33+
storageClassName: "" # Required for static provisioning
34+
resources:
35+
requests:
36+
storage: 1Gi
37+
volumeName: s3-pv # Name of your PV
38+
---
39+
apiVersion: v1
40+
kind: Pod
41+
metadata:
42+
name: proxy
43+
labels:
44+
app: proxy
45+
spec:
46+
containers:
47+
- name: proxy
48+
image: ubuntu/squid:latest
49+
ports:
50+
- containerPort: 3128
51+
---
52+
apiVersion: v1
53+
kind: Service
54+
metadata:
55+
name: proxy
56+
spec:
57+
selector:
58+
app: proxy
59+
ports:
60+
- port: 3128
61+
---
62+
apiVersion: v1
63+
kind: Pod
64+
metadata:
65+
name: s3-app
66+
spec:
67+
containers:
68+
- name: app
69+
image: ubuntu
70+
command: ["/bin/sh"]
71+
args: ["-c", "echo 'Hello from the container!' >> /data/$(date -u).txt; tail -f /dev/null"]
72+
volumeMounts:
73+
- name: persistent-storage
74+
mountPath: /data
75+
volumes:
76+
- name: persistent-storage
77+
persistentVolumeClaim:
78+
claimName: s3-pvc

pkg/driver/node/envprovider/provider.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"maps"
77
"os"
88
"slices"
9+
"strings"
910
)
1011

1112
const (
@@ -25,8 +26,12 @@ const (
2526
EnvSecretAccessKey = "AWS_SECRET_ACCESS_KEY"
2627
EnvSessionToken = "AWS_SESSION_TOKEN"
2728
EnvMountpointCacheKey = "UNSTABLE_MOUNTPOINT_CACHE_KEY"
29+
EnvHTTPSProxy = "HTTPS_PROXY"
30+
EnvNoProxy = "NO_PROXY"
2831
)
2932

33+
const mountpointEnvPrefix = "mountpointEnv."
34+
3035
// Key represents an environment variable name.
3136
type Key = string
3237

@@ -44,6 +49,12 @@ var envAllowlist = []Key{
4449
EnvSTSRegionalEndpoints,
4550
}
4651

52+
// userEnvAllowlist is the list of environment variables allowed to be configured by user.
53+
var userEnvAllowlist = []Key{
54+
EnvHTTPSProxy,
55+
EnvNoProxy,
56+
}
57+
4758
// Region returns detected region from environment variables `AWS_REGION` or `AWS_DEFAULT_REGION`.
4859
// It returns an empty string if both is unset.
4960
func Region() Value {
@@ -66,6 +77,21 @@ func Default() Environment {
6677
return environment
6778
}
6879

80+
func ParseUserEnvFromVolumeContext(volumeCtx map[string]string) (Environment, error) {
81+
env := Environment{}
82+
for key, value := range volumeCtx {
83+
envName, ok := strings.CutPrefix(key, mountpointEnvPrefix)
84+
if !ok {
85+
continue
86+
}
87+
if !slices.Contains(userEnvAllowlist, envName) {
88+
return nil, fmt.Errorf("environment variable not allowed: %s (allowed: %v)", key, userEnvAllowlist)
89+
}
90+
env.Set(envName, value)
91+
}
92+
return env, nil
93+
}
94+
6995
// List returns a sorted slice of environment variables in "KEY=VALUE" format.
7096
func (env Environment) List() []string {
7197
list := []string{}

pkg/driver/node/envprovider/provider_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package envprovider_test
22

33
import (
4+
"strings"
45
"testing"
56

67
"github.com/awslabs/mountpoint-s3-csi-driver/pkg/driver/node/envprovider"
@@ -269,3 +270,55 @@ func TestMergingEnvironments(t *testing.T) {
269270
})
270271
}
271272
}
273+
274+
func TestParseUserEnvFromVolumeContext(t *testing.T) {
275+
testCases := []struct {
276+
name string
277+
volumeCtx map[string]string
278+
want envprovider.Environment
279+
errMsg string
280+
}{
281+
{
282+
name: "correct",
283+
volumeCtx: map[string]string{
284+
"mountpointEnv.HTTPS_PROXY": "proxy:3128",
285+
"mountpointEnv.NO_PROXY": "169.254.169.254",
286+
},
287+
want: envprovider.Environment{
288+
"HTTPS_PROXY": "proxy:3128",
289+
"NO_PROXY": "169.254.169.254",
290+
},
291+
},
292+
{
293+
name: "correct, skipping invalid prefix",
294+
volumeCtx: map[string]string{
295+
"mountpointEnv.HTTPS_PROXY": "proxy:3128",
296+
"mountpointEnvvvv.HTTPS_PROXY": "invalidPrefix",
297+
},
298+
want: envprovider.Environment{
299+
"HTTPS_PROXY": "proxy:3128",
300+
},
301+
},
302+
{
303+
name: "failed, environment not allowed",
304+
volumeCtx: map[string]string{
305+
"mountpointEnv.FOO": "BAR",
306+
},
307+
errMsg: "environment variable not allowed: mountpointEnv.FOO",
308+
},
309+
}
310+
311+
for _, testCase := range testCases {
312+
t.Run(testCase.name, func(t *testing.T) {
313+
userEnv, err := envprovider.ParseUserEnvFromVolumeContext(testCase.volumeCtx)
314+
if testCase.errMsg != "" {
315+
if !strings.Contains(err.Error(), testCase.errMsg) {
316+
t.Errorf("Expected error message %q, but got %q", testCase.errMsg, err.Error())
317+
}
318+
} else {
319+
assert.NoError(t, err)
320+
assert.Equals(t, testCase.want, userEnv)
321+
}
322+
})
323+
}
324+
}

pkg/driver/node/mounter/fake_mounter.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import (
44
"context"
55

66
"github.com/awslabs/mountpoint-s3-csi-driver/pkg/driver/node/credentialprovider"
7+
"github.com/awslabs/mountpoint-s3-csi-driver/pkg/driver/node/envprovider"
78
"github.com/awslabs/mountpoint-s3-csi-driver/pkg/mountpoint"
89
)
910

1011
type FakeMounter struct{}
1112

1213
func (m *FakeMounter) Mount(ctx context.Context, bucketName string, target string,
13-
credentialCtx credentialprovider.ProvideContext, args mountpoint.Args, fsGroup string) error {
14+
credentialCtx credentialprovider.ProvideContext, args mountpoint.Args, fsGroup string, userEnv envprovider.Environment) error {
1415
return nil
1516
}
1617

pkg/driver/node/mounter/mocks/mock_mount.go

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/driver/node/mounter/mounter.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import (
66
"path/filepath"
77

88
"github.com/awslabs/mountpoint-s3-csi-driver/pkg/driver/node/credentialprovider"
9+
"github.com/awslabs/mountpoint-s3-csi-driver/pkg/driver/node/envprovider"
910
"github.com/awslabs/mountpoint-s3-csi-driver/pkg/mountpoint"
1011
)
1112

1213
// Mounter is an interface for mount operations
1314
type Mounter interface {
14-
Mount(ctx context.Context, bucketName string, target string, credentialCtx credentialprovider.ProvideContext, args mountpoint.Args, fsGroup string) error
15+
Mount(ctx context.Context, bucketName string, target string, credentialCtx credentialprovider.ProvideContext, args mountpoint.Args, fsGroup string, userEnv envprovider.Environment) error
1516
Unmount(ctx context.Context, target string, credentialCtx credentialprovider.CleanupContext) error
1617
IsMountPoint(target string) (bool, error)
1718
}

pkg/driver/node/mounter/pod_mounter.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func NewPodMounter(
102102
//
103103
// If Mountpoint is already mounted at `target`, it will return early at step 3 to ensure credentials are up-to-date.
104104
// If Mountpoint is already mounted at `source`, it will skip steps 4-7 and only perform bind mount to `target`.
105-
func (pm *PodMounter) Mount(ctx context.Context, bucketName string, target string, credentialCtx credentialprovider.ProvideContext, args mountpoint.Args, fsGroup string) error {
105+
func (pm *PodMounter) Mount(ctx context.Context, bucketName string, target string, credentialCtx credentialprovider.ProvideContext, args mountpoint.Args, fsGroup string, userEnv envprovider.Environment) error {
106106
volumeName, err := pm.volumeNameFromTargetPath(target)
107107
if err != nil {
108108
return fmt.Errorf("Failed to extract volume name from %q: %w", target, err)
@@ -163,7 +163,7 @@ func (pm *PodMounter) Mount(ctx context.Context, bucketName string, target strin
163163
}
164164

165165
if !isSourceMountPoint {
166-
err = pm.mountS3AtSource(ctx, source, pod, podPath, bucketName, credEnv, authenticationSource, args)
166+
err = pm.mountS3AtSource(ctx, source, pod, podPath, bucketName, credEnv, userEnv, authenticationSource, args)
167167
if err != nil {
168168
return fmt.Errorf("Failed to mount at source %q: %w. %s", source, err, pm.helpMessageForGettingMountpointLogs(pod))
169169
}
@@ -194,6 +194,7 @@ func (pm *PodMounter) Mount(ctx context.Context, bucketName string, target strin
194194
// - podPath: Base path for Pod-specific files
195195
// - bucketName: Name of the S3 bucket to mount
196196
// - credEnv: Environment variables related to AWS credentials
197+
// - userEnv: Environment variables provided by user
197198
// - authenticationSource: Authentication source from PV volume attribute
198199
// - args: Mountpoint arguments
199200
//
@@ -208,9 +209,13 @@ func (pm *PodMounter) Mount(ctx context.Context, bucketName string, target strin
208209
//
209210
// If any step fails, it ensures cleanup by unmounting the source path.
210211
func (pm *PodMounter) mountS3AtSource(ctx context.Context, source string, mpPod *corev1.Pod, podPath string,
211-
bucketName string, credEnv envprovider.Environment, authenticationSource credentialprovider.AuthenticationSource,
212+
bucketName string, credEnv envprovider.Environment, userEnv envprovider.Environment, authenticationSource credentialprovider.AuthenticationSource,
212213
args mountpoint.Args) error {
213-
env := envprovider.Default()
214+
215+
// Build environment with precedence (highest wins): credEnv > Default() > userEnv
216+
env := envprovider.Environment{}
217+
env.Merge(userEnv)
218+
env.Merge(envprovider.Default())
214219
env.Merge(credEnv)
215220

216221
// Move `--aws-max-attempts` to env if provided

0 commit comments

Comments
 (0)