Repository: linkerd/linkerd2
Pull request #7419 (2021-12-06 20:59:03)

Fuzzing: Move fuzzers upstream
Moves fuzzers upstream as requested in https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=42012#c1 @olix0r
<!--  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
-->


stale[bot]: This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.

kleimkuhler: Once this merges, google/oss-fuzz#7609 changes the fuzzers location from cncf/cncf-fuzzing to linkerd2. Additionally, cncf/cncf-fuzzing#153 removes the fuzzers from that repository now that they are located here.
kleimkuhler: I've also added a fuzzer README which explains that this is used by oss-fuzz and there is not currently a script for running these locally.

Patch:
diff --git a/.golangci.yml b/.golangci.yml
index 6a32c8047..6f1d958c3 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -118,10 +118,16 @@ issues:
     path: .*test.*
     text: "G204: Subprocess launched with .*"
 
-  # In tests we are usually mocking components or have a good idea about the
-  # errors that we expect. For this reason, we ignore unchecked errors in
-  # all test files.
-  - path: .*test.*\.go
+  # Test/fuzzing do not need to be checked for file handle cleanup.
+  - linters:
+    - gosec
+    path: .*(test|fuzzer).*\.go
+    text: Deferring unsafe method \"Close\" on type \"\*os\.File\"
+
+  # In tests/fuzzing we are usually mocking components or have a good idea
+  # about the errors that we expect. For this reason, we ignore unchecked
+  # errors in all test files.
+  - path: .*(test|fuzzer).*\.go
     text: Error return value of `.*` is not checked
 
   # In tests we'll ignore unchecked filename operations because the values
diff --git a/Cargo.lock b/Cargo.lock
index 955822f72..a0bec7f69 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1896,9 +1896,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-rustls"
-version = "0.23.3"
+version = "0.23.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4151fda0cf2798550ad0b34bcfc9b9dcc2a9d2471c895c68f3a8818e54f2389e"
+checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
 dependencies = [
  "rustls",
  "tokio",
diff --git a/controller/api/destination/destination_fuzzer.go b/controller/api/destination/destination_fuzzer.go
new file mode 100644
index 000000000..ff774763f
--- /dev/null
+++ b/controller/api/destination/destination_fuzzer.go
@@ -0,0 +1,100 @@
+package destination
+
+import (
+	"testing"
+
+	fuzz "github.com/AdaLogics/go-fuzz-headers"
+	pb "github.com/linkerd/linkerd2-proxy-api/go/destination"
+	"github.com/linkerd/linkerd2/controller/api/destination/watcher"
+	"github.com/linkerd/linkerd2/controller/api/util"
+	sp "github.com/linkerd/linkerd2/controller/gen/apis/serviceprofile/v1alpha2"
+	logging "github.com/sirupsen/logrus"
+)
+
+func init() {
+	testing.Init()
+}
+
+// FuzzAdd fuzzes the Add method of the destination server.
+func FuzzAdd(data []byte) int {
+	f := fuzz.NewConsumer(data)
+	set := watcher.AddressSet{}
+	err := f.GenerateStruct(&set)
+	if err != nil {
+		return 0
+	}
+	t := &testing.T{}
+	_, translator := makeEndpointTranslator(t)
+	translator.Add(set)
+	translator.Remove(set)
+	return 1
+}
+
+// FuzzGet fuzzes the Get method of the destination server.
+func FuzzGet(data []byte) int {
+	f := fuzz.NewConsumer(data)
+	dest1 := &pb.GetDestination{}
+	err := f.GenerateStruct(dest1)
+	if err != nil {
+		return 0
+	}
+	dest2 := &pb.GetDestination{}
+	err = f.GenerateStruct(dest2)
+	if err != nil {
+		return 0
+	}
+	dest3 := &pb.GetDestination{}
+	err = f.GenerateStruct(dest3)
+	if err != nil {
+		return 0
+	}
+	t := &testing.T{}
+	server := makeServer(t)
+
+	stream := &bufferingGetStream{
+		updates:          []*pb.Update{},
+		MockServerStream: util.NewMockServerStream(),
+	}
+	_ = server.Get(dest1, stream)
+	_ = server.Get(dest2, stream)
+	_ = server.Get(dest3, stream)
+	return 1
+}
+
+// FuzzGetProfile fuzzes the GetProfile method of the destination server.
+func FuzzGetProfile(data []byte) int {
+	f := fuzz.NewConsumer(data)
+	dest := &pb.GetDestination{}
+	err := f.GenerateStruct(dest)
+	if err != nil {
+		return 0
+	}
+	t := &testing.T{}
+	server := makeServer(t)
+	stream := &bufferingGetProfileStream{
+		updates:          []*pb.DestinationProfile{},
+		MockServerStream: util.NewMockServerStream(),
+	}
+	stream.Cancel()
+	_ = server.GetProfile(dest, stream)
+	return 1
+}
+
+// FuzzProfileTranslatorUpdate fuzzes the Update method of the profile translator.
+func FuzzProfileTranslatorUpdate(data []byte) int {
+	f := fuzz.NewConsumer(data)
+	profile := &sp.ServiceProfile{}
+	err := f.GenerateStruct(profile)
+	if err != nil {
+		return 0
+	}
+	t := &testing.T{}
+	mockGetProfileServer := &mockDestinationGetProfileServer{profilesReceived: []*pb.DestinationProfile{}}
+
+	translator := &profileTranslator{
+		stream: mockGetProfileServer,
+		log:    logging.WithField("test", t.Name()),
+	}
+	translator.Update(profile)
+	return 1
+}
diff --git a/go.mod b/go.mod
index 484db3345..2f6aac238 100644
--- a/go.mod
+++ b/go.mod
@@ -55,6 +55,7 @@ require (
 
 require (
 	cloud.google.com/go v0.99.0 // indirect
+	github.com/AdaLogics/go-fuzz-headers v0.0.0-20220408101031-f1761e18c0c6 // indirect
 	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
 	github.com/Azure/go-autorest v14.2.0+incompatible // indirect
 	github.com/Azure/go-autorest/autorest v0.11.20 // indirect
@@ -70,10 +71,11 @@ require (
 	github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
 	github.com/containerd/containerd v1.6.1 // indirect
+	github.com/containerd/stargz-snapshotter/estargz v0.10.1 // indirect
 	github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
 	github.com/cyphar/filepath-securejoin v0.2.3 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
-	github.com/docker/cli v20.10.11+incompatible // indirect
+	github.com/docker/cli v20.10.12+incompatible // indirect
 	github.com/docker/distribution v2.7.1+incompatible // indirect
 	github.com/docker/docker v20.10.12+incompatible // indirect
 	github.com/docker/docker-credential-helpers v0.6.4 // indirect
@@ -94,6 +96,7 @@ require (
 	github.com/google/btree v1.0.1 // indirect
 	github.com/google/gnostic v0.5.7-v3refs // indirect
 	github.com/google/go-cmp v0.5.6 // indirect
+	github.com/google/go-containerregistry v0.7.0 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
 	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
 	github.com/google/uuid v1.2.0 // indirect
@@ -131,6 +134,7 @@ require (
 	github.com/shopspring/decimal v1.2.0 // indirect
 	github.com/spf13/cast v1.4.1 // indirect
 	github.com/stretchr/testify v1.7.0 // indirect
+	github.com/vbatts/tar-split v0.11.2 // indirect
 	github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
 	github.com/xeipuuv/gojsonschema v1.2.0 // indirect
diff --git a/go.sum b/go.sum
index 1cd61bc74..f06173ac6 100644
--- a/go.sum
+++ b/go.sum
@@ -53,6 +53,8 @@ contrib.go.opencensus.io/exporter/ocagent v0.7.0 h1:BEfdCTXfMV30tLZD8c9n64V/tIZX
 contrib.go.opencensus.io/exporter/ocagent v0.7.0/go.mod h1:IshRmMJBhDfFj5Y67nVhMYTTIze91RUeT73ipWKs/GY=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg=
+github.com/AdaLogics/go-fuzz-headers v0.0.0-20220408101031-f1761e18c0c6 h1:l+ujeiNp0cAxa7aU5Cw1ufjaxu4FfpuyeW1DQhV21Cc=
+github.com/AdaLogics/go-fuzz-headers v0.0.0-20220408101031-f1761e18c0c6/go.mod h1:mHEIrBMD2IgTJphw1R5sfgwN/Xg/q8IPOUlb90leKyk=
 github.com/Azure/azure-sdk-for-go v56.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
 github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
@@ -216,6 +218,9 @@ github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDX
 github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4=
 github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
 github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM=
+github.com/containerd/stargz-snapshotter/estargz v0.10.0/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0=
+github.com/containerd/stargz-snapshotter/estargz v0.10.1 h1:hd1EoVjI2Ax8Cr64tdYqnJ4i4pZU49FkEf5kU8KxQng=
+github.com/containerd/stargz-snapshotter/estargz v0.10.1/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0=
 github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
 github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ=
 github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s=
@@ -265,11 +270,15 @@ github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684 h1:DB
 github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684/go.mod h1:UfCu3YXJJCI+IdnqGgYP82dk2+Joxmv+mUTVBES6wac=
 github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
 github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/cli v20.10.10+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/cli v20.10.11+incompatible h1:tXU1ezXcruZQRrMP8RN2z9N91h+6egZTS1gsPsKantc=
 github.com/docker/cli v20.10.11+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/cli v20.10.12+incompatible h1:lZlz0uzG+GH+c0plStMUdF/qk3ppmgnswpR5EbqzVGA=
+github.com/docker/cli v20.10.12+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
 github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker v20.10.12+incompatible h1:CEeNmFM0QZIsJCZKMkZx0ZcahTiewkrgiwfYD+dfl1U=
 github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
@@ -471,6 +480,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
 github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=
+github.com/google/go-containerregistry v0.7.0 h1:u0onUUOcyoCDHEiJoyR1R1gx5er1+r06V5DBhUU5ndk=
+github.com/google/go-containerregistry v0.7.0/go.mod h1:2zaoelrL0d08gGbpdP3LqyUuBmhWbpD6IOe2s9nLS2k=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
@@ -778,6 +789,8 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/image-spec v1.0.2-0.20210730191737-8e42a01fb1b7/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
 github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
 github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
 github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
@@ -945,6 +958,9 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr
 github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
 github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME=
+github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI=
 github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
 github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
 github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
@@ -1092,6 +1108,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
+golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
 golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
 golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1151,6 +1168,7 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -1288,6 +1306,7 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1507,6 +1526,7 @@ google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEc
 google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211111162719-482062a4217b/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
diff --git a/pkg/healthcheck/healthcheck_fuzzer.go b/pkg/healthcheck/healthcheck_fuzzer.go
new file mode 100644
index 000000000..724ccdc92
--- /dev/null
+++ b/pkg/healthcheck/healthcheck_fuzzer.go
@@ -0,0 +1,18 @@
+package healthcheck
+
+import (
+	"context"
+
+	"github.com/linkerd/linkerd2/pkg/k8s"
+)
+
+// FuzzFetchCurrentConfiguration fuzzes the FetchCurrentConfiguration function.
+func FuzzFetchCurrentConfiguration(data []byte) int {
+	clientset, err := k8s.NewFakeAPI(string(data))
+	if err != nil {
+		return 0
+	}
+
+	_, _, _ = FetchCurrentConfiguration(context.Background(), clientset, "linkerd")
+	return 1
+}
diff --git a/pkg/identity/identity_fuzzer.go b/pkg/identity/identity_fuzzer.go
new file mode 100644
index 000000000..69d9a1b83
--- /dev/null
+++ b/pkg/identity/identity_fuzzer.go
@@ -0,0 +1,26 @@
+package identity
+
+import (
+	"context"
+
+	pb "github.com/linkerd/linkerd2-proxy-api/go/identity"
+	"github.com/linkerd/linkerd2/pkg/tls"
+
+	fuzz "github.com/AdaLogics/go-fuzz-headers"
+)
+
+// FuzzServiceCertify fuzzes the Service.Certify method.
+func FuzzServiceCertify(data []byte) int {
+	f := fuzz.NewConsumer(data)
+	req := &pb.CertifyRequest{}
+	err := f.GenerateStruct(req)
+	if err != nil {
+		return 0
+	}
+
+	svc := NewService(&fakeValidator{"successful-result", nil}, nil, nil, nil, "", "", "")
+	svc.updateIssuer(&fakeIssuer{tls.Crt{}, nil})
+
+	_, _ = svc.Certify(context.Background(), req)
+	return 1
+}
diff --git a/pkg/inject/inject_fuzzer.go b/pkg/inject/inject_fuzzer.go
new file mode 100644
index 000000000..ede129e96
--- /dev/null
+++ b/pkg/inject/inject_fuzzer.go
@@ -0,0 +1,37 @@
+package inject
+
+import (
+	l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
+
+	fuzz "github.com/AdaLogics/go-fuzz-headers"
+)
+
+// FuzzInject fuzzes Pod injection.
+func FuzzInject(data []byte) int {
+	f := fuzz.NewConsumer(data)
+	yamlBytes, err := f.GetBytes()
+	if err != nil {
+		return 0
+	}
+
+	v := &l5dcharts.Values{}
+	err = f.GenerateStruct(v)
+	if err != nil {
+		return 0
+	}
+	conf := NewResourceConfig(v, OriginUnknown, "")
+	_, _ = conf.ParseMetaAndYAML(yamlBytes)
+	injectProxy, err := f.GetBool()
+	if err != nil {
+		return 0
+	}
+	_, _ = conf.GetPodPatch(injectProxy)
+	_, _ = conf.CreateOpaquePortsPatch()
+
+	report := &Report{}
+	err = f.GenerateStruct(report)
+	if err == nil {
+		_, _ = conf.Uninject(report)
+	}
+	return 1
+}
diff --git a/pkg/profiles/profiles_fuzzer.go b/pkg/profiles/profiles_fuzzer.go
new file mode 100644
index 000000000..c2e38539e
--- /dev/null
+++ b/pkg/profiles/profiles_fuzzer.go
@@ -0,0 +1,52 @@
+package profiles
+
+import (
+	"os"
+
+	fuzz "github.com/AdaLogics/go-fuzz-headers"
+)
+
+// FuzzProfilesValidate fuzzes the ProfilesValidate function.
+func FuzzProfilesValidate(data []byte) int {
+	_ = Validate(data)
+	return 1
+}
+
+// FuzzRenderProto fuzzes the RenderProto function.
+func FuzzRenderProto(data []byte) int {
+	f := fuzz.NewConsumer(data)
+	protodata, err := f.GetBytes()
+	if err != nil {
+		return 0
+	}
+	namespace, err := f.GetString()
+	if err != nil {
+		return 0
+	}
+	name, err := f.GetString()
+	if err != nil {
+		return 0
+	}
+	clusterDomain, err := f.GetString()
+	if err != nil {
+		return 0
+	}
+	protofile, err := os.Create("protofile")
+	if err != nil {
+		return 0
+	}
+	defer protofile.Close()
+	defer os.Remove(protofile.Name())
+
+	_, err = protofile.Write(protodata)
+	if err != nil {
+		return 0
+	}
+	w, err := os.OpenFile("/dev/null", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
+	if err != nil {
+		return 0
+	}
+	defer w.Close()
+	_ = RenderProto(protofile.Name(), namespace, name, clusterDomain, w)
+	return 1
+}


Test patch:
diff --git a/cli/cmd/endpoints_test.go b/cli/cmd/endpoints_test.go
index badc0ffd4..28acfba77 100644
--- a/cli/cmd/endpoints_test.go
+++ b/cli/cmd/endpoints_test.go
@@ -4,13 +4,13 @@ import (
 	"testing"
 
 	pb "github.com/linkerd/linkerd2-proxy-api/go/destination"
-	"github.com/linkerd/linkerd2/controller/api/destination"
+	"github.com/linkerd/linkerd2/controller/api/util"
 )
 
 type endpointsExp struct {
 	options     *endpointsOptions
 	authorities []string
-	endpoints   []destination.AuthorityEndpoints
+	endpoints   []util.AuthorityEndpoints
 	file        string
 }
 
@@ -21,11 +21,11 @@ func TestEndpoints(t *testing.T) {
 		testEndpointsCall(endpointsExp{
 			options:     options,
 			authorities: []string{"emoji-svc.emojivoto.svc.cluster.local:8080", "voting-svc.emojivoto.svc.cluster.local:8080"},
-			endpoints: []destination.AuthorityEndpoints{
+			endpoints: []util.AuthorityEndpoints{
 				{
 					Namespace: "emojivoto",
 					ServiceID: "emoji-svc",
-					Pods: []destination.PodDetails{
+					Pods: []util.PodDetails{
 						{
 							Name: "emoji-6bf9f47bd5-jjcrl",
 							IP:   16909060,
@@ -36,7 +36,7 @@ func TestEndpoints(t *testing.T) {
 				{
 					Namespace: "emojivoto",
 					ServiceID: "voting-svc",
-					Pods: []destination.PodDetails{
+					Pods: []util.PodDetails{
 						{
 							Name: "voting-7bf9f47bd5-jjdrl",
 							IP:   84281096,
@@ -53,11 +53,11 @@ func TestEndpoints(t *testing.T) {
 		testEndpointsCall(endpointsExp{
 			options:     options,
 			authorities: []string{"emoji-svc.emojivoto.svc.cluster.local:8080", "voting-svc.emojivoto2.svc.cluster.local:8080"},
-			endpoints: []destination.AuthorityEndpoints{
+			endpoints: []util.AuthorityEndpoints{
 				{
 					Namespace: "emojivoto",
 					ServiceID: "emoji-svc",
-					Pods: []destination.PodDetails{
+					Pods: []util.PodDetails{
 						{
 							Name: "emoji-6bf9f47bd5-jjcrl",
 							IP:   16909060,
@@ -68,7 +68,7 @@ func TestEndpoints(t *testing.T) {
 				{
 					Namespace: "emojivoto2",
 					ServiceID: "voting-svc",
-					Pods: []destination.PodDetails{
+					Pods: []util.PodDetails{
 						{
 							Name: "voting-7bf9f47bd5-jjdrl",
 							IP:   84281096,
@@ -86,11 +86,11 @@ func TestEndpoints(t *testing.T) {
 		testEndpointsCall(endpointsExp{
 			options:     options,
 			authorities: []string{"emoji-svc.emojivoto.svc.cluster.local:8080", "voting-svc.emojivoto.svc.cluster.local:8080"},
-			endpoints: []destination.AuthorityEndpoints{
+			endpoints: []util.AuthorityEndpoints{
 				{
 					Namespace: "emojivoto",
 					ServiceID: "emoji-svc",
-					Pods: []destination.PodDetails{
+					Pods: []util.PodDetails{
 						{
 							Name: "emoji-6bf9f47bd5-jjcrl",
 							IP:   16909060,
@@ -101,7 +101,7 @@ func TestEndpoints(t *testing.T) {
 				{
 					Namespace: "emojivoto",
 					ServiceID: "voting-svc",
-					Pods: []destination.PodDetails{
+					Pods: []util.PodDetails{
 						{
 							Name: "voting-7bf9f47bd5-jjdrl",
 							IP:   84281096,
@@ -118,12 +118,12 @@ func TestEndpoints(t *testing.T) {
 func testEndpointsCall(exp endpointsExp, t *testing.T) {
 	updates := make([]pb.Update, 0)
 	for _, endpoint := range exp.endpoints {
-		addrSet := destination.BuildAddrSet(endpoint)
+		addrSet := util.BuildAddrSet(endpoint)
 		updates = append(updates, pb.Update{Update: &pb.Update_Add{Add: addrSet}})
 	}
 
-	mockClient := &destination.MockAPIClient{
-		DestinationGetClientToReturn: &destination.MockDestinationGetClient{
+	mockClient := &util.MockAPIClient{
+		DestinationGetClientToReturn: &util.MockDestinationGetClient{
 			UpdatesToReturn: updates,
 		},
 	}
diff --git a/controller/api/destination/endpoint_translator_test.go b/controller/api/destination/endpoint_translator_test.go
index ce81d17c2..d59ef3044 100644
--- a/controller/api/destination/endpoint_translator_test.go
+++ b/controller/api/destination/endpoint_translator_test.go
@@ -10,10 +10,8 @@ import (
 	pb "github.com/linkerd/linkerd2-proxy-api/go/destination"
 	"github.com/linkerd/linkerd2-proxy-api/go/net"
 	"github.com/linkerd/linkerd2/controller/api/destination/watcher"
-	pkgk8s "github.com/linkerd/linkerd2/controller/k8s"
 	"github.com/linkerd/linkerd2/pkg/addr"
 	"github.com/linkerd/linkerd2/pkg/k8s"
-	logging "github.com/sirupsen/logrus"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/api/discovery/v1beta1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -104,45 +102,6 @@ var (
 	}
 )
 
-func makeEndpointTranslator(t *testing.T) (*mockDestinationGetServer, *endpointTranslator) {
-	k8sAPI, err := pkgk8s.NewFakeAPI(`
-apiVersion: v1
-kind: Node
-metadata:
-  annotations:
-    kubeadm.alpha.kubernetes.io/cri-socket: /run/containerd/containerd.sock
-    node.alpha.kubernetes.io/ttl: "0"
-  labels:
-    beta.kubernetes.io/arch: amd64
-    kubernetes.io/os: linux
-    kubernetes.io/arch: amd64
-    kubernetes.io/hostname: kind-worker
-    kubernetes.io/os: linux
-    topology.kubernetes.io/region: west
-    topology.kubernetes.io/zone: west-1a
-  name: test-123
-`,
-	)
-	if err != nil {
-		t.Fatalf("NewFakeAPI returned an error: %s", err)
-	}
-	k8sAPI.Sync(nil)
-
-	mockGetServer := &mockDestinationGetServer{updatesReceived: []*pb.Update{}}
-	translator := newEndpointTranslator(
-		"linkerd",
-		"trust.domain",
-		true,
-		"service-name.service-ns",
-		"test-123",
-		map[uint32]struct{}{},
-		k8sAPI.Node(),
-		mockGetServer,
-		logging.WithField("test", t.Name()),
-	)
-	return mockGetServer, translator
-}
-
 func TestEndpointTranslatorForRemoteGateways(t *testing.T) {
 	t.Run("Sends one update for add and another for remove", func(t *testing.T) {
 		mockGetServer, translator := makeEndpointTranslator(t)
diff --git a/controller/api/destination/server_test.go b/controller/api/destination/server_test.go
index f6d7153d0..8ec4789ab 100644
--- a/controller/api/destination/server_test.go
+++ b/controller/api/destination/server_test.go
@@ -32,401 +32,6 @@ const port uint32 = 8989
 const opaquePort uint32 = 4242
 const skippedPort uint32 = 24224
 
-type mockDestinationGetServer struct {
-	util.MockServerStream
-	updatesReceived []*pb.Update
-}
-
-func (m *mockDestinationGetServer) Send(update *pb.Update) error {
-	m.updatesReceived = append(m.updatesReceived, update)
-	return nil
-}
-
-type mockDestinationGetProfileServer struct {
-	util.MockServerStream
-	profilesReceived []*pb.DestinationProfile
-}
-
-func (m *mockDestinationGetProfileServer) Send(profile *pb.DestinationProfile) error {
-	m.profilesReceived = append(m.profilesReceived, profile)
-	return nil
-}
-
-func makeServer(t *testing.T) *server {
-	meshedPodResources := []string{`
-apiVersion: v1
-kind: Namespace
-metadata:
-  name: ns`,
-		`
-apiVersion: v1
-kind: Service
-metadata:
-  name: name1
-  namespace: ns
-spec:
-  type: LoadBalancer
-  clusterIP: 172.17.12.0
-  ports:
-  - port: 8989`,
-		`
-apiVersion: v1
-kind: Endpoints
-metadata:
-  name: name1
-  namespace: ns
-subsets:
-- addresses:
-  - ip: 172.17.0.12
-    targetRef:
-      kind: Pod
-      name: name1-1
-      namespace: ns
-  ports:
-  - port: 8989`,
-		`
-apiVersion: v1
-kind: Pod
-metadata:
-  labels:
-    linkerd.io/control-plane-ns: linkerd
-  name: name1-1
-  namespace: ns
-status:
-  phase: Running
-  podIP: 172.17.0.12
-spec:
-  containers:
-    - env:
-      - name: LINKERD2_PROXY_INBOUND_LISTEN_ADDR
-        value: 0.0.0.0:4143
-      name: linkerd-proxy`,
-		`
-apiVersion: v1
-kind: Pod
-metadata:
-  name: name2-2
-  namespace: ns
-status:
-  phase: Succeeded
-  podIP: 172.17.0.13`,
-		`
-apiVersion: v1
-kind: Pod
-metadata:
-  name: name2-3
-  namespace: ns
-status:
-  phase: Failed
-  podIP: 172.17.0.13`,
-		`
-apiVersion: v1
-kind: Pod
-metadata:
-  name: name2-4
-  namespace: ns
-  deletionTimestamp: 2021-01-01T00:00:00Z
-status:
-  podIP: 172.17.0.13`,
-		`
-apiVersion: linkerd.io/v1alpha2
-kind: ServiceProfile
-metadata:
-  name: name1.ns.svc.mycluster.local
-  namespace: ns
-spec:
-  routes:
-  - name: route1
-    isRetryable: false
-    condition:
-      pathRegex: "/a/b/c"`,
-	}
-
-	clientSP := []string{
-		`
-apiVersion: linkerd.io/v1alpha2
-kind: ServiceProfile
-metadata:
-  name: name1.ns.svc.mycluster.local
-  namespace: client-ns
-spec:
-  routes:
-  - name: route2
-    isRetryable: true
-    condition:
-      pathRegex: "/x/y/z"`,
-	}
-
-	unmeshedPod := `
-apiVersion: v1
-kind: Pod
-metadata:
-  name: name2
-  namespace: ns
-status:
-  phase: Running
-  podIP: 172.17.0.13`
-
-	meshedOpaquePodResources := []string{
-		`
-apiVersion: v1
-kind: Service
-metadata:
-  name: name3
-  namespace: ns
-spec:
-  type: LoadBalancer
-  clusterIP: 172.17.12.1
-  ports:
-  - port: 4242`,
-		`
-apiVersion: v1
-kind: Endpoints
-metadata:
-  name: name3
-  namespace: ns
-subsets:
-- addresses:
-  - ip: 172.17.0.14
-    targetRef:
-      kind: Pod
-      name: name3
-      namespace: ns
-  ports:
-  - port: 4242`,
-		`
-apiVersion: v1
-kind: Pod
-metadata:
-  labels:
-    linkerd.io/control-plane-ns: linkerd
-  annotations:
-    config.linkerd.io/opaque-ports: "4242"
-  name: name3
-  namespace: ns
-status:
-  phase: Running
-  podIP: 172.17.0.14
-spec:
-  containers:
-    - env:
-      - name: LINKERD2_PROXY_INBOUND_LISTEN_ADDR
-        value: 0.0.0.0:4143
-      name: linkerd-proxy`,
-	}
-
-	meshedOpaqueServiceResources := []string{
-		`
-apiVersion: v1
-kind: Service
-metadata:
-  name: name4
-  namespace: ns
-  annotations:
-    config.linkerd.io/opaque-ports: "4242"`,
-	}
-
-	meshedSkippedPodResource := []string{
-		`
-apiVersion: v1
-kind: Service
-metadata:
-  name: name5
-  namespace: ns
-spec:
-  type: LoadBalancer
-  clusterIP: 172.17.13.1
-  ports:
-  - port: 24224`,
-		`
-apiVersion: v1
-kind: Endpoints
-metadata:
-  name: name5
-  namespace: ns
-subsets:
-- addresses:
-  - ip: 172.17.0.15
-    targetRef:
-      kind: Pod
-      name: name5
-      namespace: ns
-  ports:
-  - port: 24224`,
-		`
-apiVersion: v1
-kind: Pod
-metadata:
-  labels:
-    linkerd.io/control-plane-ns: linkerd
-  annotations:
-    config.linkerd.io/skip-inbound-ports: "24224"
-  name: name5
-  namespace: ns
-status:
-  phase: Running
-  podIP: 172.17.0.15
-spec:
-  containers:
-    - env:
-      - name: LINKERD2_PROXY_INBOUND_LISTEN_ADDR
-        value: 0.0.0.0:4143
-      name: linkerd-proxy`,
-	}
-
-	meshedStatefulSetPodResource := []string{
-		`
-apiVersion: v1
-kind: Service
-metadata:
-  name: statefulset-svc
-  namespace: ns
-spec:
-  type: LoadBalancer
-  clusterIP: 172.17.13.5
-  ports:
-  - port: 8989`,
-		`
-apiVersion: v1
-kind: Endpoints
-metadata:
-  name:	statefulset-svc
-  namespace: ns
-subsets:
-- addresses:
-  - ip: 172.17.13.15
-    hostname: pod-0
-    targetRef:
-      kind: Pod
-      name: pod-0
-      namespace: ns
-  ports:
-  - port: 8989`,
-		`
-apiVersion: v1
-kind: Pod
-metadata:
-  labels:
-    linkerd.io/control-plane-ns: linkerd
-  name: pod-0
-  namespace: ns
-status:
-  phase: Running
-  podIP: 172.17.13.15`,
-	}
-
-	policyResources := []string{
-		`
-apiVersion: v1
-kind: Pod
-metadata:
-  labels:
-    linkerd.io/control-plane-ns: linkerd
-    app: policy-test
-  name: pod-policyResources
-  namespace: ns
-status:
-  phase: Running
-  podIP: 172.17.0.16
-spec:
-  containers:
-    - name: linkerd-proxy
-      env:
-      - name: LINKERD2_PROXY_INBOUND_LISTEN_ADDR
-        value: 0.0.0.0:4143
-    - name: app
-      image: nginx
-      ports:
-      - containerPort: 80
-        name: http
-        protocol: TCP`,
-		`
-apiVersion: policy.linkerd.io/v1beta1
-kind: Server
-metadata:
-  name: srv
-  namespace: ns
-spec:
-  podSelector:
-    matchLabels:
-      app: policy-test
-  port: 80
-  proxyProtocol: opaque`,
-	}
-
-	res := append(meshedPodResources, clientSP...)
-	res = append(res, unmeshedPod)
-	res = append(res, meshedOpaquePodResources...)
-	res = append(res, meshedOpaqueServiceResources...)
-	res = append(res, meshedSkippedPodResource...)
-	res = append(res, meshedStatefulSetPodResource...)
-	res = append(res, policyResources...)
-	k8sAPI, err := k8s.NewFakeAPI(res...)
-	if err != nil {
-		t.Fatalf("NewFakeAPI returned an error: %s", err)
-	}
-	log := logging.WithField("test", t.Name())
-	defaultOpaquePorts := map[uint32]struct{}{
-		25:    {},
-		443:   {},
-		587:   {},
-		3306:  {},
-		5432:  {},
-		11211: {},
-	}
-
-	err = watcher.InitializeIndexers(k8sAPI)
-	if err != nil {
-		t.Fatalf("initializeIndexers returned an error: %s", err)
-	}
-
-	endpoints := watcher.NewEndpointsWatcher(k8sAPI, log, false)
-	opaquePorts := watcher.NewOpaquePortsWatcher(k8sAPI, log, defaultOpaquePorts)
-	profiles := watcher.NewProfileWatcher(k8sAPI, log)
-	servers := watcher.NewServerWatcher(k8sAPI, log)
-
-	// Sync after creating watchers so that the the indexers added get updated
-	// properly
-	k8sAPI.Sync(nil)
-
-	return &server{
-		pb.UnimplementedDestinationServer{},
-		endpoints,
-		opaquePorts,
-		profiles,
-		servers,
-		k8sAPI.Node(),
-		true,
-		"linkerd",
-		"trust.domain",
-		"mycluster.local",
-		defaultOpaquePorts,
-		k8sAPI,
-		log,
-		make(<-chan struct{}),
-	}
-}
-
-type bufferingGetStream struct {
-	updates []*pb.Update
-	util.MockServerStream
-}
-
-func (bgs *bufferingGetStream) Send(update *pb.Update) error {
-	bgs.updates = append(bgs.updates, update)
-	return nil
-}
-
-type bufferingGetProfileStream struct {
-	updates []*pb.DestinationProfile
-	util.MockServerStream
-}
-
-func (bgps *bufferingGetProfileStream) Send(profile *pb.DestinationProfile) error {
-	bgps.updates = append(bgps.updates, profile)
-	return nil
-}
-
 func TestGet(t *testing.T) {
 	t.Run("Returns error if not valid service name", func(t *testing.T) {
 		server := makeServer(t)
diff --git a/controller/api/destination/test_util.go b/controller/api/destination/test_util.go
new file mode 100644
index 000000000..e447402bc
--- /dev/null
+++ b/controller/api/destination/test_util.go
@@ -0,0 +1,447 @@
+package destination
+
+import (
+	"testing"
+
+	pb "github.com/linkerd/linkerd2-proxy-api/go/destination"
+	"github.com/linkerd/linkerd2/controller/api/destination/watcher"
+	"github.com/linkerd/linkerd2/controller/api/util"
+	"github.com/linkerd/linkerd2/controller/k8s"
+	pkgk8s "github.com/linkerd/linkerd2/controller/k8s"
+	"github.com/sirupsen/logrus"
+	logging "github.com/sirupsen/logrus"
+)
+
+func makeServer(t *testing.T) *server {
+	meshedPodResources := []string{`
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: ns`,
+		`
+apiVersion: v1
+kind: Service
+metadata:
+  name: name1
+  namespace: ns
+spec:
+  type: LoadBalancer
+  clusterIP: 172.17.12.0
+  ports:
+  - port: 8989`,
+		`
+apiVersion: v1
+kind: Endpoints
+metadata:
+  name: name1
+  namespace: ns
+subsets:
+- addresses:
+  - ip: 172.17.0.12
+    targetRef:
+      kind: Pod
+      name: name1-1
+      namespace: ns
+  ports:
+  - port: 8989`,
+		`
+apiVersion: v1
+kind: Pod
+metadata:
+  labels:
+    linkerd.io/control-plane-ns: linkerd
+  name: name1-1
+  namespace: ns
+status:
+  phase: Running
+  podIP: 172.17.0.12
+spec:
+  containers:
+    - env:
+      - name: LINKERD2_PROXY_INBOUND_LISTEN_ADDR
+        value: 0.0.0.0:4143
+      name: linkerd-proxy`,
+		`
+apiVersion: v1
+kind: Pod
+metadata:
+  name: name2-2
+  namespace: ns
+status:
+  phase: Succeeded
+  podIP: 172.17.0.13`,
+		`
+apiVersion: v1
+kind: Pod
+metadata:
+  name: name2-3
+  namespace: ns
+status:
+  phase: Failed
+  podIP: 172.17.0.13`,
+		`
+apiVersion: v1
+kind: Pod
+metadata:
+  name: name2-4
+  namespace: ns
+  deletionTimestamp: 2021-01-01T00:00:00Z
+status:
+  podIP: 172.17.0.13`,
+		`
+apiVersion: linkerd.io/v1alpha2
+kind: ServiceProfile
+metadata:
+  name: name1.ns.svc.mycluster.local
+  namespace: ns
+spec:
+  routes:
+  - name: route1
+    isRetryable: false
+    condition:
+      pathRegex: "/a/b/c"`,
+	}
+
+	clientSP := []string{
+		`
+apiVersion: linkerd.io/v1alpha2
+kind: ServiceProfile
+metadata:
+  name: name1.ns.svc.mycluster.local
+  namespace: client-ns
+spec:
+  routes:
+  - name: route2
+    isRetryable: true
+    condition:
+      pathRegex: "/x/y/z"`,
+	}
+
+	unmeshedPod := `
+apiVersion: v1
+kind: Pod
+metadata:
+  name: name2
+  namespace: ns
+status:
+  phase: Running
+  podIP: 172.17.0.13`
+
+	meshedOpaquePodResources := []string{
+		`
+apiVersion: v1
+kind: Service
+metadata:
+  name: name3
+  namespace: ns
+spec:
+  type: LoadBalancer
+  clusterIP: 172.17.12.1
+  ports:
+  - port: 4242`,
+		`
+apiVersion: v1
+kind: Endpoints
+metadata:
+  name: name3
+  namespace: ns
+subsets:
+- addresses:
+  - ip: 172.17.0.14
+    targetRef:
+      kind: Pod
+      name: name3
+      namespace: ns
+  ports:
+  - port: 4242`,
+		`
+apiVersion: v1
+kind: Pod
+metadata:
+  labels:
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    config.linkerd.io/opaque-ports: "4242"
+  name: name3
+  namespace: ns
+status:
+  phase: Running
+  podIP: 172.17.0.14
+spec:
+  containers:
+    - env:
+      - name: LINKERD2_PROXY_INBOUND_LISTEN_ADDR
+        value: 0.0.0.0:4143
+      name: linkerd-proxy`,
+	}
+
+	meshedOpaqueServiceResources := []string{
+		`
+apiVersion: v1
+kind: Service
+metadata:
+  name: name4
+  namespace: ns
+  annotations:
+    config.linkerd.io/opaque-ports: "4242"`,
+	}
+
+	meshedSkippedPodResource := []string{
+		`
+apiVersion: v1
+kind: Service
+metadata:
+  name: name5
+  namespace: ns
+spec:
+  type: LoadBalancer
+  clusterIP: 172.17.13.1
+  ports:
+  - port: 24224`,
+		`
+apiVersion: v1
+kind: Endpoints
+metadata:
+  name: name5
+  namespace: ns
+subsets:
+- addresses:
+  - ip: 172.17.0.15
+    targetRef:
+      kind: Pod
+      name: name5
+      namespace: ns
+  ports:
+  - port: 24224`,
+		`
+apiVersion: v1
+kind: Pod
+metadata:
+  labels:
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    config.linkerd.io/skip-inbound-ports: "24224"
+  name: name5
+  namespace: ns
+status:
+  phase: Running
+  podIP: 172.17.0.15
+spec:
+  containers:
+    - env:
+      - name: LINKERD2_PROXY_INBOUND_LISTEN_ADDR
+        value: 0.0.0.0:4143
+      name: linkerd-proxy`,
+	}
+
+	meshedStatefulSetPodResource := []string{
+		`
+apiVersion: v1
+kind: Service
+metadata:
+  name: statefulset-svc
+  namespace: ns
+spec:
+  type: LoadBalancer
+  clusterIP: 172.17.13.5
+  ports:
+  - port: 8989`,
+		`
+apiVersion: v1
+kind: Endpoints
+metadata:
+  name:	statefulset-svc
+  namespace: ns
+subsets:
+- addresses:
+  - ip: 172.17.13.15
+    hostname: pod-0
+    targetRef:
+      kind: Pod
+      name: pod-0
+      namespace: ns
+  ports:
+  - port: 8989`,
+		`
+apiVersion: v1
+kind: Pod
+metadata:
+  labels:
+    linkerd.io/control-plane-ns: linkerd
+  name: pod-0
+  namespace: ns
+status:
+  phase: Running
+  podIP: 172.17.13.15`,
+	}
+
+	policyResources := []string{
+		`
+apiVersion: v1
+kind: Pod
+metadata:
+  labels:
+    linkerd.io/control-plane-ns: linkerd
+    app: policy-test
+  name: pod-policyResources
+  namespace: ns
+status:
+  phase: Running
+  podIP: 172.17.0.16
+spec:
+  containers:
+    - name: linkerd-proxy
+      env:
+      - name: LINKERD2_PROXY_INBOUND_LISTEN_ADDR
+        value: 0.0.0.0:4143
+    - name: app
+      image: nginx
+      ports:
+      - containerPort: 80
+        name: http
+        protocol: TCP`,
+		`
+apiVersion: policy.linkerd.io/v1beta1
+kind: Server
+metadata:
+  name: srv
+  namespace: ns
+spec:
+  podSelector:
+    matchLabels:
+      app: policy-test
+  port: 80
+  proxyProtocol: opaque`,
+	}
+
+	res := append(meshedPodResources, clientSP...)
+	res = append(res, unmeshedPod)
+	res = append(res, meshedOpaquePodResources...)
+	res = append(res, meshedOpaqueServiceResources...)
+	res = append(res, meshedSkippedPodResource...)
+	res = append(res, meshedStatefulSetPodResource...)
+	res = append(res, policyResources...)
+	k8sAPI, err := k8s.NewFakeAPI(res...)
+	if err != nil {
+		t.Fatalf("NewFakeAPI returned an error: %s", err)
+	}
+	log := logging.WithField("test", t.Name())
+	defaultOpaquePorts := map[uint32]struct{}{
+		25:    {},
+		443:   {},
+		587:   {},
+		3306:  {},
+		5432:  {},
+		11211: {},
+	}
+
+	err = watcher.InitializeIndexers(k8sAPI)
+	if err != nil {
+		t.Fatalf("initializeIndexers returned an error: %s", err)
+	}
+
+	endpoints := watcher.NewEndpointsWatcher(k8sAPI, log, false)
+	opaquePorts := watcher.NewOpaquePortsWatcher(k8sAPI, log, defaultOpaquePorts)
+	profiles := watcher.NewProfileWatcher(k8sAPI, log)
+	servers := watcher.NewServerWatcher(k8sAPI, log)
+
+	// Sync after creating watchers so that the the indexers added get updated
+	// properly
+	k8sAPI.Sync(nil)
+
+	return &server{
+		pb.UnimplementedDestinationServer{},
+		endpoints,
+		opaquePorts,
+		profiles,
+		servers,
+		k8sAPI.Node(),
+		true,
+		"linkerd",
+		"trust.domain",
+		"mycluster.local",
+		defaultOpaquePorts,
+		k8sAPI,
+		log,
+		make(<-chan struct{}),
+	}
+}
+
+type bufferingGetStream struct {
+	updates []*pb.Update
+	util.MockServerStream
+}
+
+func (bgs *bufferingGetStream) Send(update *pb.Update) error {
+	bgs.updates = append(bgs.updates, update)
+	return nil
+}
+
+type bufferingGetProfileStream struct {
+	updates []*pb.DestinationProfile
+	util.MockServerStream
+}
+
+func (bgps *bufferingGetProfileStream) Send(profile *pb.DestinationProfile) error {
+	bgps.updates = append(bgps.updates, profile)
+	return nil
+}
+
+type mockDestinationGetServer struct {
+	util.MockServerStream
+	updatesReceived []*pb.Update
+}
+
+func (m *mockDestinationGetServer) Send(update *pb.Update) error {
+	m.updatesReceived = append(m.updatesReceived, update)
+	return nil
+}
+
+type mockDestinationGetProfileServer struct {
+	util.MockServerStream
+	profilesReceived []*pb.DestinationProfile
+}
+
+func (m *mockDestinationGetProfileServer) Send(profile *pb.DestinationProfile) error {
+	m.profilesReceived = append(m.profilesReceived, profile)
+	return nil
+}
+
+func makeEndpointTranslator(t *testing.T) (*mockDestinationGetServer, *endpointTranslator) {
+	k8sAPI, err := pkgk8s.NewFakeAPI(`
+apiVersion: v1
+kind: Node
+metadata:
+  annotations:
+    kubeadm.alpha.kubernetes.io/cri-socket: /run/containerd/containerd.sock
+    node.alpha.kubernetes.io/ttl: "0"
+  labels:
+    beta.kubernetes.io/arch: amd64
+    kubernetes.io/os: linux
+    kubernetes.io/arch: amd64
+    kubernetes.io/hostname: kind-worker
+    kubernetes.io/os: linux
+    topology.kubernetes.io/region: west
+    topology.kubernetes.io/zone: west-1a
+  name: test-123
+`,
+	)
+	if err != nil {
+		t.Fatalf("NewFakeAPI returned an error: %s", err)
+	}
+	k8sAPI.Sync(nil)
+
+	mockGetServer := &mockDestinationGetServer{updatesReceived: []*pb.Update{}}
+	translator := newEndpointTranslator(
+		"linkerd",
+		"trust.domain",
+		true,
+		"service-name.service-ns",
+		"test-123",
+		map[uint32]struct{}{},
+		k8sAPI.Node(),
+		mockGetServer,
+		logrus.WithField("test", t.Name()),
+	)
+	return mockGetServer, translator
+}
diff --git a/controller/api/util/test_helper.go b/controller/api/util/test_helper.go
deleted file mode 100644
index b03567487..000000000
--- a/controller/api/util/test_helper.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package util
-
-import (
-	"golang.org/x/net/context"
-	"google.golang.org/grpc/metadata"
-)
-
-type mockStream struct {
-	ctx    context.Context
-	Cancel context.CancelFunc
-}
-
-func newMockStream() mockStream {
-	ctx, cancel := context.WithCancel(context.Background())
-	return mockStream{ctx, cancel}
-}
-
-func (ms mockStream) Context() context.Context    { return ms.ctx }
-func (ms mockStream) SendMsg(m interface{}) error { return nil }
-func (ms mockStream) RecvMsg(m interface{}) error { return nil }
-
-// MockServerStream satisfies the grpc.ServerStream interface
-type MockServerStream struct{ mockStream }
-
-// SetHeader satisfies the grpc.ServerStream interface
-func (mss MockServerStream) SetHeader(metadata.MD) error { return nil }
-
-// SendHeader satisfies the grpc.ServerStream interface
-func (mss MockServerStream) SendHeader(metadata.MD) error { return nil }
-
-// SetTrailer satisfies the grpc.ServerStream interface
-func (mss MockServerStream) SetTrailer(metadata.MD) {}
-
-// NewMockServerStream instantiates a MockServerStream
-func NewMockServerStream() MockServerStream {
-	return MockServerStream{newMockStream()}
-}
diff --git a/controller/api/destination/test_helper.go b/controller/api/util/test_util.go
similarity index 71%
rename from controller/api/destination/test_helper.go
rename to controller/api/util/test_util.go
index 3d86713be..905e3b9fc 100644
--- a/controller/api/destination/test_helper.go
+++ b/controller/api/util/test_util.go
@@ -1,16 +1,48 @@
-package destination
+package util
 
 import (
-	"context"
 	"errors"
 	"io"
 	"sync"
 
 	destinationPb "github.com/linkerd/linkerd2-proxy-api/go/destination"
 	"github.com/linkerd/linkerd2-proxy-api/go/net"
+	"golang.org/x/net/context"
 	"google.golang.org/grpc"
+	"google.golang.org/grpc/metadata"
 )
 
+type mockStream struct {
+	ctx    context.Context
+	Cancel context.CancelFunc
+}
+
+func newMockStream() mockStream {
+	ctx, cancel := context.WithCancel(context.Background())
+	return mockStream{ctx, cancel}
+}
+
+func (ms mockStream) Context() context.Context    { return ms.ctx }
+func (ms mockStream) SendMsg(m interface{}) error { return nil }
+func (ms mockStream) RecvMsg(m interface{}) error { return nil }
+
+// MockServerStream satisfies the grpc.ServerStream interface
+type MockServerStream struct{ mockStream }
+
+// SetHeader satisfies the grpc.ServerStream interface
+func (mss MockServerStream) SetHeader(metadata.MD) error { return nil }
+
+// SendHeader satisfies the grpc.ServerStream interface
+func (mss MockServerStream) SendHeader(metadata.MD) error { return nil }
+
+// SetTrailer satisfies the grpc.ServerStream interface
+func (mss MockServerStream) SetTrailer(metadata.MD) {}
+
+// NewMockServerStream instantiates a MockServerStream
+func NewMockServerStream() MockServerStream {
+	return MockServerStream{newMockStream()}
+}
+
 // MockAPIClient satisfies the destination API's interfaces
 type MockAPIClient struct {
 	ErrorToReturn                error
diff --git a/pkg/identity/service_test.go b/pkg/identity/service_test.go
index 5465b2e70..4c37e1140 100644
--- a/pkg/identity/service_test.go
+++ b/pkg/identity/service_test.go
@@ -2,31 +2,12 @@ package identity
 
 import (
 	"context"
-	"crypto/x509"
 	"testing"
 
 	pb "github.com/linkerd/linkerd2-proxy-api/go/identity"
 	"github.com/linkerd/linkerd2/pkg/tls"
 )
 
-type fakeValidator struct {
-	result string
-	err    error
-}
-
-type fakeIssuer struct {
-	result tls.Crt
-	err    error
-}
-
-func (fi *fakeIssuer) IssueEndEntityCrt(*x509.CertificateRequest) (tls.Crt, error) {
-	return fi.result, fi.err
-}
-
-func (fk *fakeValidator) Validate(context.Context, []byte) (string, error) {
-	return fk.result, fk.err
-}
-
 func TestServiceNotReady(t *testing.T) {
 	// ch := make(chan tls.Issuer, 1)
 	svc := NewService(&fakeValidator{"successful-result", nil}, nil, nil, nil, "", "", "")
diff --git a/pkg/identity/test_util.go b/pkg/identity/test_util.go
new file mode 100644
index 000000000..941d8dc3d
--- /dev/null
+++ b/pkg/identity/test_util.go
@@ -0,0 +1,26 @@
+package identity
+
+import (
+	"context"
+	"crypto/x509"
+
+	"github.com/linkerd/linkerd2/pkg/tls"
+)
+
+type fakeValidator struct {
+	result string
+	err    error
+}
+
+func (fk *fakeValidator) Validate(context.Context, []byte) (string, error) {
+	return fk.result, fk.err
+}
+
+type fakeIssuer struct {
+	result tls.Crt
+	err    error
+}
+
+func (fi *fakeIssuer) IssueEndEntityCrt(*x509.CertificateRequest) (tls.Crt, error) {
+	return fi.result, fi.err
+}
diff --git a/test/fuzzing/README.md b/test/fuzzing/README.md
new file mode 100644
index 000000000..c1a9f67aa
--- /dev/null
+++ b/test/fuzzing/README.md
@@ -0,0 +1,26 @@
+# Fuzzing Linkerd
+
+The scripting setup for fuzzing is used by
+[google/oss-fuzz](https://github.com/google/oss-fuzz) which performs continuous
+fuzzing for the Linkerd project.
+
+The fuzzing configuration for Linkerd is located in the
+[linkerd2](https://github.com/google/oss-fuzz/tree/master/projects/linkerd2)
+project which handles the docker build and execution of the fuzzers.
+
+## Running locally
+Instructions for running the fuzzers locally can be found in the oss-fuzz
+[docs](https://google.github.io/oss-fuzz/getting-started/new-project-guide/#testing-locally).
+
+This will require cloning the [oss-fuzz](https://github.com/google/oss-fuzz)
+repository locally and running the commands outlined in the instructions.
+
+### oss-fuzz File Setup
+
+- [Dockerfile](https://github.com/google/oss-fuzz/blob/master/projects/linkerd2/Dockerfile)
+  provides the necessary environment for running the fuzzer; the main thing
+  being the `oss-fuzz-base` image which provides the `compile_go_fuzzer`
+  funtions seen in this directory's `build.sh`.
+- [build.sh](https://github.com/google/oss-fuzz/blob/master/projects/linkerd2/build.sh)
+  is responsible for calling the fuzzing functions for each fuzzer in the
+  linkerd2 project.
diff --git a/test/fuzzing/fuzzers.go b/test/fuzzing/fuzzers.go
new file mode 100644
index 000000000..b0d0a96b8
--- /dev/null
+++ b/test/fuzzing/fuzzers.go
@@ -0,0 +1,53 @@
+package fuzzing
+
+import (
+	fuzz "github.com/AdaLogics/go-fuzz-headers"
+	"github.com/linkerd/linkerd2/pkg/healthcheck"
+	"github.com/linkerd/linkerd2/pkg/util"
+	corev1 "k8s.io/api/core/v1"
+)
+
+// FuzzParsePorts fuzzes the ParsePorts function.
+func FuzzParsePorts(data []byte) int {
+	_, _ = util.ParsePorts(string(data))
+	return 1
+}
+
+// FuzzParseContainerOpaquePorts fuzzes the ParseContainerOpaquePorts function.
+func FuzzParseContainerOpaquePorts(data []byte) int {
+	f := fuzz.NewConsumer(data)
+
+	qtyOfContainers, err := f.GetInt()
+	if err != nil {
+		return 0
+	}
+	qtyOfContainers %= 20
+
+	containers := make([]corev1.Container, 0)
+	for i := 0; i < qtyOfContainers; i++ {
+		newContainer := corev1.Container{}
+		err = f.GenerateStruct(&newContainer)
+		if err != nil {
+			return 0
+		}
+		containers = append(containers, newContainer)
+	}
+	override, err := f.GetString()
+	if err != nil {
+		return 0
+	}
+	_ = util.ParseContainerOpaquePorts(override, containers)
+	return 1
+}
+
+// FuzzHealthCheck fuzzes the HealthCheck method for the healthchecker.
+func FuzzHealthCheck(data []byte) int {
+	f := fuzz.NewConsumer(data)
+	options := &healthcheck.Options{}
+	err := f.GenerateStruct(options)
+	if err != nil {
+		return 0
+	}
+	_ = healthcheck.NewHealthChecker([]healthcheck.CategoryID{healthcheck.KubernetesAPIChecks}, options)
+	return 1
+}
diff --git a/testutil/test_helper.go b/testutil/test_helper.go
index 56db44521..cf6461432 100644
--- a/testutil/test_helper.go
+++ b/testutil/test_helper.go
@@ -724,8 +724,6 @@ func (h *TestHelper) DownloadCLIBinary(filepath, version string) error {
 	if err != nil {
 		return err
 	}
-	// This is a test and we don't care about errors that can occur here.
-	//nolint:gosec
 	defer out.Close()
 
 	_, err = io.Copy(out, resp.Body)