Repository: linkerd/linkerd2
Pull request #12493 (2024-04-23 22:06:30)

feat(inject): support arbitrary proxy parameters from helm
New proxy HTTP2 server parameters can be configured from the environment. These environment variables take the form:

    LINKERD2_PROXY_[INBOUND|OUTBOUND]_[SERVER]_[HTTP2]_<PARAM>

We want to allow these parameters to be set, i.e., in a values.yaml file without having to document all of the possible parameters (especially those that primarily influence internals not exposed to the application).

To accomodate this, this commit introduces generic proxy environment templating and a test to verify the behavior.

Values like the following:

    proxy:
      inbound:
        scope:
          proto:
            blueberryPi: 3.14
      outbound:
        scope:
          proto:
            appleSauce: "valueA"

Manifest as a proxy environment including:

    LINKERD2_PROXY_INBOUND_SCOPE_PROTO_BLUEBERRY_PI="3.14"
    LINKERD2_PROXY_OUTBOUND_SCOPE_PROTO_APPLE_SAUCE=valueA

That is, Helm value keys are converted to SCREAMING_SNAKE_CASE environment variable names.


Patch:
diff --git a/.proxy-version b/.proxy-version
index a32bc41ad..34840d99d 100644
--- a/.proxy-version
+++ b/.proxy-version
@@ -1,1 +1,1 @@
-v2.227.1
+v2.228.0
diff --git a/charts/partials/templates/_proxy.tpl b/charts/partials/templates/_proxy.tpl
index 1db5c8779..103ebb61c 100644
--- a/charts/partials/templates/_proxy.tpl
+++ b/charts/partials/templates/_proxy.tpl
@@ -104,6 +104,17 @@ env:
   value: 10000ms
 - name: LINKERD2_PROXY_OUTBOUND_CONNECT_KEEPALIVE
   value: 10000ms
+{{- /* Configure inbound and outbound parameters, e.g. for HTTP/2 servers. */}}
+{{ range $proxyK, $proxyV := (dict "inbound" .Values.proxy.inbound "outbound" .Values.proxy.outbound) -}}
+{{   range $scopeK, $scopeV := $proxyV -}}
+{{     range $protoK, $protoV := $scopeV -}}
+{{       range $paramK, $paramV := $protoV -}}
+- name: LINKERD2_PROXY_{{snakecase $proxyK | upper}}_{{snakecase $scopeK | upper}}_{{snakecase $protoK | upper}}_{{snakecase $paramK | upper}}
+  value: {{ quote $paramV }}
+{{       end -}}
+{{     end -}}
+{{   end -}}
+{{ end -}}
 {{ if .Values.proxy.opaquePorts -}}
 - name: LINKERD2_PROXY_INBOUND_PORTS_DISABLE_PROTOCOL_DETECTION
   value: {{.Values.proxy.opaquePorts | quote}}
diff --git a/pkg/charts/linkerd2/values.go b/pkg/charts/linkerd2/values.go
index f1fa43ac6..b43f029aa 100644
--- a/pkg/charts/linkerd2/values.go
+++ b/pkg/charts/linkerd2/values.go
@@ -139,8 +139,15 @@ type (
 
 		AdditionalEnv   []corev1.EnvVar `json:"additionalEnv"`
 		ExperimentalEnv []corev1.EnvVar `json:"experimentalEnv"`
+
+		Inbound  ProxyParams `json:"inbound,omitempty"`
+		Outbound ProxyParams `json:"outbound,omitempty"`
 	}
 
+	ProxyParams      = map[string]ProxyScopeParams
+	ProxyScopeParams = map[string]ProxyProtoParams
+	ProxyProtoParams = map[string]interface{}
+
 	ProxyControl struct {
 		Streams *ProxyControlStreams `json:"streams"`
 	}


Test patch:
diff --git a/cli/cmd/inject_test.go b/cli/cmd/inject_test.go
index 6ed75fd8e..910b9eed6 100644
--- a/cli/cmd/inject_test.go
+++ b/cli/cmd/inject_test.go
@@ -357,6 +357,32 @@ func TestUninjectAndInject(t *testing.T) {
 				return values
 			}(),
 		},
+		{
+			inputFileName:  "inject_emojivoto_deployment.input.yml",
+			goldenFileName: "inject_emojivoto_deployment_params.golden.yml",
+			reportFileName: "inject_emojivoto_deployment.report",
+			injectProxy:    true,
+			testInjectConfig: func() *linkerd2.Values {
+				values := defaultConfig()
+				values.Proxy.Inbound = linkerd2.ProxyParams{
+					"scope": linkerd2.ProxyScopeParams{
+						"proto": linkerd2.ProxyProtoParams{
+							"appleSauce": "valueA",
+							"blueberry":  3.14,
+						},
+					},
+				}
+				values.Proxy.Outbound = linkerd2.ProxyParams{
+					"scope": linkerd2.ProxyScopeParams{
+						"proto": linkerd2.ProxyProtoParams{
+							"applesauce": "valueA",
+							"blueBerry":  true,
+						},
+					},
+				}
+				return values
+			}(),
+		},
 	}
 
 	for i, tc := range testCases {
@@ -382,6 +408,8 @@ type injectCmd struct {
 }
 
 func testInjectCmd(t *testing.T, tc injectCmd) {
+	t.Helper()
+
 	testConfig := tc.values
 	if testConfig == nil {
 		var err error
diff --git a/cli/cmd/testdata/inject_emojivoto_deployment_params.golden.yml b/cli/cmd/testdata/inject_emojivoto_deployment_params.golden.yml
new file mode 100644
index 000000000..26501b96a
--- /dev/null
+++ b/cli/cmd/testdata/inject_emojivoto_deployment_params.golden.yml
@@ -0,0 +1,242 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: web
+  namespace: emojivoto
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: web-svc
+  template:
+    metadata:
+      annotations:
+        linkerd.io/created-by: linkerd/cli dev-undefined
+        linkerd.io/proxy-version: test-inject-proxy-version
+        linkerd.io/trust-root-sha256: 8dc603abd4e755c25c94da05abbf29b9b283a784733651020d72f97ca8ab98e4
+      labels:
+        app: web-svc
+        linkerd.io/control-plane-ns: linkerd
+        linkerd.io/proxy-deployment: web
+        linkerd.io/workload-ns: emojivoto
+    spec:
+      containers:
+      - env:
+        - name: _pod_name
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.name
+        - name: _pod_ns
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.namespace
+        - name: _pod_nodeName
+          valueFrom:
+            fieldRef:
+              fieldPath: spec.nodeName
+        - name: LINKERD2_PROXY_LOG
+          value: warn,linkerd=info,trust_dns=error
+        - name: LINKERD2_PROXY_LOG_FORMAT
+          value: plain
+        - name: LINKERD2_PROXY_DESTINATION_SVC_ADDR
+          value: linkerd-dst-headless.linkerd.svc.cluster.local.:8086
+        - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
+          value: 10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16,fd00::/8
+        - name: LINKERD2_PROXY_POLICY_SVC_ADDR
+          value: linkerd-policy.linkerd.svc.cluster.local.:8090
+        - name: LINKERD2_PROXY_POLICY_WORKLOAD
+          value: |
+            {"ns":"$(_pod_ns)", "pod":"$(_pod_name)"}
+        - name: LINKERD2_PROXY_INBOUND_DEFAULT_POLICY
+          value: all-unauthenticated
+        - name: LINKERD2_PROXY_POLICY_CLUSTER_NETWORKS
+          value: 10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16,fd00::/8
+        - name: LINKERD2_PROXY_CONTROL_STREAM_INITIAL_TIMEOUT
+          value: 3s
+        - name: LINKERD2_PROXY_CONTROL_STREAM_IDLE_TIMEOUT
+          value: 5m
+        - name: LINKERD2_PROXY_CONTROL_STREAM_LIFETIME
+          value: 1h
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
+        - name: LINKERD2_PROXY_OUTBOUND_DISCOVERY_IDLE_TIMEOUT
+          value: 5s
+        - name: LINKERD2_PROXY_INBOUND_DISCOVERY_IDLE_TIMEOUT
+          value: 90s
+        - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
+          value: 0.0.0.0:4190
+        - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
+          value: 0.0.0.0:4191
+        - name: LINKERD2_PROXY_OUTBOUND_LISTEN_ADDR
+          value: 127.0.0.1:4140
+        - name: LINKERD2_PROXY_INBOUND_LISTEN_ADDR
+          value: 0.0.0.0:4143
+        - name: LINKERD2_PROXY_INBOUND_IPS
+          valueFrom:
+            fieldRef:
+              fieldPath: status.podIPs
+        - name: LINKERD2_PROXY_INBOUND_PORTS
+          value: "80"
+        - name: LINKERD2_PROXY_DESTINATION_PROFILE_SUFFIXES
+          value: svc.cluster.local.
+        - name: LINKERD2_PROXY_INBOUND_ACCEPT_KEEPALIVE
+          value: 10000ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_KEEPALIVE
+          value: 10000ms
+        - name: LINKERD2_PROXY_INBOUND_SCOPE_PROTO_APPLE_SAUCE
+          value: valueA
+        - name: LINKERD2_PROXY_INBOUND_SCOPE_PROTO_BLUEBERRY
+          value: "3.14"
+        - name: LINKERD2_PROXY_OUTBOUND_SCOPE_PROTO_APPLESAUCE
+          value: valueA
+        - name: LINKERD2_PROXY_OUTBOUND_SCOPE_PROTO_BLUE_BERRY
+          value: "true"
+        - name: LINKERD2_PROXY_INBOUND_PORTS_DISABLE_PROTOCOL_DETECTION
+          value: 25,587,3306,4444,5432,6379,9300,11211
+        - name: LINKERD2_PROXY_DESTINATION_CONTEXT
+          value: |
+            {"ns":"$(_pod_ns)", "nodeName":"$(_pod_nodeName)", "pod":"$(_pod_name)"}
+        - name: _pod_sa
+          valueFrom:
+            fieldRef:
+              fieldPath: spec.serviceAccountName
+        - name: _l5d_ns
+          value: linkerd
+        - name: _l5d_trustdomain
+          value: cluster.local
+        - name: LINKERD2_PROXY_IDENTITY_DIR
+          value: /var/run/linkerd/identity/end-entity
+        - name: LINKERD2_PROXY_IDENTITY_TRUST_ANCHORS
+          value: |
+            -----BEGIN CERTIFICATE-----
+            MIIBwTCCAWagAwIBAgIQeDZp5lDaIygQ5UfMKZrFATAKBggqhkjOPQQDAjApMScw
+            JQYDVQQDEx5pZGVudGl0eS5saW5rZXJkLmNsdXN0ZXIubG9jYWwwHhcNMjAwODI4
+            MDcxMjQ3WhcNMzAwODI2MDcxMjQ3WjApMScwJQYDVQQDEx5pZGVudGl0eS5saW5r
+            ZXJkLmNsdXN0ZXIubG9jYWwwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARqc70Z
+            l1vgw79rjB5uSITICUA6GyfvSFfcuIis7B/XFSkkwAHU5S/s1AAP+R0TX7HBWUC4
+            uaG4WWsiwJKNn7mgo3AwbjAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB
+            /wIBATAdBgNVHQ4EFgQU5YtjVVPfd7I7NLHsn2C26EByGV0wKQYDVR0RBCIwIIIe
+            aWRlbnRpdHkubGlua2VyZC5jbHVzdGVyLmxvY2FsMAoGCCqGSM49BAMCA0kAMEYC
+            IQCN7lBFLDDvjx6V0+XkjpKERRsJYf5adMvnloFl48ilJgIhANtxhndcr+QJPuC8
+            vgUC0d2/9FMueIVMb+46WTCOjsqr
+            -----END CERTIFICATE-----
+        - name: LINKERD2_PROXY_IDENTITY_TOKEN_FILE
+          value: /var/run/secrets/tokens/linkerd-identity-token
+        - name: LINKERD2_PROXY_IDENTITY_SVC_ADDR
+          value: linkerd-identity-headless.linkerd.svc.cluster.local.:8080
+        - name: LINKERD2_PROXY_IDENTITY_LOCAL_NAME
+          value: $(_pod_sa).$(_pod_ns).serviceaccount.identity.linkerd.cluster.local
+        - name: LINKERD2_PROXY_IDENTITY_SVC_NAME
+          value: linkerd-identity.linkerd.serviceaccount.identity.linkerd.cluster.local
+        - name: LINKERD2_PROXY_DESTINATION_SVC_NAME
+          value: linkerd-destination.linkerd.serviceaccount.identity.linkerd.cluster.local
+        - name: LINKERD2_PROXY_POLICY_SVC_NAME
+          value: linkerd-destination.linkerd.serviceaccount.identity.linkerd.cluster.local
+        image: cr.l5d.io/linkerd/proxy:test-inject-proxy-version
+        imagePullPolicy: IfNotPresent
+        lifecycle:
+          postStart:
+            exec:
+              command:
+              - /usr/lib/linkerd/linkerd-await
+              - --timeout=2m
+              - --port=4191
+        livenessProbe:
+          httpGet:
+            path: /live
+            port: 4191
+          initialDelaySeconds: 10
+          timeoutSeconds: 1
+        name: linkerd-proxy
+        ports:
+        - containerPort: 4143
+          name: linkerd-proxy
+        - containerPort: 4191
+          name: linkerd-admin
+        readinessProbe:
+          httpGet:
+            path: /ready
+            port: 4191
+          initialDelaySeconds: 2
+          timeoutSeconds: 1
+        securityContext:
+          allowPrivilegeEscalation: false
+          readOnlyRootFilesystem: true
+          runAsNonRoot: true
+          runAsUser: 2102
+          seccompProfile:
+            type: RuntimeDefault
+        terminationMessagePolicy: FallbackToLogsOnError
+        volumeMounts:
+        - mountPath: /var/run/linkerd/identity/end-entity
+          name: linkerd-identity-end-entity
+        - mountPath: /var/run/secrets/tokens
+          name: linkerd-identity-token
+      - env:
+        - name: WEB_PORT
+          value: "80"
+        - name: EMOJISVC_HOST
+          value: emoji-svc.emojivoto:8080
+        - name: VOTINGSVC_HOST
+          value: voting-svc.emojivoto:8080
+        - name: INDEX_BUNDLE
+          value: dist/index_bundle.js
+        image: buoyantio/emojivoto-web:v10
+        name: web-svc
+        ports:
+        - containerPort: 80
+          name: http
+      initContainers:
+      - args:
+        - --incoming-proxy-port
+        - "4143"
+        - --outgoing-proxy-port
+        - "4140"
+        - --proxy-uid
+        - "2102"
+        - --inbound-ports-to-ignore
+        - 4190,4191,4567,4568
+        - --outbound-ports-to-ignore
+        - 4567,4568
+        image: cr.l5d.io/linkerd/proxy-init:v2.4.0
+        imagePullPolicy: IfNotPresent
+        name: linkerd-init
+        resources:
+          limits:
+            cpu: 100m
+            memory: 20Mi
+          requests:
+            cpu: 100m
+            memory: 20Mi
+        securityContext:
+          allowPrivilegeEscalation: false
+          capabilities:
+            add:
+            - NET_ADMIN
+            - NET_RAW
+          privileged: false
+          readOnlyRootFilesystem: true
+          runAsNonRoot: true
+          runAsUser: 65534
+          seccompProfile:
+            type: RuntimeDefault
+        terminationMessagePolicy: FallbackToLogsOnError
+        volumeMounts:
+        - mountPath: /run
+          name: linkerd-proxy-init-xtables-lock
+      volumes:
+      - emptyDir: {}
+        name: linkerd-proxy-init-xtables-lock
+      - emptyDir:
+          medium: Memory
+        name: linkerd-identity-end-entity
+      - name: linkerd-identity-token
+        projected:
+          sources:
+          - serviceAccountToken:
+              audience: identity.l5d.io
+              expirationSeconds: 86400
+              path: linkerd-identity-token
+---
diff --git a/test/integration/install/install_test.go b/test/integration/install/install_test.go
index fbeabcaab..6a9b18216 100644
--- a/test/integration/install/install_test.go
+++ b/test/integration/install/install_test.go
@@ -167,9 +167,9 @@ func TestInstallOrUpgradeCli(t *testing.T) {
 		out, err = TestHelper.KubectlApplyWithArgs(out, []string{
 			"--prune",
 			"-l", "linkerd.io/control-plane-ns=linkerd",
-			"--prune-whitelist", "rbac.authorization.k8s.io/v1/clusterrole",
-			"--prune-whitelist", "rbac.authorization.k8s.io/v1/clusterrolebinding",
-			"--prune-whitelist", "apiregistration.k8s.io/v1/apiservice",
+			"--prune-allowlist", "rbac.authorization.k8s.io/v1/clusterrole",
+			"--prune-allowlist", "rbac.authorization.k8s.io/v1/clusterrolebinding",
+			"--prune-allowlist", "apiregistration.k8s.io/v1/apiservice",
 		}...)
 		if err != nil {
 			testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
@@ -255,9 +255,9 @@ func TestInstallOrUpgradeCli(t *testing.T) {
 	cmdOut, err := TestHelper.KubectlApplyWithArgs(out, []string{
 		"--prune",
 		"-l", "linkerd.io/control-plane-ns=linkerd",
-		"--prune-whitelist", "apps/v1/deployment",
-		"--prune-whitelist", "core/v1/service",
-		"--prune-whitelist", "core/v1/configmap",
+		"--prune-allowlist", "apps/v1/deployment",
+		"--prune-allowlist", "core/v1/service",
+		"--prune-allowlist", "core/v1/configmap",
 	}...)
 	if err != nil {
 		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
diff --git a/test/integration/upgrade-edge/upgrade_edge_test.go b/test/integration/upgrade-edge/upgrade_edge_test.go
index ec300d43e..699f20792 100644
--- a/test/integration/upgrade-edge/upgrade_edge_test.go
+++ b/test/integration/upgrade-edge/upgrade_edge_test.go
@@ -213,9 +213,9 @@ func TestUpgradeCli(t *testing.T) {
 	cmdOut, err = TestHelper.KubectlApplyWithArgs(out, []string{
 		"--prune",
 		"-l", "linkerd.io/control-plane-ns=linkerd",
-		"--prune-whitelist", "apps/v1/deployment",
-		"--prune-whitelist", "core/v1/service",
-		"--prune-whitelist", "core/v1/configmap",
+		"--prune-allowlist", "apps/v1/deployment",
+		"--prune-allowlist", "core/v1/service",
+		"--prune-allowlist", "core/v1/configmap",
 	}...)
 	if err != nil {
 		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
diff --git a/test/integration/upgrade-stable/upgrade_stable_test.go b/test/integration/upgrade-stable/upgrade_stable_test.go
index 923547c75..8833f2e48 100644
--- a/test/integration/upgrade-stable/upgrade_stable_test.go
+++ b/test/integration/upgrade-stable/upgrade_stable_test.go
@@ -226,9 +226,9 @@ func TestUpgradeCli(t *testing.T) {
 	cmdOut, err = TestHelper.KubectlApplyWithArgs(out, []string{
 		"--prune",
 		"-l", "linkerd.io/control-plane-ns=linkerd",
-		"--prune-whitelist", "apps/v1/deployment",
-		"--prune-whitelist", "core/v1/service",
-		"--prune-whitelist", "core/v1/configmap",
+		"--prune-allowlist", "apps/v1/deployment",
+		"--prune-allowlist", "core/v1/service",
+		"--prune-allowlist", "core/v1/configmap",
 	}...)
 	if err != nil {
 		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",