Repository: linkerd/linkerd2
Pull request #5036 (2020-10-02 14:59:44)

Update Injection to use new linkerd-config.values
This PR Updates the Injection Logic (both CLI and proxy-injector)
to use `Values` struct instead of protobuf Config, part of our move
in removing the protobuf.

This does not touch any of the flags, install related code.

Signed-off-by: Tarun Pothulapati <tarunpothulapati@outlook.com>

<!--  Thanks for sending a pull request!

If you already have a well-structured git commit message, chances are GitHub
set the title and description of this PR to the git commit message subject and
body, respectively. If so, you may delete these instructions and submit your PR.

If this is your first time, please read our contributor guide:
https://github.com/linkerd/linkerd2/blob/main/CONTRIBUTING.md

The title and description of your Pull Request should match the git commit
subject and body, respectively. Git commit messages are structured as follows:

```
Subject

Problem

Solution

Validation

Fixes #[GitHub issue ID]

DCO Sign off
```

Example git commit message:

```
Introduce Pull Request Template

GitHub's community guidelines recommend a pull request template, the repo was
lacking one.

Introduce a `PULL_REQUEST_TEMPLATE.md` file.

Once merged, the
[Community profile checklist](https://github.com/linkerd/linkerd2/community)
should indicate the repo now provides a pull request template.

Fixes #3321

Signed-off-by: Jane Smith <jane.smith@example.com>
```

Note the git commit message subject becomes the pull request title.

For more details around git commits, see the section on Committing in our
contributor guide:
https://github.com/linkerd/linkerd2/blob/main/CONTRIBUTING.md#committing
-->


adleong: I've started to look into this integration test failure.  It seems its because a `linkerd tap` command fails to get any tap events from the `gateway` deployment in the `linkerd-tap-test` namespace.  Upon further inspection, the gateway's sidecar proxy has the `LINKERD2_PROXY_TAP_DISABLED` env var enabled, which explains why it is not emitting tap data.

Looking at the test application yaml, the annotation for disabling tap is not set for the gateway deployment but it IS set for the t4 deployment.  I suspect that when we run the injection code over this list of manifests, we have some bug where annotation overrides leak out and persist from one manifest to the next.  This could explain why the disable tap annotation from the t4 deployment seems to be affecting the gateway deployment.  
Pothulapati: @adleong Yep, That's right. It's because we were using the same `Values` passed to `ResourceConfig` for rendering, which is mutated based on annotations, etc on each file. I was able to fix this, by making a copy of `Values` and using that with `Patch`

I also retrieved the previous `trace()` logic to make Tracing work, as that logic added namespace, etc if not present for `collectorSvcAccount`
adleong: I've pushed a commit to this branch which removes the `injection` field from the patch template values.  I think this was only needed because of the previous bug that you fixed regarding re-using values.  Now that we're doing a deep copy, it doesn't seem to be necessary. 
adleong: Thanks, @alpeb!  I've just pushed a commit adding these validations back in.

Patch:
diff --git a/charts/patch/templates/patch.json b/charts/patch/templates/patch.json
index 710e0087b..6f997e64b 100644
--- a/charts/patch/templates/patch.json
+++ b/charts/patch/templates/patch.json
@@ -44,7 +44,7 @@
   },
   {{- end }}
   {{- end}}
-  {{- if .Values.global.proxyInit }}
+  {{- if and .Values.global.proxyInit (not .Values.global.cniEnabled) }}
   {{- if .Values.addRootInitContainers }}
   {
     "op": "add",
diff --git a/cli/cmd/check.go b/cli/cmd/check.go
index c4ce57bbb..799f61745 100644
--- a/cli/cmd/check.go
+++ b/cli/cmd/check.go
@@ -371,7 +371,7 @@ func renderInstallManifest(ctx context.Context) (string, error) {
 	if err != nil {
 		return "", err
 	}
-	values, _, err := options.validateAndBuild(ctx, "", nil)
+	values, err := options.validateAndBuild(ctx, "", nil)
 	if err != nil {
 		return "", err
 	}
diff --git a/cli/cmd/inject.go b/cli/cmd/inject.go
index 8dad2c909..3149f30b3 100644
--- a/cli/cmd/inject.go
+++ b/cli/cmd/inject.go
@@ -12,7 +12,7 @@ import (
 	"time"
 
 	jsonpatch "github.com/evanphx/json-patch"
-	cfg "github.com/linkerd/linkerd2/controller/gen/config"
+	"github.com/linkerd/linkerd2/pkg/charts/linkerd2"
 	"github.com/linkerd/linkerd2/pkg/healthcheck"
 	"github.com/linkerd/linkerd2/pkg/inject"
 	"github.com/linkerd/linkerd2/pkg/k8s"
@@ -36,7 +36,7 @@ const (
 type resourceTransformerInject struct {
 	allowNsInject       bool
 	injectProxy         bool
-	configs             *cfg.All
+	values              *linkerd2.Values
 	overrideAnnotations map[string]string
 	enableDebugSidecar  bool
 	closeWaitTimeout    time.Duration
@@ -80,17 +80,17 @@ sub-folders, or coming from stdin.`,
 				return err
 			}
 
-			configs, err := options.fetchConfigsOrDefault(cmd.Context())
+			values, err := options.fetchConfigsOrDefault(cmd.Context())
 			if err != nil {
 				return err
 			}
 			overrideAnnotations := map[string]string{}
-			options.overrideConfigs(configs, overrideAnnotations)
+			options.overrideConfigs(values, overrideAnnotations)
 
 			transformer := &resourceTransformerInject{
 				allowNsInject:       true,
 				injectProxy:         manualOption,
-				configs:             configs,
+				values:              values,
 				overrideAnnotations: overrideAnnotations,
 				enableDebugSidecar:  enableDebugSidecar,
 				closeWaitTimeout:    closeWaitTimeout,
@@ -149,14 +149,14 @@ sub-folders, or coming from stdin.`,
 
 func uninjectAndInject(inputs []io.Reader, errWriter, outWriter io.Writer, transformer *resourceTransformerInject) int {
 	var out bytes.Buffer
-	if exitCode := runUninjectSilentCmd(inputs, errWriter, &out, transformer.configs); exitCode != 0 {
+	if exitCode := runUninjectSilentCmd(inputs, errWriter, &out, transformer.values); exitCode != 0 {
 		return exitCode
 	}
 	return runInjectCmd([]io.Reader{&out}, errWriter, outWriter, transformer)
 }
 
 func (rt resourceTransformerInject) transform(bytes []byte) ([]byte, []inject.Report, error) {
-	conf := inject.NewResourceConfig(rt.configs, inject.OriginCLI)
+	conf := inject.NewResourceConfig(rt.values, inject.OriginCLI)
 
 	if rt.enableDebugSidecar {
 		conf.AppendPodAnnotation(k8s.ProxyEnableDebugAnnotation, "true")
@@ -336,18 +336,13 @@ func (resourceTransformerInject) generateReport(reports []inject.Report, output
 	output.Write([]byte("\n"))
 }
 
-func (options *proxyConfigOptions) fetchConfigsOrDefault(ctx context.Context) (*cfg.All, error) {
+func (options *proxyConfigOptions) fetchConfigsOrDefault(ctx context.Context) (*linkerd2.Values, error) {
 	if options.ignoreCluster {
 		if !options.disableIdentity {
 			return nil, errors.New("--disable-identity must be set with --ignore-cluster")
 		}
 
-		install, err := newInstallOptionsWithDefaults()
-		if err != nil {
-			return nil, err
-		}
-
-		return install.configs(nil), nil
+		return linkerd2.NewValues(false)
 	}
 
 	checkPublicAPIClientOrExit()
@@ -356,104 +351,82 @@ func (options *proxyConfigOptions) fetchConfigsOrDefault(ctx context.Context) (*
 		return nil, err
 	}
 
-	_, config, err := healthcheck.FetchLinkerdConfigMap(ctx, api, controlPlaneNamespace)
-	if err != nil {
-		return nil, err
-	}
-
-	return config, nil
+	// Get the New Linkerd Configuration
+	_, values, err := healthcheck.FetchCurrentConfiguration(ctx, api, controlPlaneNamespace)
+	return values, err
 }
 
 // overrideConfigs uses command-line overrides to update the provided configs.
 // the overrideAnnotations map keeps track of which configs are overridden, by
 // storing the corresponding annotations and values.
-func (options *proxyConfigOptions) overrideConfigs(configs *cfg.All, overrideAnnotations map[string]string) {
+func (options *proxyConfigOptions) overrideConfigs(values *linkerd2.Values, overrideAnnotations map[string]string) {
 	if options.proxyVersion != "" {
-		configs.Proxy.ProxyVersion = options.proxyVersion
 		overrideAnnotations[k8s.ProxyVersionOverrideAnnotation] = options.proxyVersion
 	}
 
 	if len(options.ignoreInboundPorts) > 0 {
-		configs.Proxy.IgnoreInboundPorts = inject.ToPortRanges(options.ignoreInboundPorts)
-		overrideAnnotations[k8s.ProxyIgnoreInboundPortsAnnotation] = parsePortRanges(configs.Proxy.IgnoreInboundPorts)
+		overrideAnnotations[k8s.ProxyIgnoreInboundPortsAnnotation] = strings.Join(options.ignoreInboundPorts, ",")
 	}
 	if len(options.ignoreOutboundPorts) > 0 {
-		configs.Proxy.IgnoreOutboundPorts = inject.ToPortRanges(options.ignoreOutboundPorts)
-		overrideAnnotations[k8s.ProxyIgnoreOutboundPortsAnnotation] = parsePortRanges(configs.Proxy.IgnoreOutboundPorts)
+		overrideAnnotations[k8s.ProxyIgnoreOutboundPortsAnnotation] = strings.Join(options.ignoreOutboundPorts, ",")
 	}
 
 	if options.proxyAdminPort != 0 {
-		configs.Proxy.AdminPort = toPort(options.proxyAdminPort)
-		overrideAnnotations[k8s.ProxyAdminPortAnnotation] = parsePort(configs.Proxy.AdminPort)
+		overrideAnnotations[k8s.ProxyAdminPortAnnotation] = fmt.Sprint(options.proxyAdminPort)
 	}
 	if options.proxyControlPort != 0 {
-		configs.Proxy.ControlPort = toPort(options.proxyControlPort)
-		overrideAnnotations[k8s.ProxyControlPortAnnotation] = parsePort(configs.Proxy.ControlPort)
+		overrideAnnotations[k8s.ProxyControlPortAnnotation] = fmt.Sprint(options.proxyControlPort)
 	}
 	if options.proxyInboundPort != 0 {
-		configs.Proxy.InboundPort = toPort(options.proxyInboundPort)
-		overrideAnnotations[k8s.ProxyInboundPortAnnotation] = parsePort(configs.Proxy.InboundPort)
+		overrideAnnotations[k8s.ProxyInboundPortAnnotation] = fmt.Sprint(options.proxyInboundPort)
 	}
 	if options.proxyOutboundPort != 0 {
-		configs.Proxy.OutboundPort = toPort(options.proxyOutboundPort)
-		overrideAnnotations[k8s.ProxyOutboundPortAnnotation] = parsePort(configs.Proxy.OutboundPort)
+		overrideAnnotations[k8s.ProxyOutboundPortAnnotation] = fmt.Sprint(options.proxyOutboundPort)
 	}
 
 	if options.dockerRegistry != "" {
-		debugImage := configs.GetProxy().GetDebugImage().GetImageName()
+		debugImage := values.DebugContainer.Image.Name
 		if debugImage == "" {
 			debugImage = k8s.DebugSidecarImage
 		}
-		overrideAnnotations[k8s.ProxyImageAnnotation] = overwriteRegistry(configs.GetProxy().GetProxyImage().GetImageName(), options.dockerRegistry)
-		overrideAnnotations[k8s.ProxyInitImageAnnotation] = overwriteRegistry(configs.GetProxy().GetProxyInitImage().GetImageName(), options.dockerRegistry)
+		overrideAnnotations[k8s.ProxyImageAnnotation] = overwriteRegistry(values.Global.Proxy.Image.Name, options.dockerRegistry)
+		overrideAnnotations[k8s.ProxyInitImageAnnotation] = overwriteRegistry(values.Global.ProxyInit.Image.Name, options.dockerRegistry)
 		overrideAnnotations[k8s.DebugImageAnnotation] = overwriteRegistry(debugImage, options.dockerRegistry)
 	}
 
 	if options.proxyImage != "" {
-		configs.Proxy.ProxyImage.ImageName = options.proxyImage
-		overrideAnnotations[k8s.ProxyImageAnnotation] = configs.Proxy.ProxyImage.ImageName
+		overrideAnnotations[k8s.ProxyImageAnnotation] = options.proxyImage
 	}
 
 	if options.initImage != "" {
-		configs.Proxy.ProxyInitImage.ImageName = options.initImage
-
-		overrideAnnotations[k8s.ProxyInitImageAnnotation] = configs.Proxy.ProxyInitImage.ImageName
+		overrideAnnotations[k8s.ProxyInitImageAnnotation] = options.initImage
 	}
 
 	if options.initImageVersion != "" {
-		configs.Proxy.ProxyInitImageVersion = options.initImageVersion
-		overrideAnnotations[k8s.ProxyInitImageVersionAnnotation] = configs.Proxy.ProxyInitImageVersion
+		overrideAnnotations[k8s.ProxyInitImageVersionAnnotation] = options.initImageVersion
 	}
 
 	if options.debugImageVersion != "" {
-		configs.Proxy.DebugImageVersion = options.debugImageVersion
 		overrideAnnotations[k8s.DebugImageVersionAnnotation] = options.debugImageVersion
 	}
 
 	if options.imagePullPolicy != "" {
-		configs.Proxy.ProxyImage.PullPolicy = options.imagePullPolicy
-		configs.Proxy.ProxyInitImage.PullPolicy = options.imagePullPolicy
-		configs.Proxy.DebugImage.PullPolicy = options.imagePullPolicy
 		overrideAnnotations[k8s.ProxyImagePullPolicyAnnotation] = options.imagePullPolicy
 	}
 
 	if options.proxyUID != 0 {
-		configs.Proxy.ProxyUid = options.proxyUID
 		overrideAnnotations[k8s.ProxyUIDAnnotation] = strconv.FormatInt(options.proxyUID, 10)
 	}
 
 	if options.proxyLogLevel != "" {
-		configs.Proxy.LogLevel = &cfg.LogLevel{Level: options.proxyLogLevel}
 		overrideAnnotations[k8s.ProxyLogLevelAnnotation] = options.proxyLogLevel
 	}
 
 	if options.proxyLogFormat != "" {
-		configs.Proxy.LogFormat = options.proxyLogFormat
 		overrideAnnotations[k8s.ProxyLogFormatAnnotation] = options.proxyLogFormat
 	}
 
 	if options.disableIdentity {
-		configs.Global.IdentityContext = nil
 		overrideAnnotations[k8s.ProxyDisableIdentityAnnotation] = strconv.FormatBool(true)
 	}
 
@@ -468,25 +441,20 @@ func (options *proxyConfigOptions) overrideConfigs(configs *cfg.All, overrideAnn
 	// keep track of this option because its true/false value results in different
 	// values being assigned to the LINKERD2_PROXY_DESTINATION_PROFILE_SUFFIXES
 	// env var. Its annotation is added only if its value is true.
-	configs.Proxy.DisableExternalProfiles = !options.enableExternalProfiles
 	if options.enableExternalProfiles {
 		overrideAnnotations[k8s.ProxyEnableExternalProfilesAnnotation] = strconv.FormatBool(true)
 	}
 
 	if options.proxyCPURequest != "" {
-		configs.Proxy.Resource.RequestCpu = options.proxyCPURequest
 		overrideAnnotations[k8s.ProxyCPURequestAnnotation] = options.proxyCPURequest
 	}
 	if options.proxyCPULimit != "" {
-		configs.Proxy.Resource.LimitCpu = options.proxyCPULimit
 		overrideAnnotations[k8s.ProxyCPULimitAnnotation] = options.proxyCPULimit
 	}
 	if options.proxyMemoryRequest != "" {
-		configs.Proxy.Resource.RequestMemory = options.proxyMemoryRequest
 		overrideAnnotations[k8s.ProxyMemoryRequestAnnotation] = options.proxyMemoryRequest
 	}
 	if options.proxyMemoryLimit != "" {
-		configs.Proxy.Resource.LimitMemory = options.proxyMemoryLimit
 		overrideAnnotations[k8s.ProxyMemoryLimitAnnotation] = options.proxyMemoryLimit
 	}
 
@@ -500,29 +468,15 @@ func (options *proxyConfigOptions) overrideConfigs(configs *cfg.All, overrideAnn
 	if options.waitBeforeExitSeconds != 0 {
 		overrideAnnotations[k8s.ProxyWaitBeforeExitSecondsAnnotation] = uintToString(options.waitBeforeExitSeconds)
 	}
+
+	// Set fields that can't be converted into annotations
+	values.Global.Namespace = controlPlaneNamespace
 }
 
 func uintToString(v uint64) string {
 	return strconv.FormatUint(v, 10)
 }
 
-func toPort(p uint) *cfg.Port {
-	return &cfg.Port{Port: uint32(p)}
-}
-
-func parsePort(port *cfg.Port) string {
-	return strconv.FormatUint(uint64(port.GetPort()), 10)
-}
-
-func parsePortRanges(portRanges []*cfg.PortRange) string {
-	var str string
-	for _, p := range portRanges {
-		str += p.GetPortRange() + ","
-	}
-
-	return strings.TrimSuffix(str, ",")
-}
-
 // overwriteRegistry replaces the registry-portion of the provided image with the provided registry.
 func overwriteRegistry(image, newRegistry string) string {
 	if image == "" {
diff --git a/cli/cmd/install.go b/cli/cmd/install.go
index fba6ea6b5..24b84c77d 100644
--- a/cli/cmd/install.go
+++ b/cli/cmd/install.go
@@ -398,7 +398,7 @@ control plane.`,
 }
 
 func installRunE(ctx context.Context, options *installOptions, stage string, flags *pflag.FlagSet) error {
-	values, _, err := options.validateAndBuild(ctx, stage, flags)
+	values, err := options.validateAndBuild(ctx, stage, flags)
 	if err != nil {
 		return err
 	}
@@ -406,26 +406,26 @@ func installRunE(ctx context.Context, options *installOptions, stage string, fla
 	return render(os.Stdout, values)
 }
 
-func (options *installOptions) validateAndBuild(ctx context.Context, stage string, flags *pflag.FlagSet) (*l5dcharts.Values, *pb.All, error) {
+func (options *installOptions) validateAndBuild(ctx context.Context, stage string, flags *pflag.FlagSet) (*l5dcharts.Values, error) {
 	if err := options.validate(); err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
 	options.recordFlags(flags)
 
 	identityValues, err := options.identityOptions.validateAndBuild(ctx)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	return options.validateAndBuildWithIdentity(ctx, stage, identityValues)
 }
 
-func (options *installOptions) validateAndBuildWithIdentity(ctx context.Context, stage string, identityValues *identityWithAnchorsAndTrustDomain) (*l5dcharts.Values, *pb.All, error) {
+func (options *installOptions) validateAndBuildWithIdentity(ctx context.Context, stage string, identityValues *identityWithAnchorsAndTrustDomain) (*l5dcharts.Values, error) {
 	configs := options.configs(toIdentityContext(identityValues))
 
 	values, err := options.buildValuesWithoutIdentity(configs)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	values.Identity = identityValues.Identity
 	values.Global.IdentityTrustAnchorsPEM = identityValues.TrustAnchorsPEM
@@ -435,16 +435,16 @@ func (options *installOptions) validateAndBuildWithIdentity(ctx context.Context,
 	// Update Configuration of Add-ons from config file
 	err = options.UpdateAddOnValuesFromConfig(values)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
 	if options.enableEndpointSlices {
 		if err = validateEndpointSlicesFeature(ctx); err != nil {
-			return nil, nil, fmt.Errorf("--enableEndpointSlice=true not supported: %s", err)
+			return nil, fmt.Errorf("--enableEndpointSlice=true not supported: %s", err)
 		}
 	}
 
-	return values, configs, nil
+	return values, nil
 }
 
 // renderOverrides outputs the Secret/linkerd-config-overrides resource which
diff --git a/cli/cmd/uninject.go b/cli/cmd/uninject.go
index d82e5ad23..07b49464c 100644
--- a/cli/cmd/uninject.go
+++ b/cli/cmd/uninject.go
@@ -5,25 +5,25 @@ import (
 	"io"
 	"os"
 
-	"github.com/linkerd/linkerd2/controller/gen/config"
+	"github.com/linkerd/linkerd2/pkg/charts/linkerd2"
 	"github.com/linkerd/linkerd2/pkg/inject"
 	"github.com/spf13/cobra"
 )
 
 type resourceTransformerUninject struct {
-	configs *config.All
+	values *linkerd2.Values
 }
 
 type resourceTransformerUninjectSilent struct {
-	configs *config.All
+	values *linkerd2.Values
 }
 
-func runUninjectCmd(inputs []io.Reader, errWriter, outWriter io.Writer, conf *config.All) int {
-	return transformInput(inputs, errWriter, outWriter, resourceTransformerUninject{conf})
+func runUninjectCmd(inputs []io.Reader, errWriter, outWriter io.Writer, values *linkerd2.Values) int {
+	return transformInput(inputs, errWriter, outWriter, resourceTransformerUninject{values})
 }
 
-func runUninjectSilentCmd(inputs []io.Reader, errWriter, outWriter io.Writer, conf *config.All) int {
-	return transformInput(inputs, errWriter, outWriter, resourceTransformerUninjectSilent{conf})
+func runUninjectSilentCmd(inputs []io.Reader, errWriter, outWriter io.Writer, values *linkerd2.Values) int {
+	return transformInput(inputs, errWriter, outWriter, resourceTransformerUninjectSilent{values})
 }
 
 func newCmdUninject() *cobra.Command {
@@ -63,7 +63,7 @@ sub-folders, or coming from stdin.`,
 }
 
 func (rt resourceTransformerUninject) transform(bytes []byte) ([]byte, []inject.Report, error) {
-	conf := inject.NewResourceConfig(rt.configs, inject.OriginWebhook)
+	conf := inject.NewResourceConfig(rt.values, inject.OriginWebhook)
 
 	report, err := conf.ParseMetaAndYAML(bytes)
 	if err != nil {
diff --git a/cli/cmd/upgrade.go b/cli/cmd/upgrade.go
index 2493fb28c..9aa026f62 100644
--- a/cli/cmd/upgrade.go
+++ b/cli/cmd/upgrade.go
@@ -240,10 +240,6 @@ func (options *upgradeOptions) validateAndBuild(ctx context.Context, stage strin
 	// persisted with the upgraded config.
 	options.recordFlags(flags)
 
-	// Update the configs from the synthesized options.
-	// The overrideConfigs() is used to override proxy configs only.
-	options.overrideConfigs(configs, map[string]string{})
-
 	if options.enableEndpointSlices {
 		if err = validateEndpointSlicesFeature(ctx); err != nil {
 			return nil, fmt.Errorf("--enableEndpointSlice=true not supported: %s", err)
diff --git a/controller/proxy-injector/fake/data/pod-with-debug.patch.json b/controller/proxy-injector/fake/data/pod-with-debug.patch.json
index d0083844c..9712e83e6 100644
--- a/controller/proxy-injector/fake/data/pod-with-debug.patch.json
+++ b/controller/proxy-injector/fake/data/pod-with-debug.patch.json
@@ -95,7 +95,7 @@
     "op": "add",
     "path": "/spec/containers/-",
     "value": {
-      "image": "ghcr.io/linkerd/debug:debug-image-version",
+      "image": "ghcr.io/linkerd/debug:dev-undefined",
       "imagePullPolicy": "IfNotPresent",
       "name": "linkerd-debug",
       "terminationMessagePolicy": "FallbackToLogsOnError"
@@ -126,6 +126,14 @@
           "name": "LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS",
           "value": "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
         },
+        {
+          "name": "LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT",
+          "value": "100ms"
+        },
+        {
+          "name": "LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT",
+          "value": "1000ms"
+        },
         {
           "name": "LINKERD2_PROXY_CONTROL_LISTEN_ADDR",
           "value": "0.0.0.0:4190"
@@ -148,7 +156,7 @@
         },
         {
           "name": "LINKERD2_PROXY_DESTINATION_PROFILE_SUFFIXES",
-          "value": "."
+          "value": "svc.cluster.local."
         },
         {
           "name": "LINKERD2_PROXY_INBOUND_ACCEPT_KEEPALIVE",
@@ -181,6 +189,10 @@
         {
           "name": "LINKERD2_PROXY_IDENTITY_DISABLED",
           "value": "disabled"
+        },
+        {
+          "name": "LINKERD2_PROXY_TAP_DISABLED",
+          "value": "true"
         }
       ],
       "image": "ghcr.io/linkerd/proxy:dev-undefined",
diff --git a/controller/proxy-injector/fake/data/pod.patch.json b/controller/proxy-injector/fake/data/pod.patch.json
index 49a3d209e..ff25eda98 100644
--- a/controller/proxy-injector/fake/data/pod.patch.json
+++ b/controller/proxy-injector/fake/data/pod.patch.json
@@ -116,6 +116,14 @@
           "name": "LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS",
           "value": "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
         },
+        {
+          "name": "LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT",
+          "value": "100ms"
+        },
+        {
+          "name": "LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT",
+          "value": "1000ms"
+        },
         {
           "name": "LINKERD2_PROXY_CONTROL_LISTEN_ADDR",
           "value": "0.0.0.0:4190"
@@ -138,7 +146,7 @@
         },
         {
           "name": "LINKERD2_PROXY_DESTINATION_PROFILE_SUFFIXES",
-          "value": "."
+          "value": "svc.cluster.local."
         },
         {
           "name": "LINKERD2_PROXY_INBOUND_ACCEPT_KEEPALIVE",
@@ -171,6 +179,10 @@
         {
           "name": "LINKERD2_PROXY_IDENTITY_DISABLED",
           "value": "disabled"
+        },
+        {
+          "name": "LINKERD2_PROXY_TAP_DISABLED",
+          "value": "true"
         }
       ],
       "image": "ghcr.io/linkerd/proxy:dev-undefined",
diff --git a/controller/proxy-injector/metrics.go b/controller/proxy-injector/metrics.go
index 6606b34cd..94c93396a 100644
--- a/controller/proxy-injector/metrics.go
+++ b/controller/proxy-injector/metrics.go
@@ -3,9 +3,8 @@ package injector
 import (
 	"strings"
 
-	"github.com/linkerd/linkerd2/pkg/k8s"
-
 	"github.com/linkerd/linkerd2/pkg/inject"
+	"github.com/linkerd/linkerd2/pkg/k8s"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promauto"
 )
diff --git a/controller/proxy-injector/webhook.go b/controller/proxy-injector/webhook.go
index 2ee076357..38c34b5e2 100644
--- a/controller/proxy-injector/webhook.go
+++ b/controller/proxy-injector/webhook.go
@@ -7,7 +7,6 @@ import (
 
 	"k8s.io/apimachinery/pkg/labels"
 
-	pb "github.com/linkerd/linkerd2/controller/gen/config"
 	"github.com/linkerd/linkerd2/controller/k8s"
 	"github.com/linkerd/linkerd2/pkg/config"
 	"github.com/linkerd/linkerd2/pkg/inject"
@@ -36,12 +35,7 @@ func Inject(
 ) (*admissionv1beta1.AdmissionResponse, error) {
 	log.Debugf("request object bytes: %s", request.Object.Raw)
 
-	globalConfig, err := config.Global(pkgK8s.MountPathGlobalConfig)
-	if err != nil {
-		return nil, err
-	}
-
-	proxyConfig, err := config.Proxy(pkgK8s.MountPathProxyConfig)
+	valuesConfig, err := config.Values(pkgK8s.MountPathValuesConfig)
 	if err != nil {
 		return nil, err
 	}
@@ -52,8 +46,7 @@ func Inject(
 	}
 	nsAnnotations := namespace.GetAnnotations()
 
-	configs := &pb.All{Global: globalConfig, Proxy: proxyConfig}
-	resourceConfig := inject.NewResourceConfig(configs, inject.OriginWebhook).
+	resourceConfig := inject.NewResourceConfig(valuesConfig, inject.OriginWebhook).
 		WithOwnerRetriever(ownerRetriever(ctx, api, request.Namespace)).
 		WithNsAnnotations(nsAnnotations).
 		WithKind(request.Kind.Kind)
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 0cf7cf17e..e074bbfe5 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -10,6 +10,7 @@ import (
 	pb "github.com/linkerd/linkerd2/controller/gen/config"
 	l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
 	log "github.com/sirupsen/logrus"
+	"sigs.k8s.io/yaml"
 )
 
 // Global returns the Global protobuf config from the linkerd-config ConfigMap
@@ -33,6 +34,21 @@ func Install(filepath string) (*pb.Install, error) {
 	return config, err
 }
 
+// Values returns the Value struct from the linkerd-config ConfigMap
+func Values(filepath string) (*l5dcharts.Values, error) {
+	values := &l5dcharts.Values{}
+	configYaml, err := ioutil.ReadFile(filepath)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read config file: %s", err)
+	}
+
+	log.Debugf("%s config YAML: %s", filepath, configYaml)
+	if err = yaml.Unmarshal(configYaml, values); err != nil {
+		return nil, fmt.Errorf("failed to unmarshal JSON from: %s: %s", filepath, err)
+	}
+	return values, err
+}
+
 func unmarshalFile(filepath string, msg proto.Message) error {
 	configJSON, err := ioutil.ReadFile(filepath)
 	if err != nil {
diff --git a/pkg/inject/inject.go b/pkg/inject/inject.go
index 4e73f1416..3d2fbc3e2 100644
--- a/pkg/inject/inject.go
+++ b/pkg/inject/inject.go
@@ -16,9 +16,9 @@ import (
 	"github.com/linkerd/linkerd2-proxy-init/ports"
 	"github.com/linkerd/linkerd2/controller/gen/config"
 	"github.com/linkerd/linkerd2/pkg/charts"
+	"github.com/linkerd/linkerd2/pkg/charts/linkerd2"
 	l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
 	"github.com/linkerd/linkerd2/pkg/k8s"
-	"github.com/linkerd/linkerd2/pkg/version"
 	log "github.com/sirupsen/logrus"
 	appsv1 "k8s.io/api/apps/v1"
 	batchv1 "k8s.io/api/batch/v1"
@@ -32,11 +32,6 @@ import (
 )
 
 const (
-	proxyInitResourceRequestCPU    = "10m"
-	proxyInitResourceRequestMemory = "10Mi"
-	proxyInitResourceLimitCPU      = "100m"
-	proxyInitResourceLimitMemory   = "50Mi"
-
 	traceDefaultSvcAccount = "default"
 )
 
@@ -99,7 +94,7 @@ type OwnerRetrieverFunc func(*corev1.Pod) (string, string)
 
 // ResourceConfig contains the parsed information for a given workload
 type ResourceConfig struct {
-	configs        *config.All
+	values         *l5dcharts.Values
 	nsAnnotations  map[string]string
 	ownerRetriever OwnerRetrieverFunc
 	origin         Origin
@@ -137,14 +132,20 @@ type patch struct {
 }
 
 // NewResourceConfig creates and initializes a ResourceConfig
-func NewResourceConfig(configs *config.All, origin Origin) *ResourceConfig {
+func NewResourceConfig(values *l5dcharts.Values, origin Origin) *ResourceConfig {
 	config := &ResourceConfig{
-		configs: configs,
-		origin:  origin,
+		values: values,
+		origin: origin,
 	}
 
 	config.pod.meta = &metav1.ObjectMeta{}
-	config.pod.labels = map[string]string{k8s.ControllerNSLabel: configs.GetGlobal().GetLinkerdNamespace()}
+
+	// Values can be nil for commands like Uninject
+	var ns string
+	if values != nil {
+		ns = values.Global.Namespace
+	}
+	config.pod.labels = map[string]string{k8s.ControllerNSLabel: ns}
 	config.pod.annotations = map[string]string{}
 	return config
 }
@@ -205,53 +206,72 @@ func (conf *ResourceConfig) ParseMetaAndYAML(bytes []byte) (*Report, error) {
 	return newReport(conf), nil
 }
 
+// GetOverriddenValues returns the final Values struct which is created
+// by overiding annoatated configuration on top of default Values
+func (conf *ResourceConfig) GetOverriddenValues() (*linkerd2.Values, error) {
+
+	// Make a copy of Values and mutate that
+	var copyValues linkerd2.Values
+	copyRawValues, err := yaml.Marshal(conf.values)
+	if err != nil {
+		return nil, err
+	}
+
+	err = yaml.Unmarshal(copyRawValues, &copyValues)
+	if err != nil {
+		return nil, err
+	}
+
+	conf.applyAnnotationOverrides(&copyValues)
+	return &copyValues, nil
+}
+
 // GetPatch returns the JSON patch containing the proxy and init containers specs, if any.
 // If injectProxy is false, only the config.linkerd.io annotations are set.
 func (conf *ResourceConfig) GetPatch(injectProxy bool) ([]byte, error) {
 
-	if conf.requireIdentityOnInboundPorts() != "" && conf.identityContext() == nil {
+	values, err := conf.GetOverriddenValues()
+	if err != nil {
+		return nil, fmt.Errorf("could not generate Overridden Values: %s", err)
+	}
+
+	if values.Global.Proxy.RequireIdentityOnInboundPorts != "" && values.Global.Proxy.DisableIdentity {
 		return nil, fmt.Errorf("%s cannot be set when identity is disabled", k8s.ProxyRequireIdentityOnInboundPortsAnnotation)
 	}
 
-	if conf.destinationGetNetworks() != "" {
-		for _, network := range strings.Split(strings.Trim(conf.destinationGetNetworks(), ","), ",") {
+	if values.Global.Proxy.DestinationGetNetworks != "" {
+		for _, network := range strings.Split(strings.Trim(values.Global.Proxy.DestinationGetNetworks, ","), ",") {
 			if _, _, err := net.ParseCIDR(network); err != nil {
 				return nil, fmt.Errorf("cannot parse destination get networks: %s", err)
 			}
 		}
 	}
 
-	clusterDomain := conf.configs.GetGlobal().GetClusterDomain()
-	if clusterDomain == "" {
-		clusterDomain = "cluster.local"
-	}
-	values := &patch{
-		Values: l5dcharts.Values{
-			Global: &l5dcharts.Global{
-				Namespace:     conf.configs.GetGlobal().GetLinkerdNamespace(),
-				ClusterDomain: clusterDomain,
-			},
-		},
+	patch := &patch{
+		Values:      *values,
 		Annotations: map[string]string{},
 		Labels:      map[string]string{},
 	}
 	switch strings.ToLower(conf.workload.metaType.Kind) {
 	case k8s.Pod:
 	case k8s.CronJob:
-		values.PathPrefix = "/spec/jobTemplate/spec/template"
+		patch.PathPrefix = "/spec/jobTemplate/spec/template"
 	default:
-		values.PathPrefix = "/spec/template"
+		patch.PathPrefix = "/spec/template"
 	}
 
 	if conf.pod.spec != nil {
-		conf.injectPodAnnotations(values)
+		conf.injectPodAnnotations(patch)
 		if injectProxy {
-			conf.injectObjectMeta(values)
-			conf.injectPodSpec(values)
+			conf.injectObjectMeta(patch)
+			conf.injectPodSpec(patch)
+		} else {
+			patch.Global.Proxy = nil
+			patch.Global.ProxyInit = nil
 		}
 	}
 
-	rawValues, err := yaml.Marshal(values)
+	rawValues, err := yaml.Marshal(patch)
 	if err != nil {
 		return nil, err
 	}
@@ -265,7 +285,7 @@ func (conf *ResourceConfig) GetPatch(injectProxy bool) ([]byte, error) {
 	chart := &charts.Chart{
 		Name:      "patch",
 		Dir:       "patch",
-		Namespace: values.Global.Namespace,
+		Namespace: conf.values.Global.Namespace,
 		RawValues: rawValues,
 		Files:     files,
 	}
@@ -493,58 +513,7 @@ func (conf *ResourceConfig) complete(template *corev1.PodTemplateSpec) {
 
 // injectPodSpec adds linkerd sidecars to the provided PodSpec.
 func (conf *ResourceConfig) injectPodSpec(values *patch) {
-	opaquePorts, err := conf.proxyOpaquePorts()
-	if err != nil {
-		log.Errorf("unrecognized value used for the %s annotation: %s", k8s.ProxyOpaquePortsAnnotation, conf.getOverride(k8s.ProxyOpaquePortsAnnotation))
-	}
-
-	values.Global.Proxy = &l5dcharts.Proxy{
-		Component:              conf.pod.labels[k8s.ProxyDeploymentLabel],
-		EnableExternalProfiles: conf.enableExternalProfiles(),
-		DisableTap:             conf.tapDisabled(),
-		Image: &l5dcharts.Image{
-			Name:       conf.proxyImage(),
-			Version:    conf.proxyVersion(),
-			PullPolicy: conf.proxyImagePullPolicy(),
-		},
-		LogLevel:  conf.proxyLogLevel(),
-		LogFormat: conf.proxyLogFormat(),
-		Ports: &l5dcharts.Ports{
-			Admin:    conf.proxyAdminPort(),
-			Control:  conf.proxyControlPort(),
-			Inbound:  conf.proxyInboundPort(),
-			Outbound: conf.proxyOutboundPort(),
-		},
-		UID:                           conf.proxyUID(),
-		Resources:                     conf.proxyResourceRequirements(),
-		WaitBeforeExitSeconds:         conf.proxyWaitBeforeExitSeconds(),
-		IsGateway:                     conf.isGateway(),
-		RequireIdentityOnInboundPorts: conf.requireIdentityOnInboundPorts(),
-		DestinationGetNetworks:        conf.destinationGetNetworks(),
-		OutboundConnectTimeout:        conf.getOutboundConnectTimeout(),
-		InboundConnectTimeout:         conf.getInboundConnectTimeout(),
-		OpaquePorts:                   opaquePorts,
-	}
-
-	if v := conf.pod.meta.Annotations[k8s.ProxyEnableDebugAnnotation]; v != "" {
-		debug, err := strconv.ParseBool(v)
-		if err != nil {
-			log.Warnf("unrecognized value used for the %s annotation: %s", k8s.ProxyEnableDebugAnnotation, v)
-			debug = false
-		}
-
-		if debug {
-			log.Infof("inject debug container")
-			values.DebugContainer = &l5dcharts.DebugContainer{
-				Image: &l5dcharts.Image{
-					Name:       conf.debugSidecarImage(),
-					Version:    conf.debugSidecarImageVersion(),
-					PullPolicy: conf.debugSidecarImagePullPolicy(),
-				},
-			}
-		}
-	}
-
+	values.Global.Proxy.Component = conf.pod.labels[k8s.ProxyDeploymentLabel]
 	saVolumeMount := conf.serviceAccountVolumeMount()
 
 	// use the primary container's capabilities to ensure psp compliance, if
@@ -572,55 +541,39 @@ func (conf *ResourceConfig) injectPodSpec(values *patch) {
 		}
 	}
 
-	if !conf.configs.GetGlobal().GetCniEnabled() {
-		conf.injectProxyInit(values)
+	if v := conf.pod.meta.Annotations[k8s.ProxyEnableDebugAnnotation]; v != "" {
+		debug, err := strconv.ParseBool(v)
+		if err != nil {
+			log.Warnf("unrecognized value used for the %s annotation: %s", k8s.ProxyEnableDebugAnnotation, v)
+			debug = false
+		}
+
+		if debug {
+			log.Infof("inject debug container")
+			values.DebugContainer = &l5dcharts.DebugContainer{
+				Image: &l5dcharts.Image{
+					Name:       conf.values.DebugContainer.Image.Name,
+					Version:    conf.values.DebugContainer.Image.Version,
+					PullPolicy: conf.values.DebugContainer.Image.PullPolicy,
+				},
+			}
+		}
 	}
 
+	conf.injectProxyInit(values)
 	values.AddRootVolumes = len(conf.pod.spec.Volumes) == 0
 
-	values.Global.Proxy.Trace = &l5dcharts.Trace{}
-	if trace := conf.trace(); trace != nil {
-		log.Infof("tracing enabled: remote service=%s, service account=%s", trace.CollectorSvcAddr, trace.CollectorSvcAccount)
-		values.Global.Proxy.Trace = trace
+	// Configure Tracing values based on svcAddr, as it is the main toggle for tracing
+	if conf.values.Global.Proxy.Trace.CollectorSvcAddr != "" {
+		values.Global.Proxy.Trace = conf.trace(conf.values.Global.Proxy.Trace.CollectorSvcAddr, conf.values.Global.Proxy.Trace.CollectorSvcAccount)
 	}
-
-	idctx := conf.identityContext()
-	if idctx == nil {
-		values.Global.Proxy.DisableIdentity = true
-		return
-	}
-	values.Global.IdentityTrustAnchorsPEM = idctx.GetTrustAnchorsPem()
-	values.Global.IdentityTrustDomain = idctx.GetTrustDomain()
-	values.Identity = &l5dcharts.Identity{}
-
 }
 
 func (conf *ResourceConfig) injectProxyInit(values *patch) {
-	values.Global.ProxyInit = &l5dcharts.ProxyInit{
-		Image: &l5dcharts.Image{
-			Name:       conf.proxyInitImage(),
-			PullPolicy: conf.proxyInitImagePullPolicy(),
-			Version:    conf.proxyInitVersion(),
-		},
-		IgnoreInboundPorts:  conf.proxyInboundSkipPorts(),
-		IgnoreOutboundPorts: conf.proxyOutboundSkipPorts(),
-		Resources: &l5dcharts.Resources{
-			CPU: l5dcharts.Constraints{
-				Limit:   proxyInitResourceLimitCPU,
-				Request: proxyInitResourceRequestCPU,
-			},
-			Memory: l5dcharts.Constraints{
-				Limit:   proxyInitResourceLimitMemory,
-				Request: proxyInitResourceRequestMemory,
-			},
-		},
-		Capabilities: values.Global.Proxy.Capabilities,
-		SAMountPath:  values.Global.Proxy.SAMountPath,
-		XTMountPath: &l5dcharts.VolumeMountPath{
-			MountPath: k8s.MountPathXtablesLock,
-			Name:      k8s.InitXtablesLockVolumeMountName,
-		},
-	}
+
+	// Fill common fields from Proxy into ProxyInit
+	values.Global.ProxyInit.Capabilities = values.Global.Proxy.Capabilities
+	values.Global.ProxyInit.SAMountPath = values.Global.Proxy.SAMountPath
 
 	if v := conf.pod.meta.Annotations[k8s.CloseWaitTimeoutAnnotation]; v != "" {
 		closeWait, err := time.ParseDuration(v)
@@ -648,12 +601,7 @@ func (conf *ResourceConfig) serviceAccountVolumeMount() *corev1.VolumeMount {
 	return nil
 }
 
-func (conf *ResourceConfig) trace() *l5dcharts.Trace {
-	var (
-		svcAddr    = conf.getOverride(k8s.ProxyTraceCollectorSvcAddrAnnotation)
-		svcAccount = conf.getOverride(k8s.ProxyTraceCollectorSvcAccountAnnotation)
-	)
-
+func (conf *ResourceConfig) trace(svcAddr, svcAccount string) *l5dcharts.Trace {
 	if svcAddr == "" {
 		return nil
 	}
@@ -681,12 +629,13 @@ func (conf *ResourceConfig) trace() *l5dcharts.Trace {
 // Given a ObjectMeta, update ObjectMeta in place with the new labels and
 // annotations.
 func (conf *ResourceConfig) injectObjectMeta(values *patch) {
-	values.Annotations[k8s.ProxyVersionAnnotation] = conf.proxyVersion()
 
-	if conf.identityContext() != nil {
-		values.Annotations[k8s.IdentityModeAnnotation] = k8s.IdentityModeDefault
-	} else {
+	values.Annotations[k8s.ProxyVersionAnnotation] = conf.values.Global.Proxy.Image.Version
+
+	if values.Identity == nil || values.Global.Proxy.DisableIdentity {
 		values.Annotations[k8s.IdentityModeAnnotation] = k8s.IdentityModeDisabled
+	} else {
+		values.Annotations[k8s.IdentityModeAnnotation] = k8s.IdentityModeDefault
 	}
 
 	if len(conf.pod.labels) > 0 {
@@ -715,412 +664,273 @@ func (conf *ResourceConfig) injectPodAnnotations(values *patch) {
 	}
 }
 
-func (conf *ResourceConfig) getOverride(annotation string) string {
-	if override := conf.pod.meta.Annotations[annotation]; override != "" {
-		return override
+func (conf *ResourceConfig) applyAnnotationOverrides(values *l5dcharts.Values) {
+	if override, ok := conf.getOverride(k8s.ProxyImageAnnotation); ok {
+		values.Global.Proxy.Image.Name = override
 	}
-	return conf.nsAnnotations[annotation]
-}
 
-func (conf *ResourceConfig) proxyImage() string {
-	if override := conf.getOverride(k8s.ProxyImageAnnotation); override != "" {
-		return override
+	if override, ok := conf.getOverride(k8s.ProxyVersionOverrideAnnotation); ok {
+		values.Global.Proxy.Image.Version = override
 	}
-	return conf.configs.GetProxy().GetProxyImage().GetImageName()
-}
 
-func (conf *ResourceConfig) proxyImagePullPolicy() string {
-	if override := conf.getOverride(k8s.ProxyImagePullPolicyAnnotation); override != "" {
-		return override
+	if override, ok := conf.getOverride(k8s.ProxyImagePullPolicyAnnotation); ok {
+		values.Global.Proxy.Image.PullPolicy = override
 	}
-	return conf.configs.GetProxy().GetProxyImage().GetPullPolicy()
-}
 
-func (conf *ResourceConfig) proxyVersion() string {
-	if override := conf.getOverride(k8s.ProxyVersionOverrideAnnotation); override != "" {
-		return override
+	if override, ok := conf.getOverride(k8s.ProxyInitImageVersionAnnotation); ok {
+		values.Global.ProxyInit.Image.Version = override
 	}
-	if proxyVersion := conf.configs.GetProxy().GetProxyVersion(); proxyVersion != "" {
-		return proxyVersion
-	}
-	if controlPlaneVersion := conf.configs.GetGlobal().GetVersion(); controlPlaneVersion != "" {
-		return controlPlaneVersion
-	}
-	return version.Version
-}
 
-func (conf *ResourceConfig) proxyInitVersion() string {
-	if override := conf.getOverride(k8s.ProxyInitImageVersionAnnotation); override != "" {
-		return override
-	}
-	if v := conf.configs.GetProxy().GetProxyInitImageVersion(); v != "" {
-		return v
-	}
-	return version.ProxyInitVersion
-}
-
-func (conf *ResourceConfig) proxyControlPort() int32 {
-	if override := conf.getOverride(k8s.ProxyControlPortAnnotation); override != "" {
+	if override, ok := conf.getOverride(k8s.ProxyControlPortAnnotation); ok {
 		controlPort, err := strconv.ParseInt(override, 10, 32)
 		if err == nil {
-			return int32(controlPort)
+			values.Global.Proxy.Ports.Control = int32(controlPort)
 		}
 	}
 
-	return int32(conf.configs.GetProxy().GetControlPort().GetPort())
-}
-
-func (conf *ResourceConfig) proxyInboundPort() int32 {
-	if override := conf.getOverride(k8s.ProxyInboundPortAnnotation); override != "" {
+	if override, ok := conf.getOverride(k8s.ProxyInboundPortAnnotation); ok {
 		inboundPort, err := strconv.ParseInt(override, 10, 32)
 		if err == nil {
-			return int32(inboundPort)
+			values.Global.Proxy.Ports.Inbound = int32(inboundPort)
 		}
 	}
 
-	return int32(conf.configs.GetProxy().GetInboundPort().GetPort())
-}
-
-func (conf *ResourceConfig) proxyAdminPort() int32 {
-	if override := conf.getOverride(k8s.ProxyAdminPortAnnotation); override != "" {
+	if override, ok := conf.getOverride(k8s.ProxyAdminPortAnnotation); ok {
 		adminPort, err := strconv.ParseInt(override, 10, 32)
 		if err == nil {
-			return int32(adminPort)
+			values.Global.Proxy.Ports.Admin = int32(adminPort)
 		}
 	}
-	return int32(conf.configs.GetProxy().GetAdminPort().GetPort())
-}
 
-func (conf *ResourceConfig) proxyOutboundPort() int32 {
-	if override := conf.getOverride(k8s.ProxyOutboundPortAnnotation); override != "" {
+	if override, ok := conf.getOverride(k8s.ProxyOutboundPortAnnotation); ok {
 		outboundPort, err := strconv.ParseInt(override, 10, 32)
 		if err == nil {
-			return int32(outboundPort)
+			values.Global.Proxy.Ports.Outbound = int32(outboundPort)
 		}
 	}
 
-	return int32(conf.configs.GetProxy().GetOutboundPort().GetPort())
-}
-
-func (conf *ResourceConfig) proxyLogLevel() string {
-	if override := conf.getOverride(k8s.ProxyLogLevelAnnotation); override != "" {
-		return override
+	if override, ok := conf.getOverride(k8s.ProxyLogLevelAnnotation); ok {
+		values.Global.Proxy.LogLevel = override
 	}
 
-	return conf.configs.GetProxy().GetLogLevel().GetLevel()
-}
-
-func (conf *ResourceConfig) proxyLogFormat() string {
-	if override := conf.getOverride(k8s.ProxyLogFormatAnnotation); override != "" {
-		return override
+	if override, ok := conf.getOverride(k8s.ProxyLogFormatAnnotation); ok {
+		values.Global.Proxy.LogFormat = override
 	}
 
-	return conf.configs.GetProxy().GetLogFormat()
-}
-
-func (conf *ResourceConfig) identityContext() *config.IdentityContext {
-	if override := conf.getOverride(k8s.ProxyDisableIdentityAnnotation); override != "" {
+	if override, ok := conf.getOverride(k8s.ProxyDisableIdentityAnnotation); ok {
 		value, err := strconv.ParseBool(override)
-		if err == nil && value {
-			return nil
+		if err == nil {
+			values.Global.Proxy.DisableIdentity = value
 		}
 	}
 
-	return conf.configs.GetGlobal().GetIdentityContext()
-}
-
-func (conf *ResourceConfig) tapDisabled() bool {
-	if override := conf.getOverride(k8s.ProxyDisableTapAnnotation); override != "" {
+	if override, ok := conf.getOverride(k8s.ProxyDisableTapAnnotation); ok {
 		value, err := strconv.ParseBool(override)
-		if err == nil && value {
-			return true
+		if err == nil {
+			values.Global.Proxy.DisableTap = value
 		}
 	}
-	return false
-}
 
-func (conf *ResourceConfig) requireIdentityOnInboundPorts() string {
-	return conf.getOverride(k8s.ProxyRequireIdentityOnInboundPortsAnnotation)
-}
-
-func (conf *ResourceConfig) destinationGetNetworks() string {
-	if podOverride, hasPodOverride := conf.pod.meta.Annotations[k8s.ProxyDestinationGetNetworks]; hasPodOverride {
-		return podOverride
+	if override, ok := conf.getOverride(k8s.ProxyRequireIdentityOnInboundPortsAnnotation); ok {
+		values.Global.Proxy.RequireIdentityOnInboundPorts = override
 	}
 
-	if nsOverride, hasNsOverride := conf.nsAnnotations[k8s.ProxyDestinationGetNetworks]; hasNsOverride {
-		return nsOverride
+	if override, ok := conf.getOverride(k8s.ProxyDestinationGetNetworks); ok {
+		values.Global.Proxy.DestinationGetNetworks = override
 	}
 
-	return conf.configs.GetProxy().DestinationGetNetworks
-}
-
-func (conf *ResourceConfig) getOutboundConnectTimeout() string {
-	if podOverride, hasPodOverride := conf.pod.meta.Annotations[k8s.ProxyOutboundConnectTimeout]; hasPodOverride {
-		duration, err := time.ParseDuration(podOverride)
+	if override, ok := conf.getOverride(k8s.ProxyOutboundConnectTimeout); ok {
+		duration, err := time.ParseDuration(override)
 		if err != nil {
 			log.Warnf("unrecognized proxy-outbound-connect-timeout duration value found on pod annotation: %s", err.Error())
 		} else {
-			return fmt.Sprintf("%dms", int(duration.Seconds()*1000))
+			values.Global.Proxy.OutboundConnectTimeout = fmt.Sprintf("%dms", int(duration.Seconds()*1000))
 		}
 	}
 
-	if nsOverride, hasNsOverride := conf.nsAnnotations[k8s.ProxyOutboundConnectTimeout]; hasNsOverride {
-		duration, err := time.ParseDuration(nsOverride)
-		if err != nil {
-			log.Warnf("unrecognized proxy-outbound-connect-timeout duration value found on namespace annotation: %s", err.Error())
-		} else {
-			return fmt.Sprintf("%dms", int(duration.Seconds()*1000))
-		}
-	}
-
-	return conf.configs.GetProxy().OutboundConnectTimeout
-}
-
-func (conf *ResourceConfig) getInboundConnectTimeout() string {
-	if podOverride, hasPodOverride := conf.pod.meta.Annotations[k8s.ProxyInboundConnectTimeout]; hasPodOverride {
-		duration, err := time.ParseDuration(podOverride)
+	if override, ok := conf.getOverride(k8s.ProxyInboundConnectTimeout); ok {
+		duration, err := time.ParseDuration(override)
 		if err != nil {
 			log.Warnf("unrecognized proxy-inbound-connect-timeout duration value found on pod annotation: %s", err.Error())
 		} else {
-			return fmt.Sprintf("%dms", int(duration.Seconds()*1000))
-		}
-	}
-
-	if nsOverride, hasNsOverride := conf.nsAnnotations[k8s.ProxyInboundConnectTimeout]; hasNsOverride {
-		duration, err := time.ParseDuration(nsOverride)
-		if err != nil {
-			log.Warnf("unrecognized proxy-inbound-connect-timeout duration value found on namespace annotation: %s", err.Error())
-		} else {
-			return fmt.Sprintf("%dms", int(duration.Seconds()*1000))
+			values.Global.Proxy.InboundConnectTimeout = fmt.Sprintf("%dms", int(duration.Seconds()*1000))
 		}
 	}
 
-	return conf.configs.GetProxy().InboundConnectTimeout
-}
-
-func (conf *ResourceConfig) isGateway() bool {
-	if override := conf.getOverride(k8s.ProxyEnableGatewayAnnotation); override != "" {
+	if override, ok := conf.getOverride(k8s.ProxyEnableGatewayAnnotation); ok {
 		value, err := strconv.ParseBool(override)
-		return err == nil && value
+		if err == nil {
+			values.Global.Proxy.IsGateway = value
+		}
 	}
 
-	return false
-}
-
-func (conf *ResourceConfig) proxyWaitBeforeExitSeconds() uint64 {
-	if override := conf.getOverride(k8s.ProxyWaitBeforeExitSecondsAnnotation); override != "" {
+	if override, ok := conf.getOverride(k8s.ProxyWaitBeforeExitSecondsAnnotation); ok {
 		waitBeforeExitSeconds, err := strconv.ParseUint(override, 10, 64)
 		if nil != err {
 			log.Warnf("unrecognized value used for the %s annotation, uint64 is expected: %s",
 				k8s.ProxyWaitBeforeExitSecondsAnnotation, override)
+		} else {
+			values.Global.Proxy.WaitBeforeExitSeconds = waitBeforeExitSeconds
 		}
-
-		return waitBeforeExitSeconds
 	}
 
-	return 0
-}
-
-func (conf *ResourceConfig) proxyResourceRequirements() *l5dcharts.Resources {
-	var (
-		requestCPU    k8sResource.Quantity
-		requestMemory k8sResource.Quantity
-		limitCPU      k8sResource.Quantity
-		limitMemory   k8sResource.Quantity
-		err           error
-	)
-	res := &l5dcharts.Resources{}
-
-	if override := conf.getOverride(k8s.ProxyCPURequestAnnotation); override != "" {
-		requestCPU, err = k8sResource.ParseQuantity(override)
-	} else if defaultRequest := conf.configs.GetProxy().GetResource().GetRequestCpu(); defaultRequest != "" {
-		requestCPU, err = k8sResource.ParseQuantity(defaultRequest)
-	}
-	if err != nil {
-		log.Warnf("%s (%s)", err, k8s.ProxyCPURequestAnnotation)
-	}
-	if !requestCPU.IsZero() {
-		res.CPU.Request = requestCPU.String()
+	if override, ok := conf.getOverride(k8s.ProxyCPURequestAnnotation); ok {
+		res, err := k8sResource.ParseQuantity(override)
+		if err != nil {
+			log.Warnf("%s (%s)", err, k8s.ProxyCPURequestAnnotation)
+		} else {
+			values.Global.Proxy.Resources.CPU.Request = res.String()
+		}
 	}
 
-	if override := conf.getOverride(k8s.ProxyMemoryRequestAnnotation); override != "" {
-		requestMemory, err = k8sResource.ParseQuantity(override)
-	} else if defaultRequest := conf.configs.GetProxy().GetResource().GetRequestMemory(); defaultRequest != "" {
-		requestMemory, err = k8sResource.ParseQuantity(defaultRequest)
-	}
-	if err != nil {
-		log.Warnf("%s (%s)", err, k8s.ProxyMemoryRequestAnnotation)
-	}
-	if !requestMemory.IsZero() {
-		res.Memory.Request = requestMemory.String()
+	if override, ok := conf.getOverride(k8s.ProxyMemoryRequestAnnotation); ok {
+		res, err := k8sResource.ParseQuantity(override)
+		if err != nil {
+			log.Warnf("%s (%s)", err, k8s.ProxyMemoryRequestAnnotation)
+		} else {
+			values.Global.Proxy.Resources.Memory.Request = res.String()
+		}
 	}
 
-	if override := conf.getOverride(k8s.ProxyCPULimitAnnotation); override != "" {
-		limitCPU, err = k8sResource.ParseQuantity(override)
-	} else if defaultLimit := conf.configs.GetProxy().GetResource().GetLimitCpu(); defaultLimit != "" {
-		limitCPU, err = k8sResource.ParseQuantity(defaultLimit)
-	}
-	if err != nil {
-		log.Warnf("%s (%s)", err, k8s.ProxyCPULimitAnnotation)
-	}
-	if !limitCPU.IsZero() {
-		res.CPU.Limit = limitCPU.String()
+	if override, ok := conf.getOverride(k8s.ProxyCPULimitAnnotation); ok {
+		res, err := k8sResource.ParseQuantity(override)
+		if err != nil {
+			log.Warnf("%s (%s)", err, k8s.ProxyCPULimitAnnotation)
+		} else {
+			values.Global.Proxy.Resources.CPU.Limit = res.String()
+		}
 	}
 
-	if override := conf.getOverride(k8s.ProxyMemoryLimitAnnotation); override != "" {
-		limitMemory, err = k8sResource.ParseQuantity(override)
-	} else if defaultLimit := conf.configs.GetProxy().GetResource().GetLimitMemory(); defaultLimit != "" {
-		limitMemory, err = k8sResource.ParseQuantity(defaultLimit)
-	}
-	if err != nil {
-		log.Warnf("%s (%s)", err, k8s.ProxyMemoryLimitAnnotation)
-	}
-	if !limitMemory.IsZero() {
-		res.Memory.Limit = limitMemory.String()
+	if override, ok := conf.getOverride(k8s.ProxyMemoryLimitAnnotation); ok {
+		res, err := k8sResource.ParseQuantity(override)
+		if err != nil {
+			log.Warnf("%s (%s)", err, k8s.ProxyMemoryLimitAnnotation)
+		} else {
+			values.Global.Proxy.Resources.Memory.Limit = res.String()
+		}
 	}
 
-	return res
-}
-
-func (conf *ResourceConfig) proxyUID() int64 {
-	if overrides := conf.getOverride(k8s.ProxyUIDAnnotation); overrides != "" {
-		v, err := strconv.ParseInt(overrides, 10, 64)
+	if override, ok := conf.getOverride(k8s.ProxyUIDAnnotation); ok {
+		v, err := strconv.ParseInt(override, 10, 64)
 		if err == nil {
-			return v
+			values.Global.Proxy.UID = v
 		}
 	}
 
-	return conf.configs.GetProxy().GetProxyUid()
-}
-
-func (conf *ResourceConfig) enableExternalProfiles() bool {
-	disableExternalProfiles := conf.configs.GetProxy().GetDisableExternalProfiles()
-	if override := conf.getOverride(k8s.ProxyEnableExternalProfilesAnnotation); override != "" {
+	if override, ok := conf.getOverride(k8s.ProxyEnableExternalProfilesAnnotation); ok {
 		value, err := strconv.ParseBool(override)
 		if err == nil {
-			return value
+			values.Global.Proxy.EnableExternalProfiles = value
 		}
 	}
 
-	return !disableExternalProfiles
-}
-
-func (conf *ResourceConfig) proxyInitImage() string {
-	if override := conf.getOverride(k8s.ProxyInitImageAnnotation); override != "" {
-		return override
+	if override, ok := conf.getOverride(k8s.ProxyInitImageAnnotation); ok {
+		values.Global.ProxyInit.Image.Name = override
 	}
-	return conf.configs.GetProxy().GetProxyInitImage().GetImageName()
-}
 
-func (conf *ResourceConfig) proxyInitImagePullPolicy() string {
-	if override := conf.getOverride(k8s.ProxyImagePullPolicyAnnotation); override != "" {
-		return override
+	if override, ok := conf.getOverride(k8s.ProxyImagePullPolicyAnnotation); ok {
+		values.Global.ProxyInit.Image.PullPolicy = override
 	}
-	return conf.configs.GetProxy().GetProxyInitImage().GetPullPolicy()
-}
 
-func (conf *ResourceConfig) proxyInboundSkipPorts() string {
-	if override := conf.getOverride(k8s.ProxyIgnoreInboundPortsAnnotation); override != "" {
-		return override
+	if override, ok := conf.getOverride(k8s.ProxyIgnoreInboundPortsAnnotation); ok {
+		values.Global.ProxyInit.IgnoreInboundPorts = override
 	}
 
-	portRanges := []string{}
-	for _, portOrRange := range conf.configs.GetProxy().GetIgnoreInboundPorts() {
-		portRanges = append(portRanges, portOrRange.GetPortRange())
+	if override, ok := conf.getOverride(k8s.ProxyIgnoreOutboundPortsAnnotation); ok {
+		values.Global.ProxyInit.IgnoreOutboundPorts = override
 	}
-	return strings.Join(portRanges, ",")
-}
 
-func (conf *ResourceConfig) proxyOpaquePorts() (string, error) {
-	override := conf.getOverride(k8s.ProxyOpaquePortsAnnotation)
-	if override == "" {
-		return "", nil
-	}
-
-	var portRanges []*config.PortRange
-	split := strings.Split(strings.TrimSuffix(override, ","), ",")
-	portRanges = ToPortRanges(split)
-
-	var str string
-	for _, portRange := range portRanges {
-		pr := portRange.GetPortRange()
-
-		// It is valid for the format of a port name to be the same as a
-		// port range (e.g. `123-456` is a valid name, but also is a valid
-		// range). All port names must be checked before making it a list.
-		named := false
-		for _, c := range conf.pod.spec.Containers {
-			for _, p := range c.Ports {
-				if p.Name == pr {
-					named = true
-					str += strconv.Itoa(int(p.ContainerPort)) + ","
+	if override, ok := conf.getOverride(k8s.ProxyOpaquePortsAnnotation); ok {
+		var portRanges []*config.PortRange
+		split := strings.Split(strings.TrimSuffix(override, ","), ",")
+		portRanges = ToPortRanges(split)
+
+		var str string
+		for _, portRange := range portRanges {
+			pr := portRange.GetPortRange()
+
+			// It is valid for the format of a port name to be the same as a
+			// port range (e.g. `123-456` is a valid name, but also is a valid
+			// range). All port names must be checked before making it a list.
+			named := false
+			for _, c := range conf.pod.spec.Containers {
+				for _, p := range c.Ports {
+					if p.Name == pr {
+						named = true
+						str += strconv.Itoa(int(p.ContainerPort)) + ","
+					}
 				}
 			}
-		}
 
-		if !named {
-			pr, err := ports.ParsePortRange(pr)
-			if err != nil {
-				return "", err
-			}
-			for i := pr.LowerBound; i <= pr.UpperBound; i++ {
-				str += strconv.Itoa(i) + ","
+			if !named {
+				pr, err := ports.ParsePortRange(pr)
+				if err != nil {
+					log.Warnf("Invalid port range [%v]: %s", pr, err)
+					continue
+				}
+				for i := pr.LowerBound; i <= pr.UpperBound; i++ {
+					str += strconv.Itoa(i) + ","
+				}
 			}
 		}
+		values.Global.Proxy.OpaquePorts = strings.TrimSuffix(str, ",")
 	}
-	return strings.TrimSuffix(str, ","), nil
-}
 
-func (conf *ResourceConfig) proxyOutboundSkipPorts() string {
-	if override := conf.getOverride(k8s.ProxyIgnoreOutboundPortsAnnotation); override != "" {
-		return override
+	if override, ok := conf.getOverride(k8s.DebugImageAnnotation); ok {
+		values.DebugContainer.Image.Name = override
 	}
 
-	portRanges := []string{}
-	for _, port := range conf.configs.GetProxy().GetIgnoreOutboundPorts() {
-		portRanges = append(portRanges, port.GetPortRange())
+	if override, ok := conf.getOverride(k8s.DebugImageVersionAnnotation); ok {
+		values.DebugContainer.Image.Version = override
 	}
-	return strings.Join(portRanges, ",")
-}
 
-func (conf *ResourceConfig) debugSidecarImage() string {
-	if override := conf.getOverride(k8s.DebugImageAnnotation); override != "" {
-		return override
+	if override, ok := conf.getOverride(k8s.DebugImagePullPolicyAnnotation); ok {
+		values.DebugContainer.Image.PullPolicy = override
 	}
-	return conf.configs.GetProxy().GetDebugImage().GetImageName()
-}
 
-func (conf *ResourceConfig) debugSidecarImageVersion() string {
-	if override := conf.getOverride(k8s.DebugImageVersionAnnotation); override != "" {
-		return override
-	}
-	if debugVersion := conf.configs.GetProxy().GetDebugImageVersion(); debugVersion != "" {
-		return debugVersion
+	// Get Trace Overrides
+	var svcAddr, svcAccount string
+	if override, ok := conf.getOverride(k8s.ProxyTraceCollectorSvcAddrAnnotation); ok {
+		svcAddr = override
 	}
-	if controlPlaneVersion := conf.configs.GetGlobal().GetVersion(); controlPlaneVersion != "" {
-		return controlPlaneVersion
+
+	if override, ok := conf.getOverride(k8s.ProxyTraceCollectorSvcAccountAnnotation); ok {
+		svcAccount = override
 	}
-	return version.Version
-}
 
-func (conf *ResourceConfig) debugSidecarImagePullPolicy() string {
-	if override := conf.getOverride(k8s.DebugImagePullPolicyAnnotation); override != "" {
-		return override
+	// Configure Tracing values based on svcAddr, as it is the main toggle for tracing
+	if svcAddr != "" {
+		values.Global.Proxy.Trace = conf.trace(svcAddr, svcAccount)
 	}
-	return conf.configs.GetProxy().GetDebugImage().GetPullPolicy()
 }
 
 // GetOverriddenConfiguration returns a map of the overridden proxy annotations
 func (conf *ResourceConfig) GetOverriddenConfiguration() map[string]string {
 	proxyOverrideConfig := map[string]string{}
 	for _, annotation := range ProxyAnnotations {
-		proxyOverrideConfig[annotation] = conf.getOverride(annotation)
+		proxyOverrideConfig[annotation], _ = conf.getOverride(annotation)
 	}
 
 	return proxyOverrideConfig
 }
 
+func (conf *ResourceConfig) getOverride(annotation string) (string, bool) {
+
+	// pod.annotations are set by the CLI overridden logic
+	if override, ok := conf.pod.annotations[annotation]; ok {
+		return override, ok
+	}
+
+	if override, ok := conf.pod.meta.Annotations[annotation]; ok {
+		return override, ok
+	}
+	if override, ok := conf.nsAnnotations[annotation]; ok {
+		return override, ok
+	}
+
+	return "", false
+}
+
 // IsControlPlaneComponent returns true if the component is part of linkerd control plane
 func (conf *ResourceConfig) IsControlPlaneComponent() bool {
 	_, b := conf.pod.meta.Labels[k8s.ControllerComponentLabel]
diff --git a/pkg/k8s/labels.go b/pkg/k8s/labels.go
index 2f4cdc4cf..848cc806f 100644
--- a/pkg/k8s/labels.go
+++ b/pkg/k8s/labels.go
@@ -350,6 +350,9 @@ const (
 	// MountPathInstallConfig is the path at which the install config file is mounted.
 	MountPathInstallConfig = MountPathBase + "/config/install"
 
+	// MountPathValuesConfig is the path at which the values config file is mounted.
+	MountPathValuesConfig = MountPathBase + "/config/values"
+
 	// MountPathEndEntity is the path at which a tmpfs directory is mounted to
 	// store identity credentials.
 	MountPathEndEntity = MountPathBase + "/identity/end-entity"


Test patch:
diff --git a/cli/cmd/inject_test.go b/cli/cmd/inject_test.go
index 9c7d3c812..3f7098947 100644
--- a/cli/cmd/inject_test.go
+++ b/cli/cmd/inject_test.go
@@ -3,7 +3,6 @@ package cmd
 import (
 	"bufio"
 	"bytes"
-	"context"
 	"fmt"
 	"io"
 	"io/ioutil"
@@ -12,8 +11,7 @@ import (
 	"path/filepath"
 	"testing"
 
-	"github.com/linkerd/linkerd2/controller/gen/config"
-	pb "github.com/linkerd/linkerd2/controller/gen/config"
+	"github.com/linkerd/linkerd2/pkg/charts/linkerd2"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 )
 
@@ -22,7 +20,7 @@ type testCase struct {
 	goldenFileName         string
 	reportFileName         string
 	injectProxy            bool
-	testInjectConfig       *config.All
+	testInjectConfig       *linkerd2.Values
 	overrideAnnotations    map[string]string
 	enableDebugSidecarFlag bool
 }
@@ -46,7 +44,7 @@ func testUninjectAndInject(t *testing.T, tc testCase) {
 	report := new(bytes.Buffer)
 	transformer := &resourceTransformerInject{
 		injectProxy:         tc.injectProxy,
-		configs:             tc.testInjectConfig,
+		values:              tc.testInjectConfig,
 		overrideAnnotations: tc.overrideAnnotations,
 		enableDebugSidecar:  tc.enableDebugSidecarFlag,
 		allowNsInject:       true,
@@ -61,60 +59,62 @@ func testUninjectAndInject(t *testing.T, tc testCase) {
 	diffTestdata(t, reportFileName, report.String())
 }
 
-func testInstallConfig(ctx context.Context) *pb.All {
-	installOptions, err := testInstallOptions()
+func defaultConfig() *linkerd2.Values {
+	defaultConfig, err := testInstallValues()
 	if err != nil {
 		log.Fatalf("Unexpected error: %v", err)
 	}
+	defaultConfig.Global.LinkerdVersion = "test-inject-control-plane-version"
+	defaultConfig.Global.Proxy.Image.Version = "test-inject-proxy-version"
+	defaultConfig.DebugContainer.Image.Version = "test-inject-debug-version"
 
-	_, c, err := installOptions.validateAndBuild(ctx, "", nil)
-	if err != nil {
-		log.Fatalf("test install options must be valid: %s", err)
-	}
-	return c
+	return defaultConfig
 }
 
 func TestUninjectAndInject(t *testing.T) {
-	ctx := context.Background()
-	defaultConfig := testInstallConfig(ctx)
-	defaultConfig.Global.Version = "test-inject-control-plane-version"
-	defaultConfig.Proxy.ProxyVersion = "test-inject-proxy-version"
-	defaultConfig.Proxy.DebugImageVersion = "test-inject-debug-version"
-
-	emptyVersionConfig := testInstallConfig(ctx)
-	emptyVersionConfig.Global.Version = ""
-	emptyVersionConfig.Proxy.ProxyVersion = ""
-
-	emptyProxyVersionConfig := testInstallConfig(ctx)
-	emptyProxyVersionConfig.Global.Version = "test-inject-control-plane-version"
-	emptyProxyVersionConfig.Proxy.ProxyVersion = ""
-
-	overrideConfig := testInstallConfig(ctx)
-	overrideConfig.Proxy.ProxyVersion = "override"
+	defaultValues, err := testInstallValues()
+	if err != nil {
+		log.Fatalf("Unexpected error: %v", err)
+	}
+	defaultValues.Global.LinkerdVersion = "test-inject-control-plane-version"
+	defaultValues.Global.Proxy.Image.Version = "test-inject-proxy-version"
+	defaultValues.DebugContainer.Image.Version = "test-inject-debug-version"
 
-	proxyResourceConfig := testInstallConfig(ctx)
-	proxyResourceConfig.Proxy.ProxyVersion = defaultConfig.Proxy.ProxyVersion
-	proxyResourceConfig.Proxy.Resource = &config.ResourceRequirements{
-		RequestCpu:    "110m",
-		RequestMemory: "100Mi",
-		LimitCpu:      "160m",
-		LimitMemory:   "150Mi",
+	overrideConfig, err := testInstallValues()
+	if err != nil {
+		log.Fatalf("Unexpected error: %v", err)
 	}
+	overrideConfig.Global.Proxy.Image.Version = "override"
 
-	cniEnabledConfig := testInstallConfig(ctx)
-	cniEnabledConfig.Proxy.ProxyVersion = defaultConfig.Proxy.ProxyVersion
-	cniEnabledConfig.Global.CniEnabled = true
+	proxyResourceConfig, err := testInstallValues()
+	if err != nil {
+		log.Fatalf("Unexpected error: %v", err)
+	}
+	proxyResourceConfig.Global.Proxy.Image.Version = defaultValues.Global.Proxy.Image.Version
+	proxyResourceConfig.Global.Proxy.Resources = &linkerd2.Resources{
+		CPU: linkerd2.Constraints{
+			Request: "110m",
+			Limit:   "160m",
+		},
+		Memory: linkerd2.Constraints{
+			Request: "100Mi",
+			Limit:   "150Mi",
+		},
+	}
 
-	proxyIgnorePortsOptions, err := testInstallOptions()
+	cniEnabledConfig, err := testInstallValues()
 	if err != nil {
 		log.Fatalf("Unexpected error: %v", err)
 	}
-	proxyIgnorePortsOptions.ignoreInboundPorts = []string{"22", "8100-8102"}
-	proxyIgnorePortsOptions.ignoreOutboundPorts = []string{"5432"}
-	_, proxyIgnorePortsConfig, err := proxyIgnorePortsOptions.validateAndBuild(ctx, "", nil)
+	cniEnabledConfig.Global.Proxy.Image.Version = defaultValues.Global.Proxy.Image.Version
+	cniEnabledConfig.Global.CNIEnabled = true
+
+	proxyIgnorePortsConfig, err := testInstallValues()
 	if err != nil {
-		log.Fatalf("test install proxy-ignore options must be valid: %s", err)
+		log.Fatalf("Unexpected error: %v", err)
 	}
+	proxyIgnorePortsConfig.Global.ProxyInit.IgnoreInboundPorts = "22,8100-8102"
+	proxyIgnorePortsConfig.Global.ProxyInit.IgnoreOutboundPorts = "5432"
 
 	testCases := []testCase{
 		{
@@ -122,28 +122,14 @@ func TestUninjectAndInject(t *testing.T) {
 			goldenFileName:   "inject_emojivoto_deployment.golden.yml",
 			reportFileName:   "inject_emojivoto_deployment.report",
 			injectProxy:      true,
-			testInjectConfig: defaultConfig,
-		},
-		{
-			inputFileName:    "inject_emojivoto_deployment.input.yml",
-			goldenFileName:   "inject_emojivoto_deployment_empty_version_config.golden.yml",
-			reportFileName:   "inject_emojivoto_deployment.report",
-			injectProxy:      true,
-			testInjectConfig: emptyVersionConfig,
-		},
-		{
-			inputFileName:    "inject_emojivoto_deployment.input.yml",
-			goldenFileName:   "inject_emojivoto_deployment_empty_proxy_version_config.golden.yml",
-			reportFileName:   "inject_emojivoto_deployment.report",
-			injectProxy:      true,
-			testInjectConfig: emptyProxyVersionConfig,
+			testInjectConfig: defaultValues,
 		},
 		{
 			inputFileName:    "inject_emojivoto_deployment.input.yml",
 			goldenFileName:   "inject_emojivoto_deployment_overridden_noinject.golden.yml",
 			reportFileName:   "inject_emojivoto_deployment.report",
 			injectProxy:      false,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 			overrideAnnotations: map[string]string{
 				k8s.ProxyAdminPortAnnotation: "1234",
 			},
@@ -153,7 +139,7 @@ func TestUninjectAndInject(t *testing.T) {
 			goldenFileName:   "inject_emojivoto_deployment_overridden.golden.yml",
 			reportFileName:   "inject_emojivoto_deployment.report",
 			injectProxy:      true,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 			overrideAnnotations: map[string]string{
 				k8s.ProxyAdminPortAnnotation: "1234",
 			},
@@ -163,63 +149,63 @@ func TestUninjectAndInject(t *testing.T) {
 			goldenFileName:   "inject_emojivoto_list.golden.yml",
 			reportFileName:   "inject_emojivoto_list.report",
 			injectProxy:      true,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 		},
 		{
 			inputFileName:    "inject_emojivoto_deployment_hostNetwork_false.input.yml",
 			goldenFileName:   "inject_emojivoto_deployment_hostNetwork_false.golden.yml",
 			reportFileName:   "inject_emojivoto_deployment_hostNetwork_false.report",
 			injectProxy:      true,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 		},
 		{
 			inputFileName:    "inject_emojivoto_deployment_capabilities.input.yml",
 			goldenFileName:   "inject_emojivoto_deployment_capabilities.golden.yml",
 			reportFileName:   "inject_emojivoto_deployment.report",
 			injectProxy:      true,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 		},
 		{
 			inputFileName:    "inject_emojivoto_deployment_injectDisabled.input.yml",
 			goldenFileName:   "inject_emojivoto_deployment_injectDisabled.input.yml",
 			reportFileName:   "inject_emojivoto_deployment_injectDisabled.report",
 			injectProxy:      true,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 		},
 		{
 			inputFileName:    "inject_emojivoto_deployment_controller_name.input.yml",
 			goldenFileName:   "inject_emojivoto_deployment_controller_name.golden.yml",
 			reportFileName:   "inject_emojivoto_deployment_controller_name.report",
 			injectProxy:      true,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 		},
 		{
 			inputFileName:    "inject_emojivoto_statefulset.input.yml",
 			goldenFileName:   "inject_emojivoto_statefulset.golden.yml",
 			reportFileName:   "inject_emojivoto_statefulset.report",
 			injectProxy:      true,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 		},
 		{
 			inputFileName:    "inject_emojivoto_cronjob.input.yml",
 			goldenFileName:   "inject_emojivoto_cronjob.golden.yml",
 			reportFileName:   "inject_emojivoto_cronjob.report",
 			injectProxy:      false,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 		},
 		{
 			inputFileName:    "inject_emojivoto_cronjob_nometa.input.yml",
 			goldenFileName:   "inject_emojivoto_cronjob_nometa.golden.yml",
 			reportFileName:   "inject_emojivoto_cronjob.report",
 			injectProxy:      false,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 		},
 		{
 			inputFileName:    "inject_emojivoto_pod.input.yml",
 			goldenFileName:   "inject_emojivoto_pod.golden.yml",
 			reportFileName:   "inject_emojivoto_pod.report",
 			injectProxy:      true,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 		},
 		{
 			inputFileName:    "inject_emojivoto_pod_with_requests.input.yml",
@@ -233,35 +219,35 @@ func TestUninjectAndInject(t *testing.T) {
 			goldenFileName:   "inject_emojivoto_deployment_udp.golden.yml",
 			reportFileName:   "inject_emojivoto_deployment_udp.report",
 			injectProxy:      true,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 		},
 		{
 			inputFileName:    "inject_emojivoto_already_injected.input.yml",
 			goldenFileName:   "inject_emojivoto_already_injected.golden.yml",
 			reportFileName:   "inject_emojivoto_already_injected.report",
 			injectProxy:      true,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 		},
 		{
 			inputFileName:    "inject_contour.input.yml",
 			goldenFileName:   "inject_contour.golden.yml",
 			reportFileName:   "inject_contour.report",
 			injectProxy:      true,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 		},
 		{
 			inputFileName:    "inject_emojivoto_deployment_empty_resources.input.yml",
 			goldenFileName:   "inject_emojivoto_deployment_empty_resources.golden.yml",
 			reportFileName:   "inject_emojivoto_deployment_empty_resources.report",
 			injectProxy:      true,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 		},
 		{
 			inputFileName:    "inject_emojivoto_list_empty_resources.input.yml",
 			goldenFileName:   "inject_emojivoto_list_empty_resources.golden.yml",
 			reportFileName:   "inject_emojivoto_list_empty_resources.report",
 			injectProxy:      true,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 		},
 		{
 			inputFileName:    "inject_emojivoto_deployment.input.yml",
@@ -282,7 +268,7 @@ func TestUninjectAndInject(t *testing.T) {
 			goldenFileName:         "inject_emojivoto_deployment_debug.golden.yml",
 			reportFileName:         "inject_emojivoto_deployment.report",
 			injectProxy:            true,
-			testInjectConfig:       defaultConfig,
+			testInjectConfig:       defaultConfig(),
 			enableDebugSidecarFlag: true,
 		},
 		{
@@ -290,7 +276,7 @@ func TestUninjectAndInject(t *testing.T) {
 			goldenFileName:         "inject_tap_deployment_debug.golden.yml",
 			reportFileName:         "inject_tap_deployment_debug.report",
 			injectProxy:            true,
-			testInjectConfig:       defaultConfig,
+			testInjectConfig:       defaultConfig(),
 			enableDebugSidecarFlag: true,
 		},
 		{
@@ -298,14 +284,14 @@ func TestUninjectAndInject(t *testing.T) {
 			goldenFileName:   "inject_emojivoto_namespace_good.golden.yml",
 			reportFileName:   "inject_emojivoto_namespace_good.golden.report",
 			injectProxy:      false,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 		},
 		{
 			inputFileName:    "inject_emojivoto_namespace_good.input.yml",
 			goldenFileName:   "inject_emojivoto_namespace_overidden_good.golden.yml",
 			reportFileName:   "inject_emojivoto_namespace_good.golden.report",
 			injectProxy:      false,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 			overrideAnnotations: map[string]string{
 				k8s.IdentityModeAnnotation: "default",
 				k8s.CreatedByAnnotation:    "linkerd/cli dev-undefined",
@@ -330,7 +316,7 @@ func TestUninjectAndInject(t *testing.T) {
 			goldenFileName:   "inject_emojivoto_deployment_trace.golden.yml",
 			reportFileName:   "inject_emojivoto_deployment_trace.report",
 			injectProxy:      true,
-			testInjectConfig: defaultConfig,
+			testInjectConfig: defaultConfig(),
 			overrideAnnotations: map[string]string{
 				k8s.ProxyTraceCollectorSvcAddrAnnotation:    "linkerd-collector",
 				k8s.ProxyTraceCollectorSvcAccountAnnotation: "linkerd-collector.linkerd",
@@ -360,8 +346,11 @@ type injectCmd struct {
 }
 
 func testInjectCmd(t *testing.T, tc injectCmd) {
-	testConfig := testInstallConfig(context.Background())
-	testConfig.Proxy.ProxyVersion = "testinjectversion"
+	testConfig, err := testInstallValues()
+	if err != nil {
+		t.Fatalf("Unexpected error: %v", err)
+	}
+	testConfig.Global.Proxy.Image.Version = "testinjectversion"
 
 	errBuffer := &bytes.Buffer{}
 	outBuffer := &bytes.Buffer{}
@@ -373,7 +362,7 @@ func testInjectCmd(t *testing.T, tc injectCmd) {
 
 	transformer := &resourceTransformerInject{
 		injectProxy: tc.injectProxy,
-		configs:     testConfig,
+		values:      testConfig,
 	}
 	exitCode := runInjectCmd([]io.Reader{in}, errBuffer, outBuffer, transformer)
 	if exitCode != tc.exitCode {
@@ -453,7 +442,7 @@ type injectFilePath struct {
 	stdErrFile   string
 }
 
-func testInjectFilePath(ctx context.Context, t *testing.T, tc injectFilePath) {
+func testInjectFilePath(t *testing.T, tc injectFilePath) {
 	in, err := read("testdata/" + tc.resourceFile)
 	if err != nil {
 		t.Fatal("Unexpected error: ", err)
@@ -461,9 +450,13 @@ func testInjectFilePath(ctx context.Context, t *testing.T, tc injectFilePath) {
 
 	errBuf := &bytes.Buffer{}
 	actual := &bytes.Buffer{}
+	values, err := testInstallValues()
+	if err != nil {
+		t.Fatalf("Unexpected error: %v", err)
+	}
 	transformer := &resourceTransformerInject{
 		injectProxy: true,
-		configs:     testInstallConfig(ctx),
+		values:      values,
 	}
 	if exitCode := runInjectCmd(in, errBuf, actual, transformer); exitCode != 0 {
 		t.Fatal("Unexpected error. Exit code from runInjectCmd: ", exitCode)
@@ -474,17 +467,21 @@ func testInjectFilePath(ctx context.Context, t *testing.T, tc injectFilePath) {
 	diffTestdata(t, stdErrFile, errBuf.String())
 }
 
-func testReadFromFolder(ctx context.Context, t *testing.T, resourceFolder string, expectedFolder string) {
+func testReadFromFolder(t *testing.T, resourceFolder string, expectedFolder string) {
 	in, err := read("testdata/" + resourceFolder)
 	if err != nil {
 		t.Fatal("Unexpected error: ", err)
 	}
 
+	values, err := testInstallValues()
+	if err != nil {
+		t.Fatalf("Unexpected error: %v", err)
+	}
 	errBuf := &bytes.Buffer{}
 	actual := &bytes.Buffer{}
 	transformer := &resourceTransformerInject{
 		injectProxy: true,
-		configs:     testInstallConfig(ctx),
+		values:      values,
 	}
 	if exitCode := runInjectCmd(in, errBuf, actual, transformer); exitCode != 0 {
 		t.Fatal("Unexpected error. Exit code from runInjectCmd: ", exitCode)
@@ -503,8 +500,6 @@ func TestInjectFilePath(t *testing.T) {
 		expectedFolder = filepath.Join("inject-filepath", "expected")
 	)
 
-	ctx := context.Background()
-
 	t.Run("read from files", func(t *testing.T) {
 		testCases := []injectFilePath{
 			{
@@ -525,22 +520,22 @@ func TestInjectFilePath(t *testing.T) {
 			testCase := testCase // pin
 			verbose = true
 			t.Run(fmt.Sprintf("%d %s", i, testCase.resource), func(t *testing.T) {
-				testInjectFilePath(ctx, t, testCase)
+				testInjectFilePath(t, testCase)
 			})
 			verbose = false
 			t.Run(fmt.Sprintf("%d %s", i, testCase.resource), func(t *testing.T) {
-				testInjectFilePath(ctx, t, testCase)
+				testInjectFilePath(t, testCase)
 			})
 		}
 	})
 
 	verbose = true
 	t.Run("read from folder --verbose", func(t *testing.T) {
-		testReadFromFolder(ctx, t, resourceFolder, expectedFolder)
+		testReadFromFolder(t, resourceFolder, expectedFolder)
 	})
 	verbose = false
 	t.Run("read from folder --verbose", func(t *testing.T) {
-		testReadFromFolder(ctx, t, resourceFolder, expectedFolder)
+		testReadFromFolder(t, resourceFolder, expectedFolder)
 	})
 }
 
@@ -725,7 +720,10 @@ func TestOverrideConfigsParameterized(t *testing.T) {
 	for _, tt := range tests {
 		tt := tt // pin
 		t.Run(tt.description, func(t *testing.T) {
-			defaultConfig := testInstallConfig(context.Background())
+			defaultConfig, err := testInstallValues()
+			if err != nil {
+				t.Fatalf("Unexpected error: %v", err)
+			}
 			actualOverrides := map[string]string{}
 			tt.configOptions.overrideConfigs(defaultConfig, actualOverrides)
 			if len(tt.expectedOverrides) != len(actualOverrides) {
@@ -807,21 +805,18 @@ func TestOverrideConfigsWithCustomRegistryInstall(t *testing.T) {
 
 	// Setup the registry used when "installing" linkerd
 	customRegistryAtInstall := "custom.install.registry/linkerd-io"
-	installFlags := make([]*pb.Install_Flag, 0)
-	installFlags = append(installFlags, &pb.Install_Flag{
-		Name:  "registry",
-		Value: customRegistryAtInstall,
-	})
 
 	for _, tt := range tests {
 		tt := tt // pin
 		t.Run(tt.description, func(t *testing.T) {
 
-			defaultConfig := testInstallConfig(context.Background())
-			defaultConfig.Install.Flags = installFlags
-			defaultConfig.Proxy.ProxyImage.ImageName = customRegistryAtInstall + "/proxy"
-			defaultConfig.Proxy.ProxyInitImage.ImageName = customRegistryAtInstall + "/proxy-init"
-			defaultConfig.Proxy.DebugImage.ImageName = customRegistryAtInstall + "/debug"
+			defaultConfig, err := testInstallValues()
+			if err != nil {
+				t.Fatalf("Unexpected error: %v", err)
+			}
+			defaultConfig.Global.Proxy.Image.Name = customRegistryAtInstall + "/proxy"
+			defaultConfig.Global.ProxyInit.Image.Name = customRegistryAtInstall + "/proxy-init"
+			defaultConfig.DebugContainer.Image.Name = customRegistryAtInstall + "/debug"
 
 			actualOverrides := map[string]string{}
 			tt.configOptions.overrideConfigs(defaultConfig, actualOverrides)
diff --git a/cli/cmd/install_addon_test.go b/cli/cmd/install_addon_test.go
index e3eed32b9..84726ca10 100644
--- a/cli/cmd/install_addon_test.go
+++ b/cli/cmd/install_addon_test.go
@@ -20,7 +20,7 @@ func TestAddOnRender(t *testing.T) {
 
 	ctx := context.Background()
 
-	withTracingAddonValues, _, _ := withTracingAddon.validateAndBuild(ctx, "", nil)
+	withTracingAddonValues, _ := withTracingAddon.validateAndBuild(ctx, "", nil)
 	withTracingAddonValues.Tracing["enabled"] = true
 	addFakeTLSSecrets(withTracingAddonValues)
 
@@ -29,7 +29,7 @@ func TestAddOnRender(t *testing.T) {
 		t.Fatalf("Unexpected error: %v\n", err)
 	}
 	withTracingOverwrite.addOnConfig = filepath.Join("testdata", "addon_config_overwrite.yaml")
-	withTracingOverwriteValues, _, _ := withTracingOverwrite.validateAndBuild(ctx, "", nil)
+	withTracingOverwriteValues, _ := withTracingOverwrite.validateAndBuild(ctx, "", nil)
 	addFakeTLSSecrets(withTracingOverwriteValues)
 
 	withExistingGrafana, err := testInstallOptions()
@@ -37,7 +37,7 @@ func TestAddOnRender(t *testing.T) {
 		t.Fatalf("Unexpected error: %v\n", err)
 	}
 	withExistingGrafana.addOnConfig = filepath.Join("testdata", "existing-grafana-config.yaml")
-	withExistingGrafanaValues, _, _ := withExistingGrafana.validateAndBuild(ctx, "", nil)
+	withExistingGrafanaValues, _ := withExistingGrafana.validateAndBuild(ctx, "", nil)
 	addFakeTLSSecrets(withExistingGrafanaValues)
 
 	withPrometheusAddOnOverwrite, err := testInstallOptions()
@@ -45,7 +45,7 @@ func TestAddOnRender(t *testing.T) {
 		t.Fatalf("Unexpected error: %v\n", err)
 	}
 	withPrometheusAddOnOverwrite.addOnConfig = filepath.Join("testdata", "prom-config.yaml")
-	withPrometheusAddOnOverwriteValues, _, _ := withPrometheusAddOnOverwrite.validateAndBuild(ctx, "", nil)
+	withPrometheusAddOnOverwriteValues, _ := withPrometheusAddOnOverwrite.validateAndBuild(ctx, "", nil)
 	addFakeTLSSecrets(withPrometheusAddOnOverwriteValues)
 
 	testCases := []struct {
diff --git a/cli/cmd/install_test.go b/cli/cmd/install_test.go
index ebd6f7269..a9e23656c 100644
--- a/cli/cmd/install_test.go
+++ b/cli/cmd/install_test.go
@@ -4,10 +4,12 @@ import (
 	"bytes"
 	"context"
 	"fmt"
+	"io/ioutil"
 	"path/filepath"
 	"testing"
 
 	"github.com/linkerd/linkerd2/controller/gen/config"
+	"github.com/linkerd/linkerd2/pkg/charts/linkerd2"
 	charts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
 )
 
@@ -24,19 +26,19 @@ func TestRender(t *testing.T) {
 		t.Fatalf("Unexpected error: %v", err)
 	}
 
-	defaultValues, _, err := defaultOptions.validateAndBuild(ctx, "", nil)
+	defaultValues, err := defaultOptions.validateAndBuild(ctx, "", nil)
 	if err != nil {
 		t.Fatalf("Unexpected error validating options: %v", err)
 	}
 	addFakeTLSSecrets(defaultValues)
 
-	configValues, _, err := defaultOptions.validateAndBuild(ctx, configStage, nil)
+	configValues, err := defaultOptions.validateAndBuild(ctx, configStage, nil)
 	if err != nil {
 		t.Fatalf("Unexpected error validating options: %v", err)
 	}
 	addFakeTLSSecrets(configValues)
 
-	controlPlaneValues, _, err := defaultOptions.validateAndBuild(ctx, controlPlaneStage, nil)
+	controlPlaneValues, err := defaultOptions.validateAndBuild(ctx, controlPlaneStage, nil)
 	if err != nil {
 		t.Fatalf("Unexpected error validating options: %v", err)
 	}
@@ -175,7 +177,7 @@ func TestRender(t *testing.T) {
 
 	haOptions.recordedFlags = []*config.Install_Flag{{Name: "ha", Value: "true"}}
 	haOptions.highAvailability = true
-	haValues, _, _ := haOptions.validateAndBuild(ctx, "", nil)
+	haValues, _ := haOptions.validateAndBuild(ctx, "", nil)
 	addFakeTLSSecrets(haValues)
 
 	haWithOverridesOptions, err := testInstallOptions()
@@ -193,7 +195,7 @@ func TestRender(t *testing.T) {
 	haWithOverridesOptions.controllerReplicas = 2
 	haWithOverridesOptions.proxyCPURequest = "400m"
 	haWithOverridesOptions.proxyMemoryRequest = "300Mi"
-	haWithOverridesValues, _, _ := haWithOverridesOptions.validateAndBuild(ctx, "", nil)
+	haWithOverridesValues, _ := haWithOverridesOptions.validateAndBuild(ctx, "", nil)
 	addFakeTLSSecrets(haWithOverridesValues)
 
 	cniEnabledOptions, err := testInstallOptions()
@@ -203,7 +205,7 @@ func TestRender(t *testing.T) {
 
 	cniEnabledOptions.recordedFlags = []*config.Install_Flag{{Name: "linkerd-cni-enabled", Value: "true"}}
 	cniEnabledOptions.cniEnabled = true
-	cniEnabledValues, _, _ := cniEnabledOptions.validateAndBuild(ctx, "", nil)
+	cniEnabledValues, _ := cniEnabledOptions.validateAndBuild(ctx, "", nil)
 	addFakeTLSSecrets(cniEnabledValues)
 
 	withProxyIgnoresOptions, err := testInstallOptions()
@@ -212,7 +214,7 @@ func TestRender(t *testing.T) {
 	}
 	withProxyIgnoresOptions.ignoreInboundPorts = []string{"22", "8100-8102"}
 	withProxyIgnoresOptions.ignoreOutboundPorts = []string{"5432"}
-	withProxyIgnoresValues, _, _ := withProxyIgnoresOptions.validateAndBuild(ctx, "", nil)
+	withProxyIgnoresValues, _ := withProxyIgnoresOptions.validateAndBuild(ctx, "", nil)
 	addFakeTLSSecrets(withProxyIgnoresValues)
 
 	withHeartBeatDisabled, err := testInstallOptions()
@@ -220,7 +222,7 @@ func TestRender(t *testing.T) {
 		t.Fatalf("Unexpected error: %v\n", err)
 	}
 	withHeartBeatDisabled.disableHeartbeat = true
-	withHeartBeatDisabledValues, _, _ := withHeartBeatDisabled.validateAndBuild(ctx, "", nil)
+	withHeartBeatDisabledValues, _ := withHeartBeatDisabled.validateAndBuild(ctx, "", nil)
 	addFakeTLSSecrets(withHeartBeatDisabledValues)
 
 	withRestrictedDashboardPrivileges, err := testInstallOptions()
@@ -228,7 +230,7 @@ func TestRender(t *testing.T) {
 		t.Fatalf("Unexpected error: %v\n", err)
 	}
 	withRestrictedDashboardPrivileges.restrictDashboardPrivileges = true
-	withRestrictedDashboardPrivilegesValues, _, _ := withRestrictedDashboardPrivileges.validateAndBuild(ctx, "", nil)
+	withRestrictedDashboardPrivilegesValues, _ := withRestrictedDashboardPrivileges.validateAndBuild(ctx, "", nil)
 	addFakeTLSSecrets(withRestrictedDashboardPrivilegesValues)
 
 	withControlPlaneTracing, err := testInstallOptions()
@@ -236,7 +238,7 @@ func TestRender(t *testing.T) {
 		t.Fatalf("Unexpected error: %v\n", err)
 	}
 	withControlPlaneTracing.controlPlaneTracing = true
-	withControlPlaneTracingValues, _, _ := withControlPlaneTracing.validateAndBuild(ctx, "", nil)
+	withControlPlaneTracingValues, _ := withControlPlaneTracing.validateAndBuild(ctx, "", nil)
 	addFakeTLSSecrets(withControlPlaneTracingValues)
 
 	customRegistryOverride := "my.custom.registry/linkerd-io"
@@ -248,14 +250,14 @@ func TestRender(t *testing.T) {
 	withCustomRegistryOptions.recordedFlags = []*config.Install_Flag{
 		{Name: "registry", Value: customRegistryOverride},
 	}
-	withCustomRegistryValues, _, _ := withCustomRegistryOptions.validateAndBuild(ctx, "", nil)
+	withCustomRegistryValues, _ := withCustomRegistryOptions.validateAndBuild(ctx, "", nil)
 	addFakeTLSSecrets(withCustomRegistryValues)
 
 	withAddOnConfigStage, err := testInstallOptions()
 	if err != nil {
 		t.Fatalf("Unexpected error: %v\n", err)
 	}
-	withAddOnConfigStageValues, _, _ := withAddOnConfigStage.validateAndBuild(ctx, configStage, nil)
+	withAddOnConfigStageValues, _ := withAddOnConfigStage.validateAndBuild(ctx, configStage, nil)
 	withAddOnConfigStageValues.Tracing["enabled"] = true
 	addFakeTLSSecrets(withAddOnConfigStageValues)
 
@@ -263,7 +265,7 @@ func TestRender(t *testing.T) {
 	if err != nil {
 		t.Fatalf("Unexpected error: %v\n", err)
 	}
-	withAddOnControlPlaneStageValues, _, _ := withAddOnControlPlaneStage.validateAndBuild(ctx, controlPlaneStage, nil)
+	withAddOnControlPlaneStageValues, _ := withAddOnControlPlaneStage.validateAndBuild(ctx, controlPlaneStage, nil)
 	withAddOnControlPlaneStageValues.Tracing["enabled"] = true
 	addFakeTLSSecrets(withAddOnControlPlaneStageValues)
 
@@ -272,7 +274,7 @@ func TestRender(t *testing.T) {
 		t.Fatalf("Unexpected error: %v\n", err)
 	}
 	withCustomDestinationGetNets.destinationGetNetworks = []string{"10.0.0.0/8", "172.0.0.0/8"}
-	withCustomDestinationGetNetsValues, _, _ := withCustomDestinationGetNets.validateAndBuild(ctx, "", nil)
+	withCustomDestinationGetNetsValues, _ := withCustomDestinationGetNets.validateAndBuild(ctx, "", nil)
 	addFakeTLSSecrets(withCustomDestinationGetNetsValues)
 
 	testCases := []struct {
@@ -316,7 +318,7 @@ func TestValidateAndBuild_Errors(t *testing.T) {
 			t.Fatalf("Unexpected error: %v\n", err)
 		}
 		installOptions.ignoreInboundPorts = []string{"-25"}
-		_, _, err = installOptions.validateAndBuild(ctx, "", nil)
+		_, err = installOptions.validateAndBuild(ctx, "", nil)
 		if err == nil {
 			t.Fatal("expected error but got nothing")
 		}
@@ -328,7 +330,7 @@ func TestValidateAndBuild_Errors(t *testing.T) {
 			t.Fatalf("Unexpected error: %v\n", err)
 		}
 		installOptions.ignoreOutboundPorts = []string{"-25"}
-		_, _, err = installOptions.validateAndBuild(ctx, "", nil)
+		_, err = installOptions.validateAndBuild(ctx, "", nil)
 		if err == nil {
 			t.Fatal("expected error but got nothing")
 		}
@@ -352,6 +354,37 @@ func testInstallOptions() (*installOptions, error) {
 	return o, nil
 }
 
+func testInstallValues() (*linkerd2.Values, error) {
+	values, err := linkerd2.NewValues(false)
+	if err != nil {
+		return nil, err
+	}
+
+	values.Global.Proxy.Image.Version = installProxyVersion
+	values.DebugContainer.Image.Version = installDebugVersion
+	values.Global.LinkerdVersion = installControlPlaneVersion
+	values.Global.ControllerImageVersion = installControlPlaneVersion
+	values.HeartbeatSchedule = fakeHeartbeatSchedule()
+
+	identityCert, err := ioutil.ReadFile(filepath.Join("testdata", "valid-crt.pem"))
+	if err != nil {
+		return nil, err
+	}
+	identityKey, err := ioutil.ReadFile(filepath.Join("testdata", "valid-key.pem"))
+	if err != nil {
+		return nil, err
+	}
+	trustAnchorsPEM, err := ioutil.ReadFile(filepath.Join("testdata", "valid-trust-anchors.pem"))
+	if err != nil {
+		return nil, err
+	}
+
+	values.Identity.Issuer.TLS.CrtPEM = string(identityCert)
+	values.Identity.Issuer.TLS.KeyPEM = string(identityKey)
+	values.Global.IdentityTrustAnchorsPEM = string(trustAnchorsPEM)
+	return values, nil
+}
+
 func TestValidate(t *testing.T) {
 	t.Run("Accepts the default options as valid", func(t *testing.T) {
 		opts, err := testInstallOptions()
diff --git a/cli/cmd/testdata/inject-filepath/expected/injected_nginx.yaml b/cli/cmd/testdata/inject-filepath/expected/injected_nginx.yaml
index 70dea8b99..4644ed24a 100644
--- a/cli/cmd/testdata/inject-filepath/expected/injected_nginx.yaml
+++ b/cli/cmd/testdata/inject-filepath/expected/injected_nginx.yaml
@@ -35,6 +35,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject-filepath/expected/injected_nginx_redis.yaml b/cli/cmd/testdata/inject-filepath/expected/injected_nginx_redis.yaml
index 2cffe4a2c..627d7ab24 100644
--- a/cli/cmd/testdata/inject-filepath/expected/injected_nginx_redis.yaml
+++ b/cli/cmd/testdata/inject-filepath/expected/injected_nginx_redis.yaml
@@ -35,6 +35,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
@@ -202,6 +206,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject-filepath/expected/injected_redis.yaml b/cli/cmd/testdata/inject-filepath/expected/injected_redis.yaml
index a1b71ab2e..1d981a3b5 100644
--- a/cli/cmd/testdata/inject-filepath/expected/injected_redis.yaml
+++ b/cli/cmd/testdata/inject-filepath/expected/injected_redis.yaml
@@ -35,6 +35,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_contour.golden.yml b/cli/cmd/testdata/inject_contour.golden.yml
index 3540c6427..8d9847e3f 100644
--- a/cli/cmd/testdata/inject_contour.golden.yml
+++ b/cli/cmd/testdata/inject_contour.golden.yml
@@ -64,6 +64,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_already_injected.golden.yml b/cli/cmd/testdata/inject_emojivoto_already_injected.golden.yml
index 5a7fbecb6..8147c7deb 100644
--- a/cli/cmd/testdata/inject_emojivoto_already_injected.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_already_injected.golden.yml
@@ -46,6 +46,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
@@ -224,6 +228,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
@@ -402,6 +410,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
@@ -580,6 +592,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_deployment.golden.yml b/cli/cmd/testdata/inject_emojivoto_deployment.golden.yml
index d08a2cd8e..4bb4fd179 100644
--- a/cli/cmd/testdata/inject_emojivoto_deployment.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_deployment.golden.yml
@@ -46,6 +46,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_deployment_capabilities.golden.yml b/cli/cmd/testdata/inject_emojivoto_deployment_capabilities.golden.yml
index 887f5d7be..b2f708212 100644
--- a/cli/cmd/testdata/inject_emojivoto_deployment_capabilities.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_deployment_capabilities.golden.yml
@@ -54,6 +54,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_deployment_config_overrides.golden.yml b/cli/cmd/testdata/inject_emojivoto_deployment_config_overrides.golden.yml
index e21360099..8b8e1c3cb 100644
--- a/cli/cmd/testdata/inject_emojivoto_deployment_config_overrides.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_deployment_config_overrides.golden.yml
@@ -55,6 +55,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_deployment_controller_name.golden.yml b/cli/cmd/testdata/inject_emojivoto_deployment_controller_name.golden.yml
index d0807af63..35f1d8977 100644
--- a/cli/cmd/testdata/inject_emojivoto_deployment_controller_name.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_deployment_controller_name.golden.yml
@@ -46,6 +46,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
@@ -224,6 +228,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_deployment_debug.golden.yml b/cli/cmd/testdata/inject_emojivoto_deployment_debug.golden.yml
index fca734965..e261cab89 100644
--- a/cli/cmd/testdata/inject_emojivoto_deployment_debug.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_deployment_debug.golden.yml
@@ -51,6 +51,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_deployment_empty_proxy_version_config.golden.yml b/cli/cmd/testdata/inject_emojivoto_deployment_empty_proxy_version_config.golden.yml
deleted file mode 100644
index cbf6e8522..000000000
--- a/cli/cmd/testdata/inject_emojivoto_deployment_empty_proxy_version_config.golden.yml
+++ /dev/null
@@ -1,178 +0,0 @@
-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/identity-mode: default
-        linkerd.io/proxy-version: test-inject-control-plane-version
-      labels:
-        app: web-svc
-        linkerd.io/control-plane-ns: linkerd
-        linkerd.io/proxy-deployment: web
-        linkerd.io/workload-ns: emojivoto
-    spec:
-      containers:
-      - 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
-      - env:
-        - name: LINKERD2_PROXY_LOG
-          value: warn,linkerd=info
-        - 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_GET_NETWORKS
-          value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
-        - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
-          value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
-        - 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_DESTINATION_GET_SUFFIXES
-          value: svc.cluster.local.
-        - 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: _pod_ns
-          valueFrom:
-            fieldRef:
-              fieldPath: metadata.namespace
-        - name: _pod_nodeName
-          valueFrom:
-            fieldRef:
-              fieldPath: spec.nodeName
-        - name: LINKERD2_PROXY_DESTINATION_CONTEXT
-          value: |
-            {"ns":"$(_pod_ns)", "nodeName":"$(_pod_nodeName)"}
-        - 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/kubernetes.io/serviceaccount/token
-        - name: LINKERD2_PROXY_IDENTITY_SVC_ADDR
-          value: linkerd-identity-headless.linkerd.svc.cluster.local:8080
-        - name: _pod_sa
-          valueFrom:
-            fieldRef:
-              fieldPath: spec.serviceAccountName
-        - name: _l5d_ns
-          value: linkerd
-        - name: _l5d_trustdomain
-          value: cluster.local
-        - name: LINKERD2_PROXY_IDENTITY_LOCAL_NAME
-          value: $(_pod_sa).$(_pod_ns).serviceaccount.identity.$(_l5d_ns).$(_l5d_trustdomain)
-        - name: LINKERD2_PROXY_IDENTITY_SVC_NAME
-          value: linkerd-identity.$(_l5d_ns).serviceaccount.identity.$(_l5d_ns).$(_l5d_trustdomain)
-        - name: LINKERD2_PROXY_DESTINATION_SVC_NAME
-          value: linkerd-destination.$(_l5d_ns).serviceaccount.identity.$(_l5d_ns).$(_l5d_trustdomain)
-        - name: LINKERD2_PROXY_TAP_SVC_NAME
-          value: linkerd-tap.$(_l5d_ns).serviceaccount.identity.$(_l5d_ns).$(_l5d_trustdomain)
-        image: ghcr.io/linkerd/proxy:test-inject-control-plane-version
-        imagePullPolicy: IfNotPresent
-        livenessProbe:
-          httpGet:
-            path: /live
-            port: 4191
-          initialDelaySeconds: 10
-        name: linkerd-proxy
-        ports:
-        - containerPort: 4143
-          name: linkerd-proxy
-        - containerPort: 4191
-          name: linkerd-admin
-        readinessProbe:
-          httpGet:
-            path: /ready
-            port: 4191
-          initialDelaySeconds: 2
-        securityContext:
-          allowPrivilegeEscalation: false
-          readOnlyRootFilesystem: true
-          runAsUser: 2102
-        terminationMessagePolicy: FallbackToLogsOnError
-        volumeMounts:
-        - mountPath: /var/run/linkerd/identity/end-entity
-          name: linkerd-identity-end-entity
-      initContainers:
-      - args:
-        - --incoming-proxy-port
-        - "4143"
-        - --outgoing-proxy-port
-        - "4140"
-        - --proxy-uid
-        - "2102"
-        - --inbound-ports-to-ignore
-        - 4190,4191
-        image: ghcr.io/linkerd/proxy-init:v1.3.6
-        imagePullPolicy: IfNotPresent
-        name: linkerd-init
-        resources:
-          limits:
-            cpu: 100m
-            memory: 50Mi
-          requests:
-            cpu: 10m
-            memory: 10Mi
-        securityContext:
-          allowPrivilegeEscalation: false
-          capabilities:
-            add:
-            - NET_ADMIN
-            - NET_RAW
-          privileged: false
-          readOnlyRootFilesystem: true
-          runAsNonRoot: false
-          runAsUser: 0
-        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
----
diff --git a/cli/cmd/testdata/inject_emojivoto_deployment_empty_resources.golden.yml b/cli/cmd/testdata/inject_emojivoto_deployment_empty_resources.golden.yml
index 606e840ee..2a4d82c6b 100644
--- a/cli/cmd/testdata/inject_emojivoto_deployment_empty_resources.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_deployment_empty_resources.golden.yml
@@ -46,6 +46,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_deployment_empty_version_config.golden.yml b/cli/cmd/testdata/inject_emojivoto_deployment_empty_version_config.golden.yml
deleted file mode 100644
index a4cc25e2a..000000000
--- a/cli/cmd/testdata/inject_emojivoto_deployment_empty_version_config.golden.yml
+++ /dev/null
@@ -1,178 +0,0 @@
-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/identity-mode: default
-        linkerd.io/proxy-version: dev-undefined
-      labels:
-        app: web-svc
-        linkerd.io/control-plane-ns: linkerd
-        linkerd.io/proxy-deployment: web
-        linkerd.io/workload-ns: emojivoto
-    spec:
-      containers:
-      - 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
-      - env:
-        - name: LINKERD2_PROXY_LOG
-          value: warn,linkerd=info
-        - 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_GET_NETWORKS
-          value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
-        - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
-          value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
-        - 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_DESTINATION_GET_SUFFIXES
-          value: svc.cluster.local.
-        - 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: _pod_ns
-          valueFrom:
-            fieldRef:
-              fieldPath: metadata.namespace
-        - name: _pod_nodeName
-          valueFrom:
-            fieldRef:
-              fieldPath: spec.nodeName
-        - name: LINKERD2_PROXY_DESTINATION_CONTEXT
-          value: |
-            {"ns":"$(_pod_ns)", "nodeName":"$(_pod_nodeName)"}
-        - 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/kubernetes.io/serviceaccount/token
-        - name: LINKERD2_PROXY_IDENTITY_SVC_ADDR
-          value: linkerd-identity-headless.linkerd.svc.cluster.local:8080
-        - name: _pod_sa
-          valueFrom:
-            fieldRef:
-              fieldPath: spec.serviceAccountName
-        - name: _l5d_ns
-          value: linkerd
-        - name: _l5d_trustdomain
-          value: cluster.local
-        - name: LINKERD2_PROXY_IDENTITY_LOCAL_NAME
-          value: $(_pod_sa).$(_pod_ns).serviceaccount.identity.$(_l5d_ns).$(_l5d_trustdomain)
-        - name: LINKERD2_PROXY_IDENTITY_SVC_NAME
-          value: linkerd-identity.$(_l5d_ns).serviceaccount.identity.$(_l5d_ns).$(_l5d_trustdomain)
-        - name: LINKERD2_PROXY_DESTINATION_SVC_NAME
-          value: linkerd-destination.$(_l5d_ns).serviceaccount.identity.$(_l5d_ns).$(_l5d_trustdomain)
-        - name: LINKERD2_PROXY_TAP_SVC_NAME
-          value: linkerd-tap.$(_l5d_ns).serviceaccount.identity.$(_l5d_ns).$(_l5d_trustdomain)
-        image: ghcr.io/linkerd/proxy:dev-undefined
-        imagePullPolicy: IfNotPresent
-        livenessProbe:
-          httpGet:
-            path: /live
-            port: 4191
-          initialDelaySeconds: 10
-        name: linkerd-proxy
-        ports:
-        - containerPort: 4143
-          name: linkerd-proxy
-        - containerPort: 4191
-          name: linkerd-admin
-        readinessProbe:
-          httpGet:
-            path: /ready
-            port: 4191
-          initialDelaySeconds: 2
-        securityContext:
-          allowPrivilegeEscalation: false
-          readOnlyRootFilesystem: true
-          runAsUser: 2102
-        terminationMessagePolicy: FallbackToLogsOnError
-        volumeMounts:
-        - mountPath: /var/run/linkerd/identity/end-entity
-          name: linkerd-identity-end-entity
-      initContainers:
-      - args:
-        - --incoming-proxy-port
-        - "4143"
-        - --outgoing-proxy-port
-        - "4140"
-        - --proxy-uid
-        - "2102"
-        - --inbound-ports-to-ignore
-        - 4190,4191
-        image: ghcr.io/linkerd/proxy-init:v1.3.6
-        imagePullPolicy: IfNotPresent
-        name: linkerd-init
-        resources:
-          limits:
-            cpu: 100m
-            memory: 50Mi
-          requests:
-            cpu: 10m
-            memory: 10Mi
-        securityContext:
-          allowPrivilegeEscalation: false
-          capabilities:
-            add:
-            - NET_ADMIN
-            - NET_RAW
-          privileged: false
-          readOnlyRootFilesystem: true
-          runAsNonRoot: false
-          runAsUser: 0
-        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
----
diff --git a/cli/cmd/testdata/inject_emojivoto_deployment_hostNetwork_false.golden.yml b/cli/cmd/testdata/inject_emojivoto_deployment_hostNetwork_false.golden.yml
index f30375807..330aa9a2a 100644
--- a/cli/cmd/testdata/inject_emojivoto_deployment_hostNetwork_false.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_deployment_hostNetwork_false.golden.yml
@@ -47,6 +47,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_deployment_no_init_container.golden.yml b/cli/cmd/testdata/inject_emojivoto_deployment_no_init_container.golden.yml
index a50978b26..793fcc876 100644
--- a/cli/cmd/testdata/inject_emojivoto_deployment_no_init_container.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_deployment_no_init_container.golden.yml
@@ -46,6 +46,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_deployment_overridden.golden.yml b/cli/cmd/testdata/inject_emojivoto_deployment_overridden.golden.yml
index 19b4d1662..446d7d9b6 100644
--- a/cli/cmd/testdata/inject_emojivoto_deployment_overridden.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_deployment_overridden.golden.yml
@@ -47,6 +47,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_deployment_proxyignores.golden.yml b/cli/cmd/testdata/inject_emojivoto_deployment_proxyignores.golden.yml
index 5a5957c3a..cc43b71ac 100644
--- a/cli/cmd/testdata/inject_emojivoto_deployment_proxyignores.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_deployment_proxyignores.golden.yml
@@ -46,6 +46,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_deployment_trace.golden.yml b/cli/cmd/testdata/inject_emojivoto_deployment_trace.golden.yml
index 5c24df88e..f79479c9a 100644
--- a/cli/cmd/testdata/inject_emojivoto_deployment_trace.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_deployment_trace.golden.yml
@@ -48,6 +48,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_deployment_udp.golden.yml b/cli/cmd/testdata/inject_emojivoto_deployment_udp.golden.yml
index d7531e531..ac48eabc5 100644
--- a/cli/cmd/testdata/inject_emojivoto_deployment_udp.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_deployment_udp.golden.yml
@@ -48,6 +48,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_list.golden.yml b/cli/cmd/testdata/inject_emojivoto_list.golden.yml
index b069f2e4d..8a730075b 100644
--- a/cli/cmd/testdata/inject_emojivoto_list.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_list.golden.yml
@@ -48,6 +48,10 @@ items:
             value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
           - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
             value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+          - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+            value: 100ms
+          - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+            value: 1000ms
           - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
             value: 0.0.0.0:4190
           - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
@@ -220,6 +224,10 @@ items:
             value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
           - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
             value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+          - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+            value: 100ms
+          - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+            value: 1000ms
           - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
             value: 0.0.0.0:4190
           - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_list_empty_resources.golden.yml b/cli/cmd/testdata/inject_emojivoto_list_empty_resources.golden.yml
index 81ecf5d51..23db20696 100644
--- a/cli/cmd/testdata/inject_emojivoto_list_empty_resources.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_list_empty_resources.golden.yml
@@ -48,6 +48,10 @@ items:
             value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
           - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
             value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+          - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+            value: 100ms
+          - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+            value: 1000ms
           - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
             value: 0.0.0.0:4190
           - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
@@ -220,6 +224,10 @@ items:
             value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
           - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
             value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+          - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+            value: 100ms
+          - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+            value: 1000ms
           - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
             value: 0.0.0.0:4190
           - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_pod.golden.yml b/cli/cmd/testdata/inject_emojivoto_pod.golden.yml
index 8fb24f4d8..4e3fde831 100644
--- a/cli/cmd/testdata/inject_emojivoto_pod.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_pod.golden.yml
@@ -31,6 +31,10 @@ spec:
       value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
     - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
       value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+    - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+      value: 100ms
+    - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+      value: 1000ms
     - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
       value: 0.0.0.0:4190
     - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_pod_proxyignores.golden.yml b/cli/cmd/testdata/inject_emojivoto_pod_proxyignores.golden.yml
index 3f2ca7324..eaf5e78ea 100644
--- a/cli/cmd/testdata/inject_emojivoto_pod_proxyignores.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_pod_proxyignores.golden.yml
@@ -31,6 +31,10 @@ spec:
       value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
     - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
       value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+    - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+      value: 100ms
+    - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+      value: 1000ms
     - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
       value: 0.0.0.0:4190
     - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_pod_with_requests.golden.yml b/cli/cmd/testdata/inject_emojivoto_pod_with_requests.golden.yml
index 9f2080ed8..566075b90 100644
--- a/cli/cmd/testdata/inject_emojivoto_pod_with_requests.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_pod_with_requests.golden.yml
@@ -31,6 +31,10 @@ spec:
       value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
     - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
       value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+    - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+      value: 100ms
+    - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+      value: 1000ms
     - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
       value: 0.0.0.0:4190
     - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_emojivoto_statefulset.golden.yml b/cli/cmd/testdata/inject_emojivoto_statefulset.golden.yml
index 9466afa17..b2e34d67a 100644
--- a/cli/cmd/testdata/inject_emojivoto_statefulset.golden.yml
+++ b/cli/cmd/testdata/inject_emojivoto_statefulset.golden.yml
@@ -47,6 +47,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_gettest_deployment.good.golden.yml b/cli/cmd/testdata/inject_gettest_deployment.good.golden.yml
index a83b4c713..637b633be 100644
--- a/cli/cmd/testdata/inject_gettest_deployment.good.golden.yml
+++ b/cli/cmd/testdata/inject_gettest_deployment.good.golden.yml
@@ -48,6 +48,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
@@ -228,6 +232,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/testdata/inject_tap_deployment_debug.golden.yml b/cli/cmd/testdata/inject_tap_deployment_debug.golden.yml
index 1426d9ca7..fe9ed8e09 100644
--- a/cli/cmd/testdata/inject_tap_deployment_debug.golden.yml
+++ b/cli/cmd/testdata/inject_tap_deployment_debug.golden.yml
@@ -99,6 +99,10 @@ spec:
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/cli/cmd/upgrade_test.go b/cli/cmd/upgrade_test.go
index f98bd3ffa..42038881f 100644
--- a/cli/cmd/upgrade_test.go
+++ b/cli/cmd/upgrade_test.go
@@ -23,6 +23,7 @@ const (
 	upgradeControlPlaneVersion = "UPGRADE-CONTROL-PLANE-VERSION"
 	upgradeDebugVersion        = "UPGRADE-DEBUG-VERSION"
 	overridesSecret            = "Secret/linkerd-config-overrides"
+	LinkerdConfigMap           = "ConfigMap/linkerd-config"
 )
 
 type (
@@ -107,7 +108,7 @@ func TestUpgradeExternalIssuer(t *testing.T) {
 		},
 	}
 	installOpts.recordFlags(installFlags)
-	values, _, err := installOpts.validateAndBuildWithIdentity(context.Background(), "", &identity)
+	values, err := installOpts.validateAndBuildWithIdentity(context.Background(), "", &identity)
 	install := renderInstall(t, values)
 	if err != nil {
 		t.Fatalf("Failed to build install values: %s", err)
@@ -151,7 +152,7 @@ func TestUpgradeIssuerWithExternalIssuerFails(t *testing.T) {
 		},
 	}
 	installOpts.recordFlags(installFlags)
-	values, _, err := installOpts.validateAndBuildWithIdentity(context.Background(), "", &identity)
+	values, err := installOpts.validateAndBuildWithIdentity(context.Background(), "", &identity)
 	install := renderInstall(t, values)
 	if err != nil {
 		t.Fatalf("Failed to build install values: %s", err)
@@ -200,7 +201,7 @@ func TestUpgradeOverwriteIssuer(t *testing.T) {
 				// Trust root has changed.
 				continue
 			}
-			if id == "ConfigMap/linkerd-config" {
+			if id == LinkerdConfigMap {
 				// Trust root has changed.
 				continue
 			}
@@ -745,7 +746,7 @@ func pathMatch(path []string, template []string) bool {
 }
 
 func installValues(t *testing.T, installOpts *installOptions, installFlags *pflag.FlagSet) *linkerd2.Values {
-	installValues, _, err := installOpts.validateAndBuild(context.Background(), "", installFlags)
+	installValues, err := installOpts.validateAndBuild(context.Background(), "", installFlags)
 	if err != nil {
 		t.Fatalf("Unexpected error validating install options: %v", err)
 	}
@@ -796,7 +797,13 @@ func ignorableDiff(id string, diff diff) bool {
 		// control plane and proxy versions will change.
 		return true
 	}
-	if id == "ConfigMap/linkerd-config" && pathMatch(diff.path, []string{"data", "values"}) {
+
+	if id == LinkerdConfigMap && pathMatch(diff.path, []string{"data", "proxy"}) {
+		// Skip this as we are not planning to keep this for 2.9
+		return true
+	}
+
+	if id == LinkerdConfigMap && pathMatch(diff.path, []string{"data", "values"}) {
 		// The stored values will always change because at least the control
 		// plane and proxy versions will change.
 		return true
diff --git a/controller/proxy-injector/webhook_test.go b/controller/proxy-injector/webhook_test.go
index 83df77230..9af8a8a65 100644
--- a/controller/proxy-injector/webhook_test.go
+++ b/controller/proxy-injector/webhook_test.go
@@ -7,8 +7,8 @@ import (
 	"reflect"
 	"testing"
 
-	"github.com/linkerd/linkerd2/controller/gen/config"
 	"github.com/linkerd/linkerd2/controller/proxy-injector/fake"
+	"github.com/linkerd/linkerd2/pkg/charts/linkerd2"
 	"github.com/linkerd/linkerd2/pkg/inject"
 	pkgK8s "github.com/linkerd/linkerd2/pkg/k8s"
 	admissionv1beta1 "k8s.io/api/admission/v1beta1"
@@ -21,44 +21,24 @@ import (
 type unmarshalledPatch []map[string]interface{}
 
 var (
-	configs = &config.All{
-		Global: &config.Global{
-			LinkerdNamespace: "linkerd",
-			CniEnabled:       false,
-			IdentityContext:  nil,
-			ClusterDomain:    "cluster.local",
-		},
-		Proxy: &config.Proxy{
-			ProxyImage:              &config.Image{ImageName: "ghcr.io/linkerd/proxy", PullPolicy: "IfNotPresent"},
-			ProxyInitImage:          &config.Image{ImageName: "ghcr.io/linkerd/proxy-init", PullPolicy: "IfNotPresent"},
-			ControlPort:             &config.Port{Port: 4190},
-			IgnoreInboundPorts:      nil,
-			IgnoreOutboundPorts:     nil,
-			InboundPort:             &config.Port{Port: 4143},
-			AdminPort:               &config.Port{Port: 4191},
-			OutboundPort:            &config.Port{Port: 4140},
-			Resource:                &config.ResourceRequirements{RequestCpu: "", RequestMemory: "", LimitCpu: "", LimitMemory: ""},
-			ProxyUid:                2102,
-			LogLevel:                &config.LogLevel{Level: "warn,linkerd=info"},
-			DisableExternalProfiles: false,
-			DebugImage:              &config.Image{ImageName: "ghcr.io/linkerd/debug", PullPolicy: "IfNotPresent"},
-			DebugImageVersion:       "debug-image-version",
-			DestinationGetNetworks:  "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16",
-		},
-	}
+	values, _ = linkerd2.NewValues(false)
 )
 
 func confNsEnabled() *inject.ResourceConfig {
 	return inject.
-		NewResourceConfig(configs, inject.OriginWebhook).
+		NewResourceConfig(values, inject.OriginWebhook).
 		WithNsAnnotations(map[string]string{pkgK8s.ProxyInjectAnnotation: pkgK8s.ProxyInjectEnabled})
 }
 
 func confNsDisabled() *inject.ResourceConfig {
-	return inject.NewResourceConfig(configs, inject.OriginWebhook).WithNsAnnotations(map[string]string{})
+	return inject.NewResourceConfig(values, inject.OriginWebhook).WithNsAnnotations(map[string]string{})
 }
 
 func TestGetPatch(t *testing.T) {
+
+	values.Global.Proxy.DisableIdentity = true
+	values.Global.Proxy.DisableTap = true
+
 	factory := fake.NewFactory(filepath.Join("fake", "data"))
 	nsEnabled, err := factory.Namespace("namespace-inject-enabled.yaml")
 	if err != nil {
diff --git a/pkg/inject/inject_test.go b/pkg/inject/inject_test.go
index cb8fa11fc..935d76ab0 100644
--- a/pkg/inject/inject_test.go
+++ b/pkg/inject/inject_test.go
@@ -4,93 +4,34 @@ import (
 	"reflect"
 	"testing"
 
-	"github.com/linkerd/linkerd2/controller/gen/config"
 	l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 	"github.com/linkerd/linkerd2/pkg/version"
-	"github.com/linkerd/linkerd2/testutil"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"sigs.k8s.io/yaml"
 )
 
-type expectedProxyConfigs struct {
-	identityContext               *config.IdentityContext
-	image                         string
-	imagePullPolicy               string
-	proxyVersion                  string
-	controlPort                   int32
-	inboundPort                   int32
-	adminPort                     int32
-	outboundPort                  int32
-	proxyWaitBeforeExitSeconds    uint64
-	logLevel                      string
-	logFormat                     string
-	resourceRequirements          *l5dcharts.Resources
-	proxyUID                      int64
-	initImage                     string
-	initImagePullPolicy           string
-	initVersion                   string
-	inboundSkipPorts              string
-	outboundSkipPorts             string
-	requireIdentityOnInboundPorts string
-	destinationGetNetworks        string
-	outboundConnectTimeout        string
-	inboundConnectTimeout         string
-	trace                         *l5dcharts.Trace
-	opaquePorts                   string
-}
-
-func TestConfigAccessors(t *testing.T) {
-	// this test uses an annotated deployment and a proxyConfig object to verify
-	// all the proxy config accessors. The first test suite ensures that the
-	// accessors picks up the pod-level config annotations. The second test suite
-	// ensures that the defaults in the config map is used.
+func TestGetOverriddenValues(t *testing.T) {
+	// this test uses an annotated deployment and a expected Values object to verify
+	// the GetOverriddenValues function.
 
 	var (
-		controlPlaneVersion  = "control-plane-version"
-		proxyVersion         = "proxy-version"
 		proxyVersionOverride = "proxy-version-override"
+		pullPolicy           = "Always"
 	)
 
-	proxyConfig := &config.Proxy{
-		ProxyImage:          &config.Image{ImageName: "ghcr.io/linkerd/proxy", PullPolicy: "IfNotPresent"},
-		ProxyInitImage:      &config.Image{ImageName: "ghcr.io/linkerd/proxy-init", PullPolicy: "IfNotPresent"},
-		ControlPort:         &config.Port{Port: 9000},
-		InboundPort:         &config.Port{Port: 6000},
-		AdminPort:           &config.Port{Port: 6001},
-		OutboundPort:        &config.Port{Port: 6002},
-		IgnoreInboundPorts:  []*config.PortRange{{PortRange: "53,58-59"}},
-		IgnoreOutboundPorts: []*config.PortRange{{PortRange: "9079-9080"}},
-		Resource: &config.ResourceRequirements{
-			RequestCpu:    "0.2",
-			RequestMemory: "64",
-			LimitCpu:      "1",
-			LimitMemory:   "128",
-		},
-		ProxyUid:                8888,
-		LogLevel:                &config.LogLevel{Level: "info,linkerd2_proxy=debug"},
-		LogFormat:               "plain",
-		DisableExternalProfiles: false,
-		ProxyVersion:            proxyVersion,
-		DestinationGetNetworks:  "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16",
+	testConfig, err := l5dcharts.NewValues(false)
+	if err != nil {
+		t.Fatalf("Unexpected error: %v", err)
 	}
 
-	globalConfig := &config.Global{
-		LinkerdNamespace: "linkerd",
-		Version:          controlPlaneVersion,
-		IdentityContext:  &config.IdentityContext{},
-		ClusterDomain:    "cluster.local",
-	}
-
-	configs := &config.All{Global: globalConfig, Proxy: proxyConfig}
-
 	var testCases = []struct {
 		id            string
 		nsAnnotations map[string]string
 		spec          appsv1.DeploymentSpec
-		expected      expectedProxyConfigs
+		expected      func() *l5dcharts.Values
 	}{
 		{id: "use overrides",
 			spec: appsv1.DeploymentSpec{
@@ -99,7 +40,7 @@ func TestConfigAccessors(t *testing.T) {
 						Annotations: map[string]string{
 							k8s.ProxyDisableIdentityAnnotation:               "true",
 							k8s.ProxyImageAnnotation:                         "ghcr.io/linkerd/proxy",
-							k8s.ProxyImagePullPolicyAnnotation:               "Always",
+							k8s.ProxyImagePullPolicyAnnotation:               pullPolicy,
 							k8s.ProxyInitImageAnnotation:                     "ghcr.io/linkerd/proxy-init",
 							k8s.ProxyControlPortAnnotation:                   "4000",
 							k8s.ProxyInboundPortAnnotation:                   "5000",
@@ -129,18 +70,22 @@ func TestConfigAccessors(t *testing.T) {
 					Spec: corev1.PodSpec{},
 				},
 			},
-			expected: expectedProxyConfigs{
-				image:                      "ghcr.io/linkerd/proxy",
-				imagePullPolicy:            "Always",
-				proxyVersion:               proxyVersionOverride,
-				controlPort:                int32(4000),
-				inboundPort:                int32(5000),
-				adminPort:                  int32(5001),
-				outboundPort:               int32(5002),
-				proxyWaitBeforeExitSeconds: 123,
-				logLevel:                   "debug,linkerd2_proxy=debug",
-				logFormat:                  "json",
-				resourceRequirements: &l5dcharts.Resources{
+			expected: func() *l5dcharts.Values {
+				values, _ := l5dcharts.NewValues(false)
+
+				values.Global.Proxy.DisableIdentity = true
+				values.Global.Proxy.DestinationGetNetworks = "10.0.0.0/8"
+				values.Global.Proxy.Image.Name = "ghcr.io/linkerd/proxy"
+				values.Global.Proxy.Image.PullPolicy = pullPolicy
+				values.Global.Proxy.Image.Version = proxyVersionOverride
+				values.Global.Proxy.Ports.Control = 4000
+				values.Global.Proxy.Ports.Inbound = 5000
+				values.Global.Proxy.Ports.Admin = 5001
+				values.Global.Proxy.Ports.Outbound = 5002
+				values.Global.Proxy.WaitBeforeExitSeconds = 123
+				values.Global.Proxy.LogLevel = "debug,linkerd2_proxy=debug"
+				values.Global.Proxy.LogFormat = "json"
+				values.Global.Proxy.Resources = &l5dcharts.Resources{
 					CPU: l5dcharts.Constraints{
 						Limit:   "1500m",
 						Request: "150m",
@@ -149,22 +94,22 @@ func TestConfigAccessors(t *testing.T) {
 						Limit:   "256",
 						Request: "120",
 					},
-				},
-				proxyUID:            int64(8500),
-				initImage:           "ghcr.io/linkerd/proxy-init",
-				initImagePullPolicy: "Always",
-				initVersion:         version.ProxyInitVersion,
-				inboundSkipPorts:    "4222,6222",
-				outboundSkipPorts:   "8079,8080",
-				trace: &l5dcharts.Trace{
+				}
+				values.Global.Proxy.UID = 8500
+				values.Global.ProxyInit.Image.Name = "ghcr.io/linkerd/proxy-init"
+				values.Global.ProxyInit.Image.PullPolicy = pullPolicy
+				values.Global.ProxyInit.Image.Version = version.ProxyInitVersion
+				values.Global.ProxyInit.IgnoreInboundPorts = "4222,6222"
+				values.Global.ProxyInit.IgnoreOutboundPorts = "8079,8080"
+				values.Global.Proxy.Trace = &l5dcharts.Trace{
 					CollectorSvcAddr:    "oc-collector.tracing:55678",
 					CollectorSvcAccount: "default.tracing",
-				},
-				requireIdentityOnInboundPorts: "8888,9999",
-				destinationGetNetworks:        "10.0.0.0/8",
-				outboundConnectTimeout:        "6000ms",
-				inboundConnectTimeout:         "600ms",
-				opaquePorts:                   "4320,4321,4322,4323,4324,4325,3306",
+				}
+				values.Global.Proxy.RequireIdentityOnInboundPorts = "8888,9999"
+				values.Global.Proxy.OutboundConnectTimeout = "6000ms"
+				values.Global.Proxy.InboundConnectTimeout = "600ms"
+				values.Global.Proxy.OpaquePorts = "4320,4321,4322,4323,4324,4325,3306"
+				return values
 			},
 		},
 		{id: "use defaults",
@@ -174,44 +119,16 @@ func TestConfigAccessors(t *testing.T) {
 					Spec:       corev1.PodSpec{},
 				},
 			},
-			expected: expectedProxyConfigs{
-				identityContext:            &config.IdentityContext{},
-				image:                      "ghcr.io/linkerd/proxy",
-				imagePullPolicy:            "IfNotPresent",
-				proxyVersion:               proxyVersion,
-				controlPort:                int32(9000),
-				inboundPort:                int32(6000),
-				adminPort:                  int32(6001),
-				outboundPort:               int32(6002),
-				proxyWaitBeforeExitSeconds: 0,
-				logLevel:                   "info,linkerd2_proxy=debug",
-				logFormat:                  "plain",
-				resourceRequirements: &l5dcharts.Resources{
-					CPU: l5dcharts.Constraints{
-						Limit:   "1",
-						Request: "200m",
-					},
-					Memory: l5dcharts.Constraints{
-						Limit:   "128",
-						Request: "64",
-					},
-				},
-				proxyUID:               int64(8888),
-				initImage:              "ghcr.io/linkerd/proxy-init",
-				initImagePullPolicy:    "IfNotPresent",
-				initVersion:            version.ProxyInitVersion,
-				inboundSkipPorts:       "53,58-59",
-				outboundSkipPorts:      "9079-9080",
-				destinationGetNetworks: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16",
-				outboundConnectTimeout: "1000ms",
-				inboundConnectTimeout:  "100ms",
+			expected: func() *l5dcharts.Values {
+				values, _ := l5dcharts.NewValues(false)
+				return values
 			},
 		},
 		{id: "use namespace overrides",
 			nsAnnotations: map[string]string{
 				k8s.ProxyDisableIdentityAnnotation:          "true",
 				k8s.ProxyImageAnnotation:                    "ghcr.io/linkerd/proxy",
-				k8s.ProxyImagePullPolicyAnnotation:          "Always",
+				k8s.ProxyImagePullPolicyAnnotation:          pullPolicy,
 				k8s.ProxyInitImageAnnotation:                "ghcr.io/linkerd/proxy-init",
 				k8s.ProxyControlPortAnnotation:              "4000",
 				k8s.ProxyInboundPortAnnotation:              "5000",
@@ -240,18 +157,21 @@ func TestConfigAccessors(t *testing.T) {
 					Spec: corev1.PodSpec{},
 				},
 			},
-			expected: expectedProxyConfigs{
-				image:                      "ghcr.io/linkerd/proxy",
-				imagePullPolicy:            "Always",
-				proxyVersion:               proxyVersionOverride,
-				controlPort:                int32(4000),
-				inboundPort:                int32(5000),
-				adminPort:                  int32(5001),
-				outboundPort:               int32(5002),
-				proxyWaitBeforeExitSeconds: 123,
-				logLevel:                   "debug,linkerd2_proxy=debug",
-				logFormat:                  "json",
-				resourceRequirements: &l5dcharts.Resources{
+			expected: func() *l5dcharts.Values {
+				values, _ := l5dcharts.NewValues(false)
+
+				values.Global.Proxy.DisableIdentity = true
+				values.Global.Proxy.Image.Name = "ghcr.io/linkerd/proxy"
+				values.Global.Proxy.Image.PullPolicy = pullPolicy
+				values.Global.Proxy.Image.Version = proxyVersionOverride
+				values.Global.Proxy.Ports.Control = 4000
+				values.Global.Proxy.Ports.Inbound = 5000
+				values.Global.Proxy.Ports.Admin = 5001
+				values.Global.Proxy.Ports.Outbound = 5002
+				values.Global.Proxy.WaitBeforeExitSeconds = 123
+				values.Global.Proxy.LogLevel = "debug,linkerd2_proxy=debug"
+				values.Global.Proxy.LogFormat = "json"
+				values.Global.Proxy.Resources = &l5dcharts.Resources{
 					CPU: l5dcharts.Constraints{
 						Limit:   "1500m",
 						Request: "150m",
@@ -260,64 +180,21 @@ func TestConfigAccessors(t *testing.T) {
 						Limit:   "256",
 						Request: "120",
 					},
-				},
-				proxyUID:            int64(8500),
-				initImage:           "ghcr.io/linkerd/proxy-init",
-				initImagePullPolicy: "Always",
-				initVersion:         version.ProxyInitVersion,
-				inboundSkipPorts:    "4222,6222",
-				outboundSkipPorts:   "8079,8080",
-				trace: &l5dcharts.Trace{
+				}
+				values.Global.Proxy.UID = 8500
+				values.Global.ProxyInit.Image.Name = "ghcr.io/linkerd/proxy-init"
+				values.Global.ProxyInit.Image.PullPolicy = pullPolicy
+				values.Global.ProxyInit.Image.Version = version.ProxyInitVersion
+				values.Global.ProxyInit.IgnoreInboundPorts = "4222,6222"
+				values.Global.ProxyInit.IgnoreOutboundPorts = "8079,8080"
+				values.Global.Proxy.Trace = &l5dcharts.Trace{
 					CollectorSvcAddr:    "oc-collector.tracing:55678",
 					CollectorSvcAccount: "default.tracing",
-				},
-				destinationGetNetworks: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16",
-				outboundConnectTimeout: "6000ms",
-				inboundConnectTimeout:  "600ms",
-				opaquePorts:            "4320,4321,4322,4323,4324,4325,3306",
-			},
-		},
-		{id: "use not a uint value for ProxyWaitBeforeExitSecondsAnnotation annotation",
-			nsAnnotations: map[string]string{
-				k8s.ProxyWaitBeforeExitSecondsAnnotation: "-111",
-			},
-			spec: appsv1.DeploymentSpec{
-				Template: corev1.PodTemplateSpec{
-					ObjectMeta: metav1.ObjectMeta{},
-					Spec:       corev1.PodSpec{},
-				},
-			},
-			expected: expectedProxyConfigs{
-				identityContext:            &config.IdentityContext{},
-				image:                      "ghcr.io/linkerd/proxy",
-				imagePullPolicy:            "IfNotPresent",
-				proxyVersion:               proxyVersion,
-				controlPort:                int32(9000),
-				inboundPort:                int32(6000),
-				adminPort:                  int32(6001),
-				outboundPort:               int32(6002),
-				proxyWaitBeforeExitSeconds: 0,
-				logLevel:                   "info,linkerd2_proxy=debug",
-				logFormat:                  "plain",
-				resourceRequirements: &l5dcharts.Resources{
-					CPU: l5dcharts.Constraints{
-						Limit:   "1",
-						Request: "200m",
-					},
-					Memory: l5dcharts.Constraints{
-						Limit:   "128",
-						Request: "64",
-					},
-				},
-				proxyUID:               int64(8888),
-				initImage:              "ghcr.io/linkerd/proxy-init",
-				initImagePullPolicy:    "IfNotPresent",
-				initVersion:            version.ProxyInitVersion,
-				inboundSkipPorts:       "53,58-59",
-				outboundSkipPorts:      "9079-9080",
-				destinationGetNetworks: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16",
-				outboundConnectTimeout: "1000ms",
-				inboundConnectTimeout:  "100ms",
+				}
+				values.Global.Proxy.OutboundConnectTimeout = "6000ms"
+				values.Global.Proxy.InboundConnectTimeout = "600ms"
+				values.Global.Proxy.OpaquePorts = "4320,4321,4322,4323,4324,4325,3306"
+				return values
 			},
 		},
 		{id: "use empty string for dst networks",
@@ -330,37 +207,10 @@ func TestConfigAccessors(t *testing.T) {
 					Spec:       corev1.PodSpec{},
 				},
 			},
-			expected: expectedProxyConfigs{
-				identityContext:            &config.IdentityContext{},
-				image:                      "ghcr.io/linkerd/proxy",
-				imagePullPolicy:            "IfNotPresent",
-				proxyVersion:               proxyVersion,
-				controlPort:                int32(9000),
-				inboundPort:                int32(6000),
-				adminPort:                  int32(6001),
-				outboundPort:               int32(6002),
-				proxyWaitBeforeExitSeconds: 0,
-				logLevel:                   "info,linkerd2_proxy=debug",
-				logFormat:                  "plain",
-				resourceRequirements: &l5dcharts.Resources{
-					CPU: l5dcharts.Constraints{
-						Limit:   "1",
-						Request: "200m",
-					},
-					Memory: l5dcharts.Constraints{
-						Limit:   "128",
-						Request: "64",
-					},
-				},
-				proxyUID:               int64(8888),
-				initImage:              "ghcr.io/linkerd/proxy-init",
-				initImagePullPolicy:    "IfNotPresent",
-				initVersion:            version.ProxyInitVersion,
-				inboundSkipPorts:       "53,58-59",
-				outboundSkipPorts:      "9079-9080",
-				destinationGetNetworks: "",
-				outboundConnectTimeout: "1000ms",
-				inboundConnectTimeout:  "100ms",
+			expected: func() *l5dcharts.Values {
+				values, _ := l5dcharts.NewValues(false)
+				values.Global.Proxy.DestinationGetNetworks = ""
+				return values
 			},
 		},
 		{id: "use invalid duration for TCP connect timeouts",
@@ -374,44 +224,16 @@ func TestConfigAccessors(t *testing.T) {
 					Spec:       corev1.PodSpec{},
 				},
 			},
-			expected: expectedProxyConfigs{
-				identityContext:            &config.IdentityContext{},
-				image:                      "ghcr.io/linkerd/proxy",
-				imagePullPolicy:            "IfNotPresent",
-				proxyVersion:               proxyVersion,
-				controlPort:                int32(9000),
-				inboundPort:                int32(6000),
-				adminPort:                  int32(6001),
-				outboundPort:               int32(6002),
-				proxyWaitBeforeExitSeconds: 0,
-				logLevel:                   "info,linkerd2_proxy=debug",
-				logFormat:                  "plain",
-				resourceRequirements: &l5dcharts.Resources{
-					CPU: l5dcharts.Constraints{
-						Limit:   "1",
-						Request: "200m",
-					},
-					Memory: l5dcharts.Constraints{
-						Limit:   "128",
-						Request: "64",
-					},
-				},
-				proxyUID:               int64(8888),
-				initImage:              "ghcr.io/linkerd/proxy-init",
-				initImagePullPolicy:    "IfNotPresent",
-				initVersion:            version.ProxyInitVersion,
-				inboundSkipPorts:       "53,58-59",
-				outboundSkipPorts:      "9079-9080",
-				destinationGetNetworks: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16",
-				outboundConnectTimeout: "1000ms",
-				inboundConnectTimeout:  "100ms",
+			expected: func() *l5dcharts.Values {
+				values, _ := l5dcharts.NewValues(false)
+				return values
 			},
 		},
 		{id: "use valid duration for TCP connect timeouts",
 			nsAnnotations: map[string]string{
 				// Validate we're converting time values into ms for the proxy to parse correctly.
 				k8s.ProxyOutboundConnectTimeout: "6s5ms",
-				k8s.ProxyInboundConnectTimeout:  "1s5ms",
+				k8s.ProxyInboundConnectTimeout:  "2s5ms",
 			},
 			spec: appsv1.DeploymentSpec{
 				Template: corev1.PodTemplateSpec{
@@ -419,37 +241,11 @@ func TestConfigAccessors(t *testing.T) {
 					Spec:       corev1.PodSpec{},
 				},
 			},
-			expected: expectedProxyConfigs{
-				identityContext:            &config.IdentityContext{},
-				image:                      "ghcr.io/linkerd/proxy",
-				imagePullPolicy:            "IfNotPresent",
-				proxyVersion:               proxyVersion,
-				controlPort:                int32(9000),
-				inboundPort:                int32(6000),
-				adminPort:                  int32(6001),
-				outboundPort:               int32(6002),
-				proxyWaitBeforeExitSeconds: 0,
-				logLevel:                   "info,linkerd2_proxy=debug",
-				logFormat:                  "plain",
-				resourceRequirements: &l5dcharts.Resources{
-					CPU: l5dcharts.Constraints{
-						Limit:   "1",
-						Request: "200m",
-					},
-					Memory: l5dcharts.Constraints{
-						Limit:   "128",
-						Request: "64",
-					},
-				},
-				proxyUID:               int64(8888),
-				initImage:              "ghcr.io/linkerd/proxy-init",
-				initImagePullPolicy:    "IfNotPresent",
-				initVersion:            version.ProxyInitVersion,
-				inboundSkipPorts:       "53,58-59",
-				outboundSkipPorts:      "9079-9080",
-				destinationGetNetworks: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16",
-				outboundConnectTimeout: "6005ms",
-				inboundConnectTimeout:  "1005ms",
+			expected: func() *l5dcharts.Values {
+				values, _ := l5dcharts.NewValues(false)
+				values.Global.Proxy.OutboundConnectTimeout = "6005ms"
+				values.Global.Proxy.InboundConnectTimeout = "2005ms"
+				return values
 			},
 		},
 		{id: "use named port for opaque ports",
@@ -474,38 +270,10 @@ func TestConfigAccessors(t *testing.T) {
 					},
 				},
 			},
-			expected: expectedProxyConfigs{
-				identityContext:            &config.IdentityContext{},
-				image:                      "ghcr.io/linkerd/proxy",
-				imagePullPolicy:            "IfNotPresent",
-				proxyVersion:               proxyVersion,
-				controlPort:                int32(9000),
-				inboundPort:                int32(6000),
-				adminPort:                  int32(6001),
-				outboundPort:               int32(6002),
-				proxyWaitBeforeExitSeconds: 0,
-				logLevel:                   "info,linkerd2_proxy=debug",
-				logFormat:                  "plain",
-				resourceRequirements: &l5dcharts.Resources{
-					CPU: l5dcharts.Constraints{
-						Limit:   "1",
-						Request: "200m",
-					},
-					Memory: l5dcharts.Constraints{
-						Limit:   "128",
-						Request: "64",
-					},
-				},
-				proxyUID:               int64(8888),
-				initImage:              "ghcr.io/linkerd/proxy-init",
-				initImagePullPolicy:    "IfNotPresent",
-				initVersion:            version.ProxyInitVersion,
-				inboundSkipPorts:       "53,58-59",
-				outboundSkipPorts:      "9079-9080",
-				destinationGetNetworks: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16",
-				outboundConnectTimeout: "1000ms",
-				inboundConnectTimeout:  "100ms",
-				opaquePorts:            "3306",
+			expected: func() *l5dcharts.Values {
+				values, _ := l5dcharts.NewValues(false)
+				values.Global.Proxy.OpaquePorts = "3306"
+				return values
 			},
 		},
 	}
@@ -518,168 +286,20 @@ func TestConfigAccessors(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			resourceConfig := NewResourceConfig(configs, OriginUnknown).WithKind("Deployment").WithNsAnnotations(testCase.nsAnnotations)
+			resourceConfig := NewResourceConfig(testConfig, OriginUnknown).WithKind("Deployment").WithNsAnnotations(testCase.nsAnnotations)
 			if err := resourceConfig.parse(data); err != nil {
 				t.Fatal(err)
 			}
 
-			t.Run("identityContext", func(t *testing.T) {
-				expected := testCase.expected.identityContext
-				if actual := resourceConfig.identityContext(); !reflect.DeepEqual(expected, actual) {
-					testutil.Errorf(t, "Expected: %+v Actual: %+v", expected, actual)
-				}
-			})
-
-			t.Run("proxyImage", func(t *testing.T) {
-				expected := testCase.expected.image
-				if actual := resourceConfig.proxyImage(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyImagePullPolicy", func(t *testing.T) {
-				expected := testCase.expected.imagePullPolicy
-				if actual := resourceConfig.proxyImagePullPolicy(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyVersion", func(t *testing.T) {
-				expected := testCase.expected.proxyVersion
-				if actual := resourceConfig.proxyVersion(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyInitVersion", func(t *testing.T) {
-				expected := testCase.expected.initVersion
-				if actual := resourceConfig.proxyInitVersion(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyControlPort", func(t *testing.T) {
-				expected := testCase.expected.controlPort
-				if actual := resourceConfig.proxyControlPort(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyInboundPort", func(t *testing.T) {
-				expected := testCase.expected.inboundPort
-				if actual := resourceConfig.proxyInboundPort(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyAdminPort", func(t *testing.T) {
-				expected := testCase.expected.adminPort
-				if actual := resourceConfig.proxyAdminPort(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyOutboundPort", func(t *testing.T) {
-				expected := testCase.expected.outboundPort
-				if actual := resourceConfig.proxyOutboundPort(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyWaitBeforeExitSeconds", func(t *testing.T) {
-				expected := testCase.expected.proxyWaitBeforeExitSeconds
-				if actual := resourceConfig.proxyWaitBeforeExitSeconds(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyLogLevel", func(t *testing.T) {
-				expected := testCase.expected.logLevel
-				if actual := resourceConfig.proxyLogLevel(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyResourceRequirements", func(t *testing.T) {
-				expected := testCase.expected.resourceRequirements
-				if actual := resourceConfig.proxyResourceRequirements(); !reflect.DeepEqual(expected, actual) {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyUID", func(t *testing.T) {
-				expected := testCase.expected.proxyUID
-				if actual := resourceConfig.proxyUID(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyInitImage", func(t *testing.T) {
-				expected := testCase.expected.initImage
-				if actual := resourceConfig.proxyInitImage(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyInitImagePullPolicy", func(t *testing.T) {
-				expected := testCase.expected.initImagePullPolicy
-				if actual := resourceConfig.proxyInitImagePullPolicy(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyInboundSkipPorts", func(t *testing.T) {
-				expected := testCase.expected.inboundSkipPorts
-				if actual := resourceConfig.proxyInboundSkipPorts(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyOutboundSkipPorts", func(t *testing.T) {
-				expected := testCase.expected.outboundSkipPorts
-				if actual := resourceConfig.proxyOutboundSkipPorts(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyRequireIdentityOnInboundPorts", func(t *testing.T) {
-				expected := testCase.expected.requireIdentityOnInboundPorts
-				if actual := resourceConfig.requireIdentityOnInboundPorts(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("destinationGetNetworks", func(t *testing.T) {
-				expected := testCase.expected.destinationGetNetworks
-				if actual := resourceConfig.destinationGetNetworks(); expected != actual {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
-
-			t.Run("proxyTraceCollectorService", func(t *testing.T) {
-				var expected *l5dcharts.Trace
-				if testCase.expected.trace != nil {
-					expected = &l5dcharts.Trace{
-						CollectorSvcAddr:    testCase.expected.trace.CollectorSvcAddr,
-						CollectorSvcAccount: testCase.expected.trace.CollectorSvcAccount,
-					}
-				}
-
-				if actual := resourceConfig.trace(); !reflect.DeepEqual(expected, actual) {
-					t.Errorf("Expected: %+v Actual: %+v", expected, actual)
-				}
-			})
+			actual, err := resourceConfig.GetOverriddenValues()
+			if err != nil {
+				t.Fatal(err)
+			}
+			expected := testCase.expected()
+			if !reflect.DeepEqual(actual, expected) {
+				t.Fatalf("Expected values to be \n%v\n but was \n%v", actual, expected)
+			}
 
-			t.Run("opaquePorts", func(t *testing.T) {
-				expected := testCase.expected.opaquePorts
-				actual, err := resourceConfig.proxyOpaquePorts()
-				if err != nil {
-					t.Fatal(err)
-				}
-				if actual != expected {
-					t.Errorf("Expected: %v Actual: %v", expected, actual)
-				}
-			})
 		})
 	}
 }
diff --git a/test/integration/inject/testdata/injected_default.golden b/test/integration/inject/testdata/injected_default.golden
index ba8cebf05..1ac708989 100644
--- a/test/integration/inject/testdata/injected_default.golden
+++ b/test/integration/inject/testdata/injected_default.golden
@@ -44,6 +44,10 @@ spec:
           value: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:4190
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR
diff --git a/test/integration/inject/testdata/injected_params.golden b/test/integration/inject/testdata/injected_params.golden
index 6573b42f6..4f8a8aaed 100644
--- a/test/integration/inject/testdata/injected_params.golden
+++ b/test/integration/inject/testdata/injected_params.golden
@@ -59,6 +59,10 @@ spec:
           value: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
         - name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS
           value: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
+        - name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT
+          value: 100ms
+        - name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT
+          value: 1000ms
         - name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR
           value: 0.0.0.0:123
         - name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR