Repository: knative-sandbox/net-kourier
Pull request #744 (2022-01-10 17:10:38)

enable proxy protocol
<!-- Thanks for sending a pull request! -->

# Changes

<!-- 
Describe your changes here- ideally you can get that description straight from
your descriptive commit message(s)! 

- :gift: Add new feature
- :bug: Fix bug
- :broom: Update or clean up current behavior
- :wastebasket: Remove feature or internal logic
-->

:gift: enable proxy protocol 
<!--
In addition, categorize the changes you're making using the "/kind" Prow command, example:

/kind <kind>

Supported kinds are: api-change, bug, cleanup, deprecation, removal, documentation, enhancement, performance

-->
/kind enhancement

<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related issues/tests in a comment and do not use `Fixes`_*
-->
<!-- Please include the 'why' behind your changes if no issue exists -->
This patch will help users to retreive connection information from the source requesting the connection, especially the source IP address
**Release Note**

<!--
:page_facing_up: If this change has user-visible impact, write a release note in the block
below. Include the string "action required" if additional action is required of
users switching to the new release, for example in case of a breaking change.

Write as if you are speaking to users, not other Knative contributors. If this
change has no user-visible impact, no release-note is needed.
-->
```release-note
net-kourier supports proxy protocol
action required
```

**Docs**

<!--
:book: If this change has user-visible impact, link to an issue or PR in
https://github.com/knative/docs.

See here for guidance on getting permanent links to files: https://help.github.com/en/articles/getting-permanent-links-to-files

Please use the following format for linking documentation:
- [knative/docs]: <issue or pr link>
- [Feature Track]: <link>
- [Usage]: <link>
- [Other doc]: <link>
-->
```docs
[README.md]: <https://github.com/RamyChaabane/net-kourier/tree/SRVLESS-7277-proxy-protocol#proxy-protocol-configuration>
```


knative-prow-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative-sandbox/net-kourier/pull/744#" title="Author self-approved">RamyChaabane</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **evankanderson** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @evankanderson` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=knative-sandbox%2Fnet-kourier).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/knative-sandbox/net-kourier/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["evankanderson"]} -->
knative-prow-robot: Hi @RamyChaabane. Thanks for your PR.

I'm waiting for a [knative-sandbox](https://github.com/orgs/knative-sandbox/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://github.com/orgs/knative-sandbox/people) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=knative-sandbox%2Fnet-kourier).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

knative-prow-robot: @RamyChaabane: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
RamyChaabane: /assign @evankanderson

I will add some tests if this is approved, thx
evankanderson: /assign @carlisia 

For a second set of eyes / experience
mcastilho: Isn't this proxy support feature a must have if you are using AWS NLB ? I have everything working with Knative + Courier expect that all the request IPs are the same (the load balancer). 

Am I missing something? Has any of you able to get original IP address without this feature using a Layer 4 LB (AWS NLB in this case)

I can't wait for this to be merged in. 
carlisia: Hey @mcastilho, unless someone gets to this first, I'll review the PR tomorrow.

I haven't used net-kourier, but we do have conformance tests that run frequently for all components, although they might only be hitting GCP now that I think about it. I'll check on that.
nak3: @RamyChaabane Thank you for your PRs. Do you have any plan to add an e2e test for this feature? If I understand correctly, we need to setup a L4 proxy (which supports proxy protocol) in front. How will you tackle it?

Also, if we support further Envoy features in Kourier (like this PR and [pull/759](https://github.com/knative-sandbox/net-kourier/pull/759)), Kourier will become Istio Gateway or Contour eventually... Frankly speaking, why you want to add the features into Kourier rather than using Istio (or Contour)? Please don't get me wrong. I am happy to improve Kourier and glad to see your PRs but I am curious about it.
RamyChaabane: @nak3 Thank you for your comment. We've tested Istio Gateway and Contour and we've noticed issues when it comes to deployment time and even scaling, especially for Contour: it does not scale pretty well when we have reached arround 500 KSVC and the deployment was taking too much time. Adding those features does not change much to Kourier.

Regarding tests, I'm planning to add some units tests if this feature will be accepted. For e2e tests, I can do that, but we will need, as you said, to setup a L4 proxy and rerun eventually all the existing tests, so we migh need to setup another cluster(s) for that
knative-prow-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative-sandbox/net-kourier/pull/744#" title="Author self-approved">RamyChaabane</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please ask for approval from **evankanderson** after the PR has been reviewed.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=knative-sandbox%2Fnet-kourier).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/knative-sandbox/net-kourier/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["evankanderson"]} -->
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1486377968149204992/artifacts/line-cov.html#file3) | 80.6% | 59.3% | -21.2
[pkg/envoy/api/route.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1486377968149204992/artifacts/line-cov.html#file4) | 80.0% | 83.3% | 3.3
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1486377968149204992/artifacts/line-cov.html#file5) | 81.4% | 80.8% | -0.7

knative-prow-robot: @RamyChaabane: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-knative-sandbox-net-kourier-integration-tests | 4a3aae9d85b7773a90ce3af4e73d446dbf16d075 | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-integration-tests/1486377968115650560) | true | `/test pull-knative-sandbox-net-kourier-integration-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative-sandbox_net-kourier/744). [Your PR dashboard](https://gubernator.knative.dev/pr/RamyChaabane).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
RamyChaabane: /retest
knative-prow-robot: @RamyChaabane: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-knative-sandbox-net-kourier-integration-tests | 4a3aae9d85b7773a90ce3af4e73d446dbf16d075 | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-integration-tests/1486628229509812224) | true | `/test pull-knative-sandbox-net-kourier-integration-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative-sandbox_net-kourier/744). [Your PR dashboard](https://gubernator.knative.dev/pr/RamyChaabane).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1486660960163729408/artifacts/line-cov.html#file3) | 80.6% | 59.3% | -21.2
[pkg/envoy/api/route.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1486660960163729408/artifacts/line-cov.html#file4) | 80.0% | 83.3% | 3.3
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1486660960163729408/artifacts/line-cov.html#file5) | 81.4% | 81.3% | -0.1

knative-prow-robot: @RamyChaabane: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-knative-sandbox-net-kourier-integration-tests | 23b6df66defe51e32725294bf380ded751ea8f1e | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-integration-tests/1486660960134369280) | true | `/test pull-knative-sandbox-net-kourier-integration-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative-sandbox_net-kourier/744). [Your PR dashboard](https://gubernator.knative.dev/pr/RamyChaabane).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1486709102213074944/artifacts/line-cov.html#file3) | 80.6% | 59.3% | -21.2
[pkg/envoy/api/route.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1486709102213074944/artifacts/line-cov.html#file4) | 80.0% | 83.3% | 3.3
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1486709102213074944/artifacts/line-cov.html#file5) | 81.4% | 80.6% | -0.9

RamyChaabane: /retest
RamyChaabane: @nak3, I've updated the code and now ports 8090 and 9443 are the prob ports by default, can't wait for the feedback
nak3: Thank you! And sorry I will be day off until next week but will take a look at it.
knative-prow-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative-sandbox/net-kourier/pull/744#" title="Author self-approved">RamyChaabane</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please ask for approval from **carlisia** after the PR has been reviewed.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=knative-sandbox%2Fnet-kourier).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/knative-sandbox/net-kourier/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["carlisia"]} -->
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1489178260037177344/artifacts/line-cov.html#file3) | 80.6% | 59.3% | -21.2
[pkg/envoy/api/route.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1489178260037177344/artifacts/line-cov.html#file4) | 80.0% | 83.3% | 3.3
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1489178260037177344/artifacts/line-cov.html#file5) | 81.4% | 80.6% | -0.9

nak3: Thank you for the update.
I learned curl command has `--haproxy-protocol` option and it works against your change.
So, I guess we can add e2e test under `test/proxyproto` by using https://github.com/pires/go-proxyproto or library?

RamyChaabane: @nak3 Thank you for the feedback
I updated the code according to your comments, if you feel it's ready to go, I can start add some tests
nak3: Thank you @RamyChaabane I left one more comment but I think it is ready to go. Let's add some tests. 
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/http_connection_manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1490999420911620096/artifacts/line-cov.html#file2) | 90.0% | 83.3% | -6.7
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1490999420911620096/artifacts/line-cov.html#file3) | 80.6% | 60.3% | -20.2
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1490999420911620096/artifacts/line-cov.html#file4) | 81.4% | 80.6% | -0.9

knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/http_connection_manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491074785445154816/artifacts/line-cov.html#file2) | 90.0% | 91.7% | 1.7
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491074785445154816/artifacts/line-cov.html#file3) | 80.6% | 81.0% | 0.5
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491074785445154816/artifacts/line-cov.html#file4) | 81.4% | 80.6% | -0.9

RamyChaabane: @nak3 I've added some units tests and now we have a good coverage, I'll start working on e2e tests once you're OK with these changes
nak3: Thank you! Sure, it should be alright to start adding the e2e tests.
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/http_connection_manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491827213127913472/artifacts/line-cov.html#file2) | 90.0% | 91.7% | 1.7
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491827213127913472/artifacts/line-cov.html#file3) | 80.6% | 81.0% | 0.5
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491827213127913472/artifacts/line-cov.html#file4) | 81.4% | 80.6% | -0.9

knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/http_connection_manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491839345374531584/artifacts/line-cov.html#file2) | 90.0% | 91.7% | 1.7
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491839345374531584/artifacts/line-cov.html#file3) | 80.6% | 81.0% | 0.5
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491839345374531584/artifacts/line-cov.html#file4) | 81.4% | 80.6% | -0.9

knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/http_connection_manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491849429873856512/artifacts/line-cov.html#file2) | 90.0% | 91.7% | 1.7
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491849429873856512/artifacts/line-cov.html#file3) | 80.6% | 81.0% | 0.5
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491849429873856512/artifacts/line-cov.html#file4) | 81.4% | 80.6% | -0.9

knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/http_connection_manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491855594129199104/artifacts/line-cov.html#file2) | 90.0% | 91.7% | 1.7
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491855594129199104/artifacts/line-cov.html#file3) | 80.6% | 81.0% | 0.5
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491855594129199104/artifacts/line-cov.html#file4) | 81.4% | 80.6% | -0.9

knative-prow-robot: @RamyChaabane: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-knative-sandbox-net-kourier-build-tests | 387cd5544360834ebf0d8a18f3297d3a0144e0a5 | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-build-tests/1491855594028535808) | true | `/test pull-knative-sandbox-net-kourier-build-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative-sandbox_net-kourier/744). [Your PR dashboard](https://gubernator.knative.dev/pr/RamyChaabane).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/http_connection_manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491859139456929792/artifacts/line-cov.html#file2) | 90.0% | 91.7% | 1.7
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491859139456929792/artifacts/line-cov.html#file3) | 80.6% | 81.0% | 0.5
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1491859139456929792/artifacts/line-cov.html#file4) | 81.4% | 80.6% | -0.9

knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/http_connection_manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492098156723179520/artifacts/line-cov.html#file2) | 90.0% | 91.7% | 1.7
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492098156723179520/artifacts/line-cov.html#file3) | 80.6% | 81.0% | 0.5
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492098156723179520/artifacts/line-cov.html#file4) | 81.4% | 80.6% | -0.9

knative-prow-robot: @RamyChaabane: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-knative-sandbox-net-kourier-unit-tests | 30a8942b20246a8b3209b7e927e1e11c532caa5f | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-unit-tests/1492107423270309888) | true | `/test pull-knative-sandbox-net-kourier-unit-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative-sandbox_net-kourier/744). [Your PR dashboard](https://gubernator.knative.dev/pr/RamyChaabane).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/http_connection_manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492107423341613056/artifacts/line-cov.html#file2) | 90.0% | 91.7% | 1.7
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492107423341613056/artifacts/line-cov.html#file3) | 80.6% | 81.0% | 0.5
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492107423341613056/artifacts/line-cov.html#file4) | 81.4% | 80.6% | -0.9

knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/http_connection_manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492110196405702656/artifacts/line-cov.html#file2) | 90.0% | 91.7% | 1.7
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492110196405702656/artifacts/line-cov.html#file3) | 80.6% | 81.0% | 0.5
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492110196405702656/artifacts/line-cov.html#file4) | 81.4% | 80.6% | -0.9

knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/http_connection_manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492116814774145024/artifacts/line-cov.html#file2) | 90.0% | 91.7% | 1.7
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492116814774145024/artifacts/line-cov.html#file3) | 80.6% | 81.0% | 0.5
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492116814774145024/artifacts/line-cov.html#file4) | 81.4% | 80.6% | -0.9

knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/http_connection_manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492122755661828096/artifacts/line-cov.html#file2) | 90.0% | 91.7% | 1.7
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492122755661828096/artifacts/line-cov.html#file3) | 80.6% | 81.0% | 0.5
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492122755661828096/artifacts/line-cov.html#file4) | 81.4% | 80.6% | -0.9

RamyChaabane: @nak3 e2e tests has been added and passed successfully 
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/http_connection_manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492136280257990656/artifacts/line-cov.html#file2) | 90.0% | 91.7% | 1.7
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492136280257990656/artifacts/line-cov.html#file3) | 80.6% | 81.0% | 0.5
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1492136280257990656/artifacts/line-cov.html#file4) | 81.4% | 80.6% | -0.9

nak3: Thank you so much! I will look at it early next week. 
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/http_connection_manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1493223977135378432/artifacts/line-cov.html#file2) | 90.0% | 91.7% | 1.7
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1493223977135378432/artifacts/line-cov.html#file3) | 80.6% | 81.0% | 0.5
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1493223977135378432/artifacts/line-cov.html#file4) | 81.4% | 80.6% | -0.9

knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/http_connection_manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1493224561477423104/artifacts/line-cov.html#file2) | 90.0% | 91.7% | 1.7
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1493224561477423104/artifacts/line-cov.html#file3) | 80.6% | 81.0% | 0.5
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1493224561477423104/artifacts/line-cov.html#file4) | 81.4% | 80.6% | -0.9

knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-sandbox-net-kourier-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/envoy/api/http_connection_manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1493226524373946368/artifacts/line-cov.html#file2) | 90.0% | 91.7% | 1.7
[pkg/envoy/api/listener.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1493226524373946368/artifacts/line-cov.html#file3) | 80.6% | 81.0% | 0.5
[pkg/generator/caches.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative-sandbox_net-kourier/744/pull-knative-sandbox-net-kourier-go-coverage/1493226524373946368/artifacts/line-cov.html#file4) | 81.4% | 80.6% | -0.9

nak3: /hold cancel
/lgtm
/approve

Thank you so much for your patience and this improvement @RamyChaabane 
Let's merge this.
knative-prow-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative-sandbox/net-kourier/pull/744#issuecomment-1039853212" title="Approved">nak3</a>*, *<a href="https://github.com/knative-sandbox/net-kourier/pull/744#" title="Author self-approved">RamyChaabane</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=knative-sandbox%2Fnet-kourier).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/knative-sandbox/net-kourier/blob/main/OWNERS)~~ [nak3]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/README.md b/README.md
index 1591c47f..da4d5d35 100644
--- a/README.md
+++ b/README.md
@@ -100,6 +100,7 @@ To change the Kourier gateway namespace, you will need to:
 - Timeouts and retries.
 - TLS
 - External Authorization support.
+- Proxy Protocol (AN EXPERIMENTAL / ALPHA FEATURE)
 
 ## Setup TLS certificate
 
@@ -133,6 +134,57 @@ vars in the `net-kourier-controller` deployment:
 
 `*` Required
 
+## Proxy Protocol Configuration
+Note: this is an experimental/alpha feature.
+
+To enable proxy protocol feature, run the following command to patch `config-kourier` ConfigMap:
+```
+kubectl patch configmap/config-kourier \
+  -n knative-serving \
+  --type merge \
+  -p '{"data":{"enable-proxy-protocol":"true"}}'
+```
+Ensure that the file was updated successfully:
+```
+kubectl get configmap config-kourier --namespace knative-serving --output yaml
+```
+### LoadBalancer configuration:
+
+We need to:
+- Use your LB provider annotation to enable proxy-protocol.
+- If you are planning to enable autoTLS, use your LB provider annotation to specify a custom name to use for the Load balancer,
+  This is used to work around the issue of kube-proxy adding external LB address to node local iptables rule, which will break requests to an LB from in-cluster if the LB is expected to terminate SSL or proxy protocol.
+- Change the external Traffic Policy to `local` so the LB we'll preserve the client source IP and avoids a second hop for LoadBalancer.
+
+Example (Scaleway provider):
+```
+apiVersion: v1
+kind: Service
+metadata:
+  name: kourier
+  namespace: kourier-system
+  annotations:
+    service.beta.kubernetes.io/scw-loadbalancer-proxy-protocol-v2: '*'
+    service.beta.kubernetes.io/scw-loadbalancer-use-hostname: "true"
+  labels:
+    networking.knative.dev/ingress-provider: kourier
+    serving.knative.dev/release: "v0.26.0"
+spec:
+  ports:
+    - name: http2
+      port: 80
+      protocol: TCP
+      targetPort: 8080
+    - name: https
+      port: 443
+      protocol: TCP
+      targetPort: 8443
+  selector:
+    app: 3scale-kourier-gateway
+  externalTrafficPolicy: Local
+  type: LoadBalancer
+```
+
 ## License
 
 [Apache 2.0 License](LICENSE)
diff --git a/config/200-config.yaml b/config/200-config.yaml
index 4d5be5d1..73c63cfc 100644
--- a/config/200-config.yaml
+++ b/config/200-config.yaml
@@ -44,3 +44,9 @@ data:
     # in the context of services should be logged. Readiness
     # probes etc. must be configured via the bootstrap config.
     enable-service-access-logging: "true"
+
+    # Specifies whether to use proxy-protocol in order to safely
+    # transport connection information such as a client's address
+    # across multiple layers of TCP proxies.
+    # NOTE THAT THIS IS AN EXPERIMENTAL / ALPHA FEATURE
+    enable-proxy-protocol: "false"
diff --git a/config/300-gateway.yaml b/config/300-gateway.yaml
index 82aabd6b..38eccf11 100644
--- a/config/300-gateway.yaml
+++ b/config/300-gateway.yaml
@@ -60,6 +60,12 @@ spec:
             - name: https-external
               containerPort: 8443
               protocol: TCP
+            - name: http-probe
+              containerPort: 8090
+              protocol: TCP
+            - name: https-probe
+              containerPort: 9443
+              protocol: TCP
           securityContext:
             allowPrivilegeEscalation: false
             readOnlyRootFilesystem: false
diff --git a/go.mod b/go.mod
index a90cc426..d04f21b9 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,9 @@ require (
 	github.com/google/uuid v1.3.0
 	github.com/kelseyhightower/envconfig v1.4.0
 	github.com/patrickmn/go-cache v2.1.0+incompatible
+	github.com/pires/go-proxyproto v0.6.1
 	go.uber.org/zap v1.19.1
+	golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe // indirect
 	google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12
 	google.golang.org/grpc v1.42.0
 	google.golang.org/protobuf v1.27.1
diff --git a/go.sum b/go.sum
index 72873bea..61e2100a 100644
--- a/go.sum
+++ b/go.sum
@@ -475,6 +475,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
 github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
 github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/pires/go-proxyproto v0.6.1 h1:EBupykFmo22SDjv4fQVQd2J9NOoLPmyZA/15ldOGkPw=
+github.com/pires/go-proxyproto v0.6.1/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -816,8 +818,9 @@ golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/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-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc=
 golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe h1:W8vbETX/n8S6EmY0Pu4Ix7VvpsJUESTwl0oCK8MJOgk=
+golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
diff --git a/pkg/config/config.go b/pkg/config/config.go
index e9a974a2..694c9824 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -38,6 +38,10 @@ const (
 	HTTPPortInternal = uint32(8081)
 	// HTTPSPortExternal is the port for external HTTPS availability.
 	HTTPSPortExternal = uint32(8443)
+	// HTTPPortProb is the port for prob
+	HTTPPortProb = uint32(8090)
+	// HTTPSPortProb is the port for prob
+	HTTPSPortProb = uint32(9443)
 
 	// InternalKourierDomain is an internal envoy endpoint.
 	InternalKourierDomain = "internalkourier"
diff --git a/pkg/config/configmap.go b/pkg/config/configmap.go
index 8945407f..68314531 100644
--- a/pkg/config/configmap.go
+++ b/pkg/config/configmap.go
@@ -29,11 +29,15 @@ const (
 	// enableServiceAccessLoggingKey is the config map key for enabling service related
 	// access logging.
 	enableServiceAccessLoggingKey = "enable-service-access-logging"
+
+	// enableProxyProtocol is the config map key for enabling proxy protocol
+	enableProxyProtocol = "enable-proxy-protocol"
 )
 
 func DefaultConfig() *Kourier {
 	return &Kourier{
 		EnableServiceAccessLogging: true, // true is the default for backwards-compat
+		EnableProxyProtocol:        false,
 	}
 }
 
@@ -43,6 +47,7 @@ func NewConfigFromMap(configMap map[string]string) (*Kourier, error) {
 
 	if err := cm.Parse(configMap,
 		cm.AsBool(enableServiceAccessLoggingKey, &nc.EnableServiceAccessLogging),
+		cm.AsBool(enableProxyProtocol, &nc.EnableProxyProtocol),
 	); err != nil {
 		return nil, err
 	}
@@ -61,4 +66,6 @@ type Kourier struct {
 	// EnableServiceAccessLogging specifies whether requests reaching the Kourier gateway
 	// should be logged.
 	EnableServiceAccessLogging bool
+	// EnableProxyProtocol specifies whether proxy protocol feature is enabled
+	EnableProxyProtocol bool
 }
diff --git a/pkg/envoy/api/http_connection_manager.go b/pkg/envoy/api/http_connection_manager.go
index d574b02a..41fc8ae5 100644
--- a/pkg/envoy/api/http_connection_manager.go
+++ b/pkg/envoy/api/http_connection_manager.go
@@ -35,7 +35,7 @@ import (
 
 // NewHTTPConnectionManager creates a new HttpConnectionManager that points to the given
 // RouteConfig for further configuration.
-func NewHTTPConnectionManager(routeConfigName string, enableAccessLog bool) *hcm.HttpConnectionManager {
+func NewHTTPConnectionManager(routeConfigName string, enableAccessLog, enableProxyProtocol bool) *hcm.HttpConnectionManager {
 	filters := make([]*hcm.HttpFilter, 0, 1)
 
 	if config.ExternalAuthz.Enabled {
@@ -65,6 +65,11 @@ func NewHTTPConnectionManager(routeConfigName string, enableAccessLog bool) *hcm
 		},
 	}
 
+	if enableProxyProtocol {
+		//Force the connection manager to use the real remote address of the client connection.
+		mgr.UseRemoteAddress = &wrapperspb.BoolValue{Value: true}
+	}
+
 	if enableAccessLog {
 		// Write access logs to stdout by default.
 		accessLog, _ := anypb.New(&accesslog_file_v3.FileAccessLog{
diff --git a/pkg/envoy/api/listener.go b/pkg/envoy/api/listener.go
index ed0ca865..b98221e2 100644
--- a/pkg/envoy/api/listener.go
+++ b/pkg/envoy/api/listener.go
@@ -21,6 +21,7 @@ import (
 
 	core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
 	listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+	prx "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/proxy_protocol/v3"
 	hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
 	auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
 	"github.com/envoyproxy/go-control-plane/pkg/wellknown"
@@ -38,15 +39,25 @@ type SNIMatch struct {
 }
 
 // NewHTTPListener creates a new Listener at the given port, backed by the given manager.
-func NewHTTPListener(manager *hcm.HttpConnectionManager, port uint32) (*listener.Listener, error) {
+func NewHTTPListener(manager *hcm.HttpConnectionManager, port uint32, enableProxyProtocol bool) (*listener.Listener, error) {
 	filters, err := createFilters(manager)
 	if err != nil {
 		return nil, err
 	}
 
+	var listenerFilter []*listener.ListenerFilter
+	if enableProxyProtocol {
+		proxyProtocolListenerFilter, err := createProxyProtocolListenerFilter()
+		if err != nil {
+			return nil, err
+		}
+		listenerFilter = append(listenerFilter, proxyProtocolListenerFilter)
+	}
+
 	return &listener.Listener{
-		Name:    CreateListenerName(port),
-		Address: createAddress(port),
+		Name:            CreateListenerName(port),
+		Address:         createAddress(port),
+		ListenerFilters: listenerFilter,
 		FilterChains: []*listener.FilterChain{{
 			Filters: filters,
 		}},
@@ -54,11 +65,21 @@ func NewHTTPListener(manager *hcm.HttpConnectionManager, port uint32) (*listener
 }
 
 // NewHTTPSListener creates a new Listener at the given port with a given filter chain
-func NewHTTPSListener(port uint32, filterChain *listener.FilterChain) (*listener.Listener, error) {
+func NewHTTPSListener(port uint32, filterChain []*listener.FilterChain, enableProxyProtocol bool) (*listener.Listener, error) {
+	var listenerFilter []*listener.ListenerFilter
+	if enableProxyProtocol {
+		proxyProtocolListenerFilter, err := createProxyProtocolListenerFilter()
+		if err != nil {
+			return nil, err
+		}
+		listenerFilter = append(listenerFilter, proxyProtocolListenerFilter)
+	}
+
 	return &listener.Listener{
-		Name:         CreateListenerName(port),
-		Address:      createAddress(port),
-		FilterChains: []*listener.FilterChain{filterChain},
+		Name:            CreateListenerName(port),
+		Address:         createAddress(port),
+		ListenerFilters: listenerFilter,
+		FilterChains:    filterChain,
 	}, nil
 }
 
@@ -92,22 +113,34 @@ func CreateFilterChainFromCertificateAndPrivateKey(
 // manager and applies a FilterChain with the given sniMatches.
 //
 // Ref: https://www.envoyproxy.io/docs/envoy/latest/faq/configuration/sni.html
-func NewHTTPSListenerWithSNI(manager *hcm.HttpConnectionManager, port uint32, sniMatches []*SNIMatch) (*listener.Listener, error) {
+func NewHTTPSListenerWithSNI(manager *hcm.HttpConnectionManager, port uint32, sniMatches []*SNIMatch, enableProxyProtocol bool) (*listener.Listener, error) {
 	filterChains, err := createFilterChainsForTLS(manager, sniMatches)
 	if err != nil {
 		return nil, err
 	}
 
-	return &listener.Listener{
-		Name:         CreateListenerName(port),
-		Address:      createAddress(port),
-		FilterChains: filterChains,
-		ListenerFilters: []*listener.ListenerFilter{{
+	listenerFilter := []*listener.ListenerFilter{
+		{
 			// TLS Inspector listener filter must be configured in order to
 			// detect requested SNI.
 			// Ref: https://www.envoyproxy.io/docs/envoy/latest/faq/configuration/sni.html
 			Name: wellknown.TlsInspector,
-		}},
+		},
+	}
+
+	if enableProxyProtocol {
+		proxyProtocolListenerFilter, err := createProxyProtocolListenerFilter()
+		if err != nil {
+			return nil, err
+		}
+		listenerFilter = append(listenerFilter, proxyProtocolListenerFilter)
+	}
+
+	return &listener.Listener{
+		Name:            CreateListenerName(port),
+		Address:         createAddress(port),
+		FilterChains:    filterChains,
+		ListenerFilters: listenerFilter,
 	}, nil
 }
 
@@ -188,3 +221,18 @@ func createTLSContext(certificate []byte, privateKey []byte) *auth.DownstreamTls
 		},
 	}
 }
+
+// Ref: https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/listener_filters/proxy_protocol
+func createProxyProtocolListenerFilter() (listenerFilter *listener.ListenerFilter, err error) {
+	listenerFilterConfig, err := anypb.New(&prx.ProxyProtocol{})
+	if err != nil {
+		return nil, err
+	}
+
+	return &listener.ListenerFilter{
+		Name: wellknown.ProxyProtocol,
+		ConfigType: &listener.ListenerFilter_TypedConfig{
+			TypedConfig: listenerFilterConfig,
+		},
+	}, nil
+}
diff --git a/pkg/generator/caches.go b/pkg/generator/caches.go
index d3b9c621..fe7f89ec 100644
--- a/pkg/generator/caches.go
+++ b/pkg/generator/caches.go
@@ -219,14 +219,14 @@ func generateListenersAndRouteConfigs(
 	internalRouteConfig := envoy.NewRouteConfig(internalRouteConfigName, clusterLocalVirtualHosts)
 
 	// Now we setup connection managers, that reference the routeconfigs via RDS.
-	externalManager := envoy.NewHTTPConnectionManager(externalRouteConfig.Name, cfg.Kourier.EnableServiceAccessLogging)
-	externalTLSManager := envoy.NewHTTPConnectionManager(externalTLSRouteConfig.Name, cfg.Kourier.EnableServiceAccessLogging)
-	internalManager := envoy.NewHTTPConnectionManager(internalRouteConfig.Name, cfg.Kourier.EnableServiceAccessLogging)
-	externalHTTPEnvoyListener, err := envoy.NewHTTPListener(externalManager, config.HTTPPortExternal)
+	externalManager := envoy.NewHTTPConnectionManager(externalRouteConfig.Name, cfg.Kourier.EnableServiceAccessLogging, cfg.Kourier.EnableProxyProtocol)
+	externalTLSManager := envoy.NewHTTPConnectionManager(externalTLSRouteConfig.Name, cfg.Kourier.EnableServiceAccessLogging, cfg.Kourier.EnableProxyProtocol)
+	internalManager := envoy.NewHTTPConnectionManager(internalRouteConfig.Name, cfg.Kourier.EnableServiceAccessLogging, cfg.Kourier.EnableProxyProtocol)
+	externalHTTPEnvoyListener, err := envoy.NewHTTPListener(externalManager, config.HTTPPortExternal, cfg.Kourier.EnableProxyProtocol)
 	if err != nil {
 		return nil, nil, err
 	}
-	internalEnvoyListener, err := envoy.NewHTTPListener(internalManager, config.HTTPPortInternal)
+	internalEnvoyListener, err := envoy.NewHTTPListener(internalManager, config.HTTPPortInternal, false)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -234,11 +234,30 @@ func generateListenersAndRouteConfigs(
 	listeners := []cachetypes.Resource{externalHTTPEnvoyListener, internalEnvoyListener}
 	routes := []cachetypes.Resource{externalRouteConfig, internalRouteConfig}
 
+	// create probe listeners
+	probHTTPListener, err := envoy.NewHTTPListener(externalManager, config.HTTPPortProb, false)
+	if err != nil {
+		return nil, nil, err
+	}
+	listeners = append(listeners, probHTTPListener)
+
 	// Configure TLS Listener. If there's at least one ingress that contains the
 	// TLS field, that takes precedence. If there is not, TLS will be configured
 	// using a single cert for all the services if the creds are given via ENV.
 	if len(sniMatches) > 0 {
-		externalHTTPSEnvoyListener, err := envoy.NewHTTPSListenerWithSNI(externalTLSManager, config.HTTPSPortExternal, sniMatches)
+		externalHTTPSEnvoyListener, err := envoy.NewHTTPSListenerWithSNI(
+			externalTLSManager, config.HTTPSPortExternal,
+			sniMatches, cfg.Kourier.EnableProxyProtocol,
+		)
+		if err != nil {
+			return nil, nil, err
+		}
+
+		// create https prob listener with SNI
+		probHTTPSListener, err := envoy.NewHTTPSListenerWithSNI(
+			externalManager, config.HTTPSPortProb,
+			sniMatches, false,
+		)
 		if err != nil {
 			return nil, nil, err
 		}
@@ -254,18 +273,28 @@ func generateListenersAndRouteConfigs(
 
 			externalHTTPSEnvoyListener.FilterChains = append(externalHTTPSEnvoyListener.FilterChains,
 				externalHTTPSEnvoyListenerWithOneCertFilterChain)
+			probHTTPSListener.FilterChains = append(probHTTPSListener.FilterChains,
+				externalHTTPSEnvoyListenerWithOneCertFilterChain)
 		}
 
-		listeners = append(listeners, externalHTTPSEnvoyListener)
+		listeners = append(listeners, externalHTTPSEnvoyListener, probHTTPSListener)
 		routes = append(routes, externalTLSRouteConfig)
 	} else if useHTTPSListenerWithOneCert() {
 		externalHTTPSEnvoyListener, err := newExternalEnvoyListenerWithOneCert(
 			ctx, externalTLSManager, kubeclient,
+			cfg.Kourier.EnableProxyProtocol,
 		)
 		if err != nil {
 			return nil, nil, err
 		}
-		listeners = append(listeners, externalHTTPSEnvoyListener)
+
+		// create https prob listener
+		probHTTPSListener, err := envoy.NewHTTPSListener(config.HTTPSPortProb, externalHTTPSEnvoyListener.FilterChains, false)
+		if err != nil {
+			return nil, nil, err
+		}
+
+		listeners = append(listeners, externalHTTPSEnvoyListener, probHTTPSListener)
 		routes = append(routes, externalTLSRouteConfig)
 	}
 
@@ -299,11 +328,11 @@ func newExternalEnvoyListenerWithOneCertFilterChain(ctx context.Context, manager
 	return envoy.CreateFilterChainFromCertificateAndPrivateKey(manager, certificateChain, privateKey)
 }
 
-func newExternalEnvoyListenerWithOneCert(ctx context.Context, manager *httpconnmanagerv3.HttpConnectionManager, kubeClient kubeclient.Interface) (*v3.Listener, error) {
+func newExternalEnvoyListenerWithOneCert(ctx context.Context, manager *httpconnmanagerv3.HttpConnectionManager, kubeClient kubeclient.Interface, enableProxyProtocol bool) (*v3.Listener, error) {
 	filterChain, err := newExternalEnvoyListenerWithOneCertFilterChain(ctx, manager, kubeClient)
 	if err != nil {
 		return nil, err
 	}
 
-	return envoy.NewHTTPSListener(config.HTTPSPortExternal, filterChain)
+	return envoy.NewHTTPSListener(config.HTTPSPortExternal, []*v3.FilterChain{filterChain}, enableProxyProtocol)
 }
diff --git a/pkg/reconciler/ingress/lister.go b/pkg/reconciler/ingress/lister.go
index 6afc12ca..bef01f0e 100644
--- a/pkg/reconciler/ingress/lister.go
+++ b/pkg/reconciler/ingress/lister.go
@@ -75,10 +75,10 @@ func (l *gatewayPodTargetLister) getIngressUrls(ing *v1alpha1.Ingress, gatewayIp
 				PodIPs: ips,
 			}
 			if len(ing.Spec.TLS) != 0 {
-				target.PodPort = strconv.Itoa(int(config.HTTPSPortExternal))
+				target.PodPort = strconv.Itoa(int(config.HTTPSPortProb))
 				target.URLs = domainsToURL(domains, "https")
 			} else {
-				target.PodPort = strconv.Itoa(int(config.HTTPPortExternal))
+				target.PodPort = strconv.Itoa(int(config.HTTPPortProb))
 				target.URLs = domainsToURL(domains, scheme)
 			}
 		} else {
diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.pb.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.pb.go
new file mode 100644
index 00000000..1ef50c3d
--- /dev/null
+++ b/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.pb.go
@@ -0,0 +1,337 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.25.0
+// 	protoc        v3.19.1
+// source: envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.proto
+
+package envoy_extensions_filters_listener_proxy_protocol_v3
+
+import (
+	_ "github.com/cncf/xds/go/udpa/annotations"
+	_ "github.com/envoyproxy/protoc-gen-validate/validate"
+	proto "github.com/golang/protobuf/proto"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// This is a compile-time assertion that a sufficiently up-to-date version
+// of the legacy proto package is being used.
+const _ = proto.ProtoPackageIsVersion4
+
+type ProxyProtocol struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The list of rules to apply to requests.
+	Rules []*ProxyProtocol_Rule `protobuf:"bytes,1,rep,name=rules,proto3" json:"rules,omitempty"`
+}
+
+func (x *ProxyProtocol) Reset() {
+	*x = ProxyProtocol{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ProxyProtocol) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ProxyProtocol) ProtoMessage() {}
+
+func (x *ProxyProtocol) ProtoReflect() protoreflect.Message {
+	mi := &file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ProxyProtocol.ProtoReflect.Descriptor instead.
+func (*ProxyProtocol) Descriptor() ([]byte, []int) {
+	return file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *ProxyProtocol) GetRules() []*ProxyProtocol_Rule {
+	if x != nil {
+		return x.Rules
+	}
+	return nil
+}
+
+type ProxyProtocol_KeyValuePair struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The namespace — if this is empty, the filter's namespace will be used.
+	MetadataNamespace string `protobuf:"bytes,1,opt,name=metadata_namespace,json=metadataNamespace,proto3" json:"metadata_namespace,omitempty"`
+	// The key to use within the namespace.
+	Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
+}
+
+func (x *ProxyProtocol_KeyValuePair) Reset() {
+	*x = ProxyProtocol_KeyValuePair{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ProxyProtocol_KeyValuePair) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ProxyProtocol_KeyValuePair) ProtoMessage() {}
+
+func (x *ProxyProtocol_KeyValuePair) ProtoReflect() protoreflect.Message {
+	mi := &file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ProxyProtocol_KeyValuePair.ProtoReflect.Descriptor instead.
+func (*ProxyProtocol_KeyValuePair) Descriptor() ([]byte, []int) {
+	return file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_rawDescGZIP(), []int{0, 0}
+}
+
+func (x *ProxyProtocol_KeyValuePair) GetMetadataNamespace() string {
+	if x != nil {
+		return x.MetadataNamespace
+	}
+	return ""
+}
+
+func (x *ProxyProtocol_KeyValuePair) GetKey() string {
+	if x != nil {
+		return x.Key
+	}
+	return ""
+}
+
+// A Rule defines what metadata to apply when a header is present or missing.
+type ProxyProtocol_Rule struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The type that triggers the rule - required
+	// TLV type is defined as uint8_t in proxy protocol. See `the spec
+	// <https://www.haproxy.org/download/2.1/doc/proxy-protocol.txt>`_ for details.
+	TlvType uint32 `protobuf:"varint,1,opt,name=tlv_type,json=tlvType,proto3" json:"tlv_type,omitempty"`
+	// If the TLV type is present, apply this metadata KeyValuePair.
+	OnTlvPresent *ProxyProtocol_KeyValuePair `protobuf:"bytes,2,opt,name=on_tlv_present,json=onTlvPresent,proto3" json:"on_tlv_present,omitempty"`
+}
+
+func (x *ProxyProtocol_Rule) Reset() {
+	*x = ProxyProtocol_Rule{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ProxyProtocol_Rule) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ProxyProtocol_Rule) ProtoMessage() {}
+
+func (x *ProxyProtocol_Rule) ProtoReflect() protoreflect.Message {
+	mi := &file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ProxyProtocol_Rule.ProtoReflect.Descriptor instead.
+func (*ProxyProtocol_Rule) Descriptor() ([]byte, []int) {
+	return file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_rawDescGZIP(), []int{0, 1}
+}
+
+func (x *ProxyProtocol_Rule) GetTlvType() uint32 {
+	if x != nil {
+		return x.TlvType
+	}
+	return 0
+}
+
+func (x *ProxyProtocol_Rule) GetOnTlvPresent() *ProxyProtocol_KeyValuePair {
+	if x != nil {
+		return x.OnTlvPresent
+	}
+	return nil
+}
+
+var File_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto protoreflect.FileDescriptor
+
+var file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_rawDesc = []byte{
+	0x0a, 0x48, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
+	0x6e, 0x73, 0x2f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2f, 0x6c, 0x69, 0x73, 0x74, 0x65,
+	0x6e, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+	0x6f, 0x6c, 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x33, 0x65, 0x6e, 0x76, 0x6f,
+	0x79, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x69, 0x6c,
+	0x74, 0x65, 0x72, 0x73, 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x2e, 0x70, 0x72,
+	0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76, 0x33, 0x1a,
+	0x1d, 0x75, 0x64, 0x70, 0x61, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21,
+	0x75, 0x64, 0x70, 0x61, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+	0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69,
+	0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb2, 0x03, 0x0a, 0x0d, 0x50,
+	0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x5d, 0x0a, 0x05,
+	0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e, 0x65, 0x6e,
+	0x76, 0x6f, 0x79, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66,
+	0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x2e,
+	0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76,
+	0x33, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,
+	0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x1a, 0x58, 0x0a, 0x0c, 0x4b,
+	0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x2d, 0x0a, 0x12, 0x6d,
+	0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63,
+	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
+	0x61, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x19, 0x0a, 0x03, 0x6b, 0x65,
+	0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01,
+	0x52, 0x03, 0x6b, 0x65, 0x79, 0x1a, 0xa2, 0x01, 0x0a, 0x04, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x23,
+	0x0a, 0x08, 0x74, 0x6c, 0x76, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
+	0x42, 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, 0x10, 0x80, 0x02, 0x52, 0x07, 0x74, 0x6c, 0x76, 0x54,
+	0x79, 0x70, 0x65, 0x12, 0x75, 0x0a, 0x0e, 0x6f, 0x6e, 0x5f, 0x74, 0x6c, 0x76, 0x5f, 0x70, 0x72,
+	0x65, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x4f, 0x2e, 0x65, 0x6e,
+	0x76, 0x6f, 0x79, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66,
+	0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x2e,
+	0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76,
+	0x33, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,
+	0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x0c, 0x6f, 0x6e,
+	0x54, 0x6c, 0x76, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x3a, 0x43, 0x9a, 0xc5, 0x88, 0x1e,
+	0x3e, 0x0a, 0x3c, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
+	0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x2e,
+	0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x76,
+	0x32, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x42,
+	0x61, 0x0a, 0x41, 0x69, 0x6f, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x70, 0x72, 0x6f, 0x78, 0x79,
+	0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+	0x73, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e,
+	0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,
+	0x6c, 0x2e, 0x76, 0x33, 0x42, 0x12, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f,
+	0x63, 0x6f, 0x6c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0xba, 0x80, 0xc8, 0xd1, 0x06, 0x02,
+	0x10, 0x02, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_rawDescOnce sync.Once
+	file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_rawDescData = file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_rawDesc
+)
+
+func file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_rawDescGZIP() []byte {
+	file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_rawDescOnce.Do(func() {
+		file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_rawDescData = protoimpl.X.CompressGZIP(file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_rawDescData)
+	})
+	return file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_rawDescData
+}
+
+var file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
+var file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_goTypes = []interface{}{
+	(*ProxyProtocol)(nil),              // 0: envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol
+	(*ProxyProtocol_KeyValuePair)(nil), // 1: envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol.KeyValuePair
+	(*ProxyProtocol_Rule)(nil),         // 2: envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol.Rule
+}
+var file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_depIdxs = []int32{
+	2, // 0: envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol.rules:type_name -> envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol.Rule
+	1, // 1: envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol.Rule.on_tlv_present:type_name -> envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol.KeyValuePair
+	2, // [2:2] is the sub-list for method output_type
+	2, // [2:2] is the sub-list for method input_type
+	2, // [2:2] is the sub-list for extension type_name
+	2, // [2:2] is the sub-list for extension extendee
+	0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_init() }
+func file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_init() {
+	if File_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ProxyProtocol); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ProxyProtocol_KeyValuePair); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ProxyProtocol_Rule); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   3,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_goTypes,
+		DependencyIndexes: file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_depIdxs,
+		MessageInfos:      file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_msgTypes,
+	}.Build()
+	File_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto = out.File
+	file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_rawDesc = nil
+	file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_goTypes = nil
+	file_envoy_extensions_filters_listener_proxy_protocol_v3_proxy_protocol_proto_depIdxs = nil
+}
diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.pb.validate.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.pb.validate.go
new file mode 100644
index 00000000..53dd991a
--- /dev/null
+++ b/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.pb.validate.go
@@ -0,0 +1,424 @@
+// Code generated by protoc-gen-validate. DO NOT EDIT.
+// source: envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.proto
+
+package envoy_extensions_filters_listener_proxy_protocol_v3
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"net"
+	"net/mail"
+	"net/url"
+	"regexp"
+	"sort"
+	"strings"
+	"time"
+	"unicode/utf8"
+
+	"google.golang.org/protobuf/types/known/anypb"
+)
+
+// ensure the imports are used
+var (
+	_ = bytes.MinRead
+	_ = errors.New("")
+	_ = fmt.Print
+	_ = utf8.UTFMax
+	_ = (*regexp.Regexp)(nil)
+	_ = (*strings.Reader)(nil)
+	_ = net.IPv4len
+	_ = time.Duration(0)
+	_ = (*url.URL)(nil)
+	_ = (*mail.Address)(nil)
+	_ = anypb.Any{}
+	_ = sort.Sort
+)
+
+// Validate checks the field values on ProxyProtocol with the rules defined in
+// the proto definition for this message. If any rules are violated, the first
+// error encountered is returned, or nil if there are no violations.
+func (m *ProxyProtocol) Validate() error {
+	return m.validate(false)
+}
+
+// ValidateAll checks the field values on ProxyProtocol with the rules defined
+// in the proto definition for this message. If any rules are violated, the
+// result is a list of violation errors wrapped in ProxyProtocolMultiError, or
+// nil if none found.
+func (m *ProxyProtocol) ValidateAll() error {
+	return m.validate(true)
+}
+
+func (m *ProxyProtocol) validate(all bool) error {
+	if m == nil {
+		return nil
+	}
+
+	var errors []error
+
+	for idx, item := range m.GetRules() {
+		_, _ = idx, item
+
+		if all {
+			switch v := interface{}(item).(type) {
+			case interface{ ValidateAll() error }:
+				if err := v.ValidateAll(); err != nil {
+					errors = append(errors, ProxyProtocolValidationError{
+						field:  fmt.Sprintf("Rules[%v]", idx),
+						reason: "embedded message failed validation",
+						cause:  err,
+					})
+				}
+			case interface{ Validate() error }:
+				if err := v.Validate(); err != nil {
+					errors = append(errors, ProxyProtocolValidationError{
+						field:  fmt.Sprintf("Rules[%v]", idx),
+						reason: "embedded message failed validation",
+						cause:  err,
+					})
+				}
+			}
+		} else if v, ok := interface{}(item).(interface{ Validate() error }); ok {
+			if err := v.Validate(); err != nil {
+				return ProxyProtocolValidationError{
+					field:  fmt.Sprintf("Rules[%v]", idx),
+					reason: "embedded message failed validation",
+					cause:  err,
+				}
+			}
+		}
+
+	}
+
+	if len(errors) > 0 {
+		return ProxyProtocolMultiError(errors)
+	}
+	return nil
+}
+
+// ProxyProtocolMultiError is an error wrapping multiple validation errors
+// returned by ProxyProtocol.ValidateAll() if the designated constraints
+// aren't met.
+type ProxyProtocolMultiError []error
+
+// Error returns a concatenation of all the error messages it wraps.
+func (m ProxyProtocolMultiError) Error() string {
+	var msgs []string
+	for _, err := range m {
+		msgs = append(msgs, err.Error())
+	}
+	return strings.Join(msgs, "; ")
+}
+
+// AllErrors returns a list of validation violation errors.
+func (m ProxyProtocolMultiError) AllErrors() []error { return m }
+
+// ProxyProtocolValidationError is the validation error returned by
+// ProxyProtocol.Validate if the designated constraints aren't met.
+type ProxyProtocolValidationError struct {
+	field  string
+	reason string
+	cause  error
+	key    bool
+}
+
+// Field function returns field value.
+func (e ProxyProtocolValidationError) Field() string { return e.field }
+
+// Reason function returns reason value.
+func (e ProxyProtocolValidationError) Reason() string { return e.reason }
+
+// Cause function returns cause value.
+func (e ProxyProtocolValidationError) Cause() error { return e.cause }
+
+// Key function returns key value.
+func (e ProxyProtocolValidationError) Key() bool { return e.key }
+
+// ErrorName returns error name.
+func (e ProxyProtocolValidationError) ErrorName() string { return "ProxyProtocolValidationError" }
+
+// Error satisfies the builtin error interface
+func (e ProxyProtocolValidationError) Error() string {
+	cause := ""
+	if e.cause != nil {
+		cause = fmt.Sprintf(" | caused by: %v", e.cause)
+	}
+
+	key := ""
+	if e.key {
+		key = "key for "
+	}
+
+	return fmt.Sprintf(
+		"invalid %sProxyProtocol.%s: %s%s",
+		key,
+		e.field,
+		e.reason,
+		cause)
+}
+
+var _ error = ProxyProtocolValidationError{}
+
+var _ interface {
+	Field() string
+	Reason() string
+	Key() bool
+	Cause() error
+	ErrorName() string
+} = ProxyProtocolValidationError{}
+
+// Validate checks the field values on ProxyProtocol_KeyValuePair with the
+// rules defined in the proto definition for this message. If any rules are
+// violated, the first error encountered is returned, or nil if there are no violations.
+func (m *ProxyProtocol_KeyValuePair) Validate() error {
+	return m.validate(false)
+}
+
+// ValidateAll checks the field values on ProxyProtocol_KeyValuePair with the
+// rules defined in the proto definition for this message. If any rules are
+// violated, the result is a list of violation errors wrapped in
+// ProxyProtocol_KeyValuePairMultiError, or nil if none found.
+func (m *ProxyProtocol_KeyValuePair) ValidateAll() error {
+	return m.validate(true)
+}
+
+func (m *ProxyProtocol_KeyValuePair) validate(all bool) error {
+	if m == nil {
+		return nil
+	}
+
+	var errors []error
+
+	// no validation rules for MetadataNamespace
+
+	if utf8.RuneCountInString(m.GetKey()) < 1 {
+		err := ProxyProtocol_KeyValuePairValidationError{
+			field:  "Key",
+			reason: "value length must be at least 1 runes",
+		}
+		if !all {
+			return err
+		}
+		errors = append(errors, err)
+	}
+
+	if len(errors) > 0 {
+		return ProxyProtocol_KeyValuePairMultiError(errors)
+	}
+	return nil
+}
+
+// ProxyProtocol_KeyValuePairMultiError is an error wrapping multiple
+// validation errors returned by ProxyProtocol_KeyValuePair.ValidateAll() if
+// the designated constraints aren't met.
+type ProxyProtocol_KeyValuePairMultiError []error
+
+// Error returns a concatenation of all the error messages it wraps.
+func (m ProxyProtocol_KeyValuePairMultiError) Error() string {
+	var msgs []string
+	for _, err := range m {
+		msgs = append(msgs, err.Error())
+	}
+	return strings.Join(msgs, "; ")
+}
+
+// AllErrors returns a list of validation violation errors.
+func (m ProxyProtocol_KeyValuePairMultiError) AllErrors() []error { return m }
+
+// ProxyProtocol_KeyValuePairValidationError is the validation error returned
+// by ProxyProtocol_KeyValuePair.Validate if the designated constraints aren't met.
+type ProxyProtocol_KeyValuePairValidationError struct {
+	field  string
+	reason string
+	cause  error
+	key    bool
+}
+
+// Field function returns field value.
+func (e ProxyProtocol_KeyValuePairValidationError) Field() string { return e.field }
+
+// Reason function returns reason value.
+func (e ProxyProtocol_KeyValuePairValidationError) Reason() string { return e.reason }
+
+// Cause function returns cause value.
+func (e ProxyProtocol_KeyValuePairValidationError) Cause() error { return e.cause }
+
+// Key function returns key value.
+func (e ProxyProtocol_KeyValuePairValidationError) Key() bool { return e.key }
+
+// ErrorName returns error name.
+func (e ProxyProtocol_KeyValuePairValidationError) ErrorName() string {
+	return "ProxyProtocol_KeyValuePairValidationError"
+}
+
+// Error satisfies the builtin error interface
+func (e ProxyProtocol_KeyValuePairValidationError) Error() string {
+	cause := ""
+	if e.cause != nil {
+		cause = fmt.Sprintf(" | caused by: %v", e.cause)
+	}
+
+	key := ""
+	if e.key {
+		key = "key for "
+	}
+
+	return fmt.Sprintf(
+		"invalid %sProxyProtocol_KeyValuePair.%s: %s%s",
+		key,
+		e.field,
+		e.reason,
+		cause)
+}
+
+var _ error = ProxyProtocol_KeyValuePairValidationError{}
+
+var _ interface {
+	Field() string
+	Reason() string
+	Key() bool
+	Cause() error
+	ErrorName() string
+} = ProxyProtocol_KeyValuePairValidationError{}
+
+// Validate checks the field values on ProxyProtocol_Rule with the rules
+// defined in the proto definition for this message. If any rules are
+// violated, the first error encountered is returned, or nil if there are no violations.
+func (m *ProxyProtocol_Rule) Validate() error {
+	return m.validate(false)
+}
+
+// ValidateAll checks the field values on ProxyProtocol_Rule with the rules
+// defined in the proto definition for this message. If any rules are
+// violated, the result is a list of violation errors wrapped in
+// ProxyProtocol_RuleMultiError, or nil if none found.
+func (m *ProxyProtocol_Rule) ValidateAll() error {
+	return m.validate(true)
+}
+
+func (m *ProxyProtocol_Rule) validate(all bool) error {
+	if m == nil {
+		return nil
+	}
+
+	var errors []error
+
+	if m.GetTlvType() >= 256 {
+		err := ProxyProtocol_RuleValidationError{
+			field:  "TlvType",
+			reason: "value must be less than 256",
+		}
+		if !all {
+			return err
+		}
+		errors = append(errors, err)
+	}
+
+	if all {
+		switch v := interface{}(m.GetOnTlvPresent()).(type) {
+		case interface{ ValidateAll() error }:
+			if err := v.ValidateAll(); err != nil {
+				errors = append(errors, ProxyProtocol_RuleValidationError{
+					field:  "OnTlvPresent",
+					reason: "embedded message failed validation",
+					cause:  err,
+				})
+			}
+		case interface{ Validate() error }:
+			if err := v.Validate(); err != nil {
+				errors = append(errors, ProxyProtocol_RuleValidationError{
+					field:  "OnTlvPresent",
+					reason: "embedded message failed validation",
+					cause:  err,
+				})
+			}
+		}
+	} else if v, ok := interface{}(m.GetOnTlvPresent()).(interface{ Validate() error }); ok {
+		if err := v.Validate(); err != nil {
+			return ProxyProtocol_RuleValidationError{
+				field:  "OnTlvPresent",
+				reason: "embedded message failed validation",
+				cause:  err,
+			}
+		}
+	}
+
+	if len(errors) > 0 {
+		return ProxyProtocol_RuleMultiError(errors)
+	}
+	return nil
+}
+
+// ProxyProtocol_RuleMultiError is an error wrapping multiple validation errors
+// returned by ProxyProtocol_Rule.ValidateAll() if the designated constraints
+// aren't met.
+type ProxyProtocol_RuleMultiError []error
+
+// Error returns a concatenation of all the error messages it wraps.
+func (m ProxyProtocol_RuleMultiError) Error() string {
+	var msgs []string
+	for _, err := range m {
+		msgs = append(msgs, err.Error())
+	}
+	return strings.Join(msgs, "; ")
+}
+
+// AllErrors returns a list of validation violation errors.
+func (m ProxyProtocol_RuleMultiError) AllErrors() []error { return m }
+
+// ProxyProtocol_RuleValidationError is the validation error returned by
+// ProxyProtocol_Rule.Validate if the designated constraints aren't met.
+type ProxyProtocol_RuleValidationError struct {
+	field  string
+	reason string
+	cause  error
+	key    bool
+}
+
+// Field function returns field value.
+func (e ProxyProtocol_RuleValidationError) Field() string { return e.field }
+
+// Reason function returns reason value.
+func (e ProxyProtocol_RuleValidationError) Reason() string { return e.reason }
+
+// Cause function returns cause value.
+func (e ProxyProtocol_RuleValidationError) Cause() error { return e.cause }
+
+// Key function returns key value.
+func (e ProxyProtocol_RuleValidationError) Key() bool { return e.key }
+
+// ErrorName returns error name.
+func (e ProxyProtocol_RuleValidationError) ErrorName() string {
+	return "ProxyProtocol_RuleValidationError"
+}
+
+// Error satisfies the builtin error interface
+func (e ProxyProtocol_RuleValidationError) Error() string {
+	cause := ""
+	if e.cause != nil {
+		cause = fmt.Sprintf(" | caused by: %v", e.cause)
+	}
+
+	key := ""
+	if e.key {
+		key = "key for "
+	}
+
+	return fmt.Sprintf(
+		"invalid %sProxyProtocol_Rule.%s: %s%s",
+		key,
+		e.field,
+		e.reason,
+		cause)
+}
+
+var _ error = ProxyProtocol_RuleValidationError{}
+
+var _ interface {
+	Field() string
+	Reason() string
+	Key() bool
+	Cause() error
+	ErrorName() string
+} = ProxyProtocol_RuleValidationError{}
diff --git a/vendor/github.com/pires/go-proxyproto/.gitignore b/vendor/github.com/pires/go-proxyproto/.gitignore
new file mode 100644
index 00000000..a2d2c301
--- /dev/null
+++ b/vendor/github.com/pires/go-proxyproto/.gitignore
@@ -0,0 +1,11 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+.idea
+bin
+pkg
+
+*.out
diff --git a/vendor/github.com/pires/go-proxyproto/LICENSE b/vendor/github.com/pires/go-proxyproto/LICENSE
new file mode 100644
index 00000000..a65c05a6
--- /dev/null
+++ b/vendor/github.com/pires/go-proxyproto/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2016 Paulo Pires
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/vendor/github.com/pires/go-proxyproto/README.md b/vendor/github.com/pires/go-proxyproto/README.md
new file mode 100644
index 00000000..982707cc
--- /dev/null
+++ b/vendor/github.com/pires/go-proxyproto/README.md
@@ -0,0 +1,162 @@
+# go-proxyproto
+
+[![Actions Status](https://github.com/pires/go-proxyproto/workflows/test/badge.svg)](https://github.com/pires/go-proxyproto/actions)
+[![Coverage Status](https://coveralls.io/repos/github/pires/go-proxyproto/badge.svg?branch=master)](https://coveralls.io/github/pires/go-proxyproto?branch=master)
+[![Go Report Card](https://goreportcard.com/badge/github.com/pires/go-proxyproto)](https://goreportcard.com/report/github.com/pires/go-proxyproto)
+[![](https://godoc.org/github.com/pires/go-proxyproto?status.svg)](https://pkg.go.dev/github.com/pires/go-proxyproto?tab=doc)
+
+
+A Go library implementation of the [PROXY protocol, versions 1 and 2](https://www.haproxy.org/download/2.3/doc/proxy-protocol.txt),
+which provides, as per specification:
+> (...) a convenient way to safely transport connection
+> information such as a client's address across multiple layers of NAT or TCP
+> proxies. It is designed to require little changes to existing components and
+> to limit the performance impact caused by the processing of the transported
+> information.
+
+This library is to be used in one of or both proxy clients and proxy servers that need to support said protocol.
+Both protocol versions, 1 (text-based) and 2 (binary-based) are supported.
+
+## Installation
+
+```shell
+$ go get -u github.com/pires/go-proxyproto
+```
+
+## Usage
+
+### Client
+
+```go
+package main
+
+import (
+	"io"
+	"log"
+	"net"
+
+	proxyproto "github.com/pires/go-proxyproto"
+)
+
+func chkErr(err error) {
+	if err != nil {
+		log.Fatalf("Error: %s", err.Error())
+	}
+}
+
+func main() {
+	// Dial some proxy listener e.g. https://github.com/mailgun/proxyproto
+	target, err := net.ResolveTCPAddr("tcp", "127.0.0.1:2319")
+	chkErr(err)
+
+	conn, err := net.DialTCP("tcp", nil, target)
+	chkErr(err)
+
+	defer conn.Close()
+
+	// Create a proxyprotocol header or use HeaderProxyFromAddrs() if you
+	// have two conn's
+	header := &proxyproto.Header{
+		Version:            1,
+		Command:            proxyproto.PROXY,
+		TransportProtocol:  proxyproto.TCPv4,
+		SourceAddr: &net.TCPAddr{
+			IP:   net.ParseIP("10.1.1.1"),
+			Port: 1000,
+		},
+		DestinationAddr: &net.TCPAddr{
+			IP:   net.ParseIP("20.2.2.2"),
+			Port: 2000,
+		},
+	}
+	// After the connection was created write the proxy headers first
+	_, err = header.WriteTo(conn)
+	chkErr(err)
+	// Then your data... e.g.:
+	_, err = io.WriteString(conn, "HELO")
+	chkErr(err)
+}
+```
+
+### Server
+
+```go
+package main
+
+import (
+	"log"
+	"net"
+
+	proxyproto "github.com/pires/go-proxyproto"
+)
+
+func main() {
+	// Create a listener
+	addr := "localhost:9876"
+	list, err := net.Listen("tcp", addr)
+	if err != nil {
+		log.Fatalf("couldn't listen to %q: %q\n", addr, err.Error())
+	}
+
+	// Wrap listener in a proxyproto listener
+	proxyListener := &proxyproto.Listener{Listener: list}
+	defer proxyListener.Close()
+
+	// Wait for a connection and accept it
+	conn, err := proxyListener.Accept()
+	defer conn.Close()
+
+	// Print connection details
+	if conn.LocalAddr() == nil {
+		log.Fatal("couldn't retrieve local address")
+	}
+	log.Printf("local address: %q", conn.LocalAddr().String())
+
+	if conn.RemoteAddr() == nil {
+		log.Fatal("couldn't retrieve remote address")
+	}
+	log.Printf("remote address: %q", conn.RemoteAddr().String())
+}
+```
+
+### HTTP Server
+```go
+package main
+
+import (
+	"net"
+	"net/http"
+	"time"
+
+	"github.com/pires/go-proxyproto"
+)
+
+func main() {
+	server := http.Server{
+		Addr: ":8080",
+	}
+
+	ln, err := net.Listen("tcp", server.Addr)
+	if err != nil {
+		panic(err)
+	}
+
+	proxyListener := &proxyproto.Listener{
+		Listener:          ln,
+		ReadHeaderTimeout: 10 * time.Second,
+	}
+	defer proxyListener.Close()
+
+	server.Serve(proxyListener)
+}
+```
+
+## Special notes
+
+### AWS
+
+AWS Network Load Balancer (NLB) does not push the PPV2 header until the client starts sending the data. This is a problem if your server speaks first. e.g. SMTP, FTP, SSH etc.
+
+By default, NLB target group attribute `proxy_protocol_v2.client_to_server.header_placement` has the value `on_first_ack_with_payload`. You need to contact AWS support to change it to `on_first_ack`, instead.
+
+Just to be clear, you need this fix only if your server is designed to speak first.
diff --git a/vendor/github.com/pires/go-proxyproto/addr_proto.go b/vendor/github.com/pires/go-proxyproto/addr_proto.go
new file mode 100644
index 00000000..d254fc41
--- /dev/null
+++ b/vendor/github.com/pires/go-proxyproto/addr_proto.go
@@ -0,0 +1,62 @@
+package proxyproto
+
+// AddressFamilyAndProtocol represents address family and transport protocol.
+type AddressFamilyAndProtocol byte
+
+const (
+	UNSPEC       AddressFamilyAndProtocol = '\x00'
+	TCPv4        AddressFamilyAndProtocol = '\x11'
+	UDPv4        AddressFamilyAndProtocol = '\x12'
+	TCPv6        AddressFamilyAndProtocol = '\x21'
+	UDPv6        AddressFamilyAndProtocol = '\x22'
+	UnixStream   AddressFamilyAndProtocol = '\x31'
+	UnixDatagram AddressFamilyAndProtocol = '\x32'
+)
+
+// IsIPv4 returns true if the address family is IPv4 (AF_INET4), false otherwise.
+func (ap AddressFamilyAndProtocol) IsIPv4() bool {
+	return ap&0xF0 == 0x10
+}
+
+// IsIPv6 returns true if the address family is IPv6 (AF_INET6), false otherwise.
+func (ap AddressFamilyAndProtocol) IsIPv6() bool {
+	return ap&0xF0 == 0x20
+}
+
+// IsUnix returns true if the address family is UNIX (AF_UNIX), false otherwise.
+func (ap AddressFamilyAndProtocol) IsUnix() bool {
+	return ap&0xF0 == 0x30
+}
+
+// IsStream returns true if the transport protocol is TCP or STREAM (SOCK_STREAM), false otherwise.
+func (ap AddressFamilyAndProtocol) IsStream() bool {
+	return ap&0x0F == 0x01
+}
+
+// IsDatagram returns true if the transport protocol is UDP or DGRAM (SOCK_DGRAM), false otherwise.
+func (ap AddressFamilyAndProtocol) IsDatagram() bool {
+	return ap&0x0F == 0x02
+}
+
+// IsUnspec returns true if the transport protocol or address family is unspecified, false otherwise.
+func (ap AddressFamilyAndProtocol) IsUnspec() bool {
+	return (ap&0xF0 == 0x00) || (ap&0x0F == 0x00)
+}
+
+func (ap AddressFamilyAndProtocol) toByte() byte {
+	if ap.IsIPv4() && ap.IsStream() {
+		return byte(TCPv4)
+	} else if ap.IsIPv4() && ap.IsDatagram() {
+		return byte(UDPv4)
+	} else if ap.IsIPv6() && ap.IsStream() {
+		return byte(TCPv6)
+	} else if ap.IsIPv6() && ap.IsDatagram() {
+		return byte(UDPv6)
+	} else if ap.IsUnix() && ap.IsStream() {
+		return byte(UnixStream)
+	} else if ap.IsUnix() && ap.IsDatagram() {
+		return byte(UnixDatagram)
+	}
+
+	return byte(UNSPEC)
+}
diff --git a/vendor/github.com/pires/go-proxyproto/go.mod b/vendor/github.com/pires/go-proxyproto/go.mod
new file mode 100644
index 00000000..0700cb11
--- /dev/null
+++ b/vendor/github.com/pires/go-proxyproto/go.mod
@@ -0,0 +1,3 @@
+module github.com/pires/go-proxyproto
+
+go 1.13
diff --git a/vendor/github.com/pires/go-proxyproto/header.go b/vendor/github.com/pires/go-proxyproto/header.go
new file mode 100644
index 00000000..81ebeb38
--- /dev/null
+++ b/vendor/github.com/pires/go-proxyproto/header.go
@@ -0,0 +1,280 @@
+// Package proxyproto implements Proxy Protocol (v1 and v2) parser and writer, as per specification:
+// https://www.haproxy.org/download/2.3/doc/proxy-protocol.txt
+package proxyproto
+
+import (
+	"bufio"
+	"bytes"
+	"errors"
+	"io"
+	"net"
+	"time"
+)
+
+var (
+	// Protocol
+	SIGV1 = []byte{'\x50', '\x52', '\x4F', '\x58', '\x59'}
+	SIGV2 = []byte{'\x0D', '\x0A', '\x0D', '\x0A', '\x00', '\x0D', '\x0A', '\x51', '\x55', '\x49', '\x54', '\x0A'}
+
+	ErrCantReadVersion1Header               = errors.New("proxyproto: can't read version 1 header")
+	ErrVersion1HeaderTooLong                = errors.New("proxyproto: version 1 header must be 107 bytes or less")
+	ErrLineMustEndWithCrlf                  = errors.New("proxyproto: version 1 header is invalid, must end with \\r\\n")
+	ErrCantReadProtocolVersionAndCommand    = errors.New("proxyproto: can't read proxy protocol version and command")
+	ErrCantReadAddressFamilyAndProtocol     = errors.New("proxyproto: can't read address family or protocol")
+	ErrCantReadLength                       = errors.New("proxyproto: can't read length")
+	ErrCantResolveSourceUnixAddress         = errors.New("proxyproto: can't resolve source Unix address")
+	ErrCantResolveDestinationUnixAddress    = errors.New("proxyproto: can't resolve destination Unix address")
+	ErrNoProxyProtocol                      = errors.New("proxyproto: proxy protocol signature not present")
+	ErrUnknownProxyProtocolVersion          = errors.New("proxyproto: unknown proxy protocol version")
+	ErrUnsupportedProtocolVersionAndCommand = errors.New("proxyproto: unsupported proxy protocol version and command")
+	ErrUnsupportedAddressFamilyAndProtocol  = errors.New("proxyproto: unsupported address family and protocol")
+	ErrInvalidLength                        = errors.New("proxyproto: invalid length")
+	ErrInvalidAddress                       = errors.New("proxyproto: invalid address")
+	ErrInvalidPortNumber                    = errors.New("proxyproto: invalid port number")
+	ErrSuperfluousProxyHeader               = errors.New("proxyproto: upstream connection sent PROXY header but isn't allowed to send one")
+)
+
+// Header is the placeholder for proxy protocol header.
+type Header struct {
+	Version           byte
+	Command           ProtocolVersionAndCommand
+	TransportProtocol AddressFamilyAndProtocol
+	SourceAddr        net.Addr
+	DestinationAddr   net.Addr
+	rawTLVs           []byte
+}
+
+// HeaderProxyFromAddrs creates a new PROXY header from a source and a
+// destination address. If version is zero, the latest protocol version is
+// used.
+//
+// The header is filled on a best-effort basis: if hints cannot be inferred
+// from the provided addresses, the header will be left unspecified.
+func HeaderProxyFromAddrs(version byte, sourceAddr, destAddr net.Addr) *Header {
+	if version < 1 || version > 2 {
+		version = 2
+	}
+	h := &Header{
+		Version:           version,
+		Command:           LOCAL,
+		TransportProtocol: UNSPEC,
+	}
+	switch sourceAddr := sourceAddr.(type) {
+	case *net.TCPAddr:
+		if _, ok := destAddr.(*net.TCPAddr); !ok {
+			break
+		}
+		if len(sourceAddr.IP.To4()) == net.IPv4len {
+			h.TransportProtocol = TCPv4
+		} else if len(sourceAddr.IP) == net.IPv6len {
+			h.TransportProtocol = TCPv6
+		}
+	case *net.UDPAddr:
+		if _, ok := destAddr.(*net.UDPAddr); !ok {
+			break
+		}
+		if len(sourceAddr.IP.To4()) == net.IPv4len {
+			h.TransportProtocol = UDPv4
+		} else if len(sourceAddr.IP) == net.IPv6len {
+			h.TransportProtocol = UDPv6
+		}
+	case *net.UnixAddr:
+		if _, ok := destAddr.(*net.UnixAddr); !ok {
+			break
+		}
+		switch sourceAddr.Net {
+		case "unix":
+			h.TransportProtocol = UnixStream
+		case "unixgram":
+			h.TransportProtocol = UnixDatagram
+		}
+	}
+	if h.TransportProtocol != UNSPEC {
+		h.Command = PROXY
+		h.SourceAddr = sourceAddr
+		h.DestinationAddr = destAddr
+	}
+	return h
+}
+
+func (header *Header) TCPAddrs() (sourceAddr, destAddr *net.TCPAddr, ok bool) {
+	if !header.TransportProtocol.IsStream() {
+		return nil, nil, false
+	}
+	sourceAddr, sourceOK := header.SourceAddr.(*net.TCPAddr)
+	destAddr, destOK := header.DestinationAddr.(*net.TCPAddr)
+	return sourceAddr, destAddr, sourceOK && destOK
+}
+
+func (header *Header) UDPAddrs() (sourceAddr, destAddr *net.UDPAddr, ok bool) {
+	if !header.TransportProtocol.IsDatagram() {
+		return nil, nil, false
+	}
+	sourceAddr, sourceOK := header.SourceAddr.(*net.UDPAddr)
+	destAddr, destOK := header.DestinationAddr.(*net.UDPAddr)
+	return sourceAddr, destAddr, sourceOK && destOK
+}
+
+func (header *Header) UnixAddrs() (sourceAddr, destAddr *net.UnixAddr, ok bool) {
+	if !header.TransportProtocol.IsUnix() {
+		return nil, nil, false
+	}
+	sourceAddr, sourceOK := header.SourceAddr.(*net.UnixAddr)
+	destAddr, destOK := header.DestinationAddr.(*net.UnixAddr)
+	return sourceAddr, destAddr, sourceOK && destOK
+}
+
+func (header *Header) IPs() (sourceIP, destIP net.IP, ok bool) {
+	if sourceAddr, destAddr, ok := header.TCPAddrs(); ok {
+		return sourceAddr.IP, destAddr.IP, true
+	} else if sourceAddr, destAddr, ok := header.UDPAddrs(); ok {
+		return sourceAddr.IP, destAddr.IP, true
+	} else {
+		return nil, nil, false
+	}
+}
+
+func (header *Header) Ports() (sourcePort, destPort int, ok bool) {
+	if sourceAddr, destAddr, ok := header.TCPAddrs(); ok {
+		return sourceAddr.Port, destAddr.Port, true
+	} else if sourceAddr, destAddr, ok := header.UDPAddrs(); ok {
+		return sourceAddr.Port, destAddr.Port, true
+	} else {
+		return 0, 0, false
+	}
+}
+
+// EqualTo returns true if headers are equivalent, false otherwise.
+// Deprecated: use EqualsTo instead. This method will eventually be removed.
+func (header *Header) EqualTo(otherHeader *Header) bool {
+	return header.EqualsTo(otherHeader)
+}
+
+// EqualsTo returns true if headers are equivalent, false otherwise.
+func (header *Header) EqualsTo(otherHeader *Header) bool {
+	if otherHeader == nil {
+		return false
+	}
+	// TLVs only exist for version 2
+	if header.Version == 2 && !bytes.Equal(header.rawTLVs, otherHeader.rawTLVs) {
+		return false
+	}
+	if header.Version != otherHeader.Version || header.Command != otherHeader.Command || header.TransportProtocol != otherHeader.TransportProtocol {
+		return false
+	}
+	// Return early for header with LOCAL command, which contains no address information
+	if header.Command == LOCAL {
+		return true
+	}
+	return header.SourceAddr.String() == otherHeader.SourceAddr.String() &&
+		header.DestinationAddr.String() == otherHeader.DestinationAddr.String()
+}
+
+// WriteTo renders a proxy protocol header in a format and writes it to an io.Writer.
+func (header *Header) WriteTo(w io.Writer) (int64, error) {
+	buf, err := header.Format()
+	if err != nil {
+		return 0, err
+	}
+
+	return bytes.NewBuffer(buf).WriteTo(w)
+}
+
+// Format renders a proxy protocol header in a format to write over the wire.
+func (header *Header) Format() ([]byte, error) {
+	switch header.Version {
+	case 1:
+		return header.formatVersion1()
+	case 2:
+		return header.formatVersion2()
+	default:
+		return nil, ErrUnknownProxyProtocolVersion
+	}
+}
+
+// TLVs returns the TLVs stored into this header, if they exist.  TLVs are optional for v2 of the protocol.
+func (header *Header) TLVs() ([]TLV, error) {
+	return SplitTLVs(header.rawTLVs)
+}
+
+// SetTLVs sets the TLVs stored in this header. This method replaces any
+// previous TLV.
+func (header *Header) SetTLVs(tlvs []TLV) error {
+	raw, err := JoinTLVs(tlvs)
+	if err != nil {
+		return err
+	}
+	header.rawTLVs = raw
+	return nil
+}
+
+// Read identifies the proxy protocol version and reads the remaining of
+// the header, accordingly.
+//
+// If proxy protocol header signature is not present, the reader buffer remains untouched
+// and is safe for reading outside of this code.
+//
+// If proxy protocol header signature is present but an error is raised while processing
+// the remaining header, assume the reader buffer to be in a corrupt state.
+// Also, this operation will block until enough bytes are available for peeking.
+func Read(reader *bufio.Reader) (*Header, error) {
+	// In order to improve speed for small non-PROXYed packets, take a peek at the first byte alone.
+	b1, err := reader.Peek(1)
+	if err != nil {
+		if err == io.EOF {
+			return nil, ErrNoProxyProtocol
+		}
+		return nil, err
+	}
+
+	if bytes.Equal(b1[:1], SIGV1[:1]) || bytes.Equal(b1[:1], SIGV2[:1]) {
+		signature, err := reader.Peek(5)
+		if err != nil {
+			if err == io.EOF {
+				return nil, ErrNoProxyProtocol
+			}
+			return nil, err
+		}
+		if bytes.Equal(signature[:5], SIGV1) {
+			return parseVersion1(reader)
+		}
+
+		signature, err = reader.Peek(12)
+		if err != nil {
+			if err == io.EOF {
+				return nil, ErrNoProxyProtocol
+			}
+			return nil, err
+		}
+		if bytes.Equal(signature[:12], SIGV2) {
+			return parseVersion2(reader)
+		}
+	}
+
+	return nil, ErrNoProxyProtocol
+}
+
+// ReadTimeout acts as Read but takes a timeout. If that timeout is reached, it's assumed
+// there's no proxy protocol header.
+func ReadTimeout(reader *bufio.Reader, timeout time.Duration) (*Header, error) {
+	type header struct {
+		h *Header
+		e error
+	}
+	read := make(chan *header, 1)
+
+	go func() {
+		h := &header{}
+		h.h, h.e = Read(reader)
+		read <- h
+	}()
+
+	timer := time.NewTimer(timeout)
+	select {
+	case result := <-read:
+		timer.Stop()
+		return result.h, result.e
+	case <-timer.C:
+		return nil, ErrNoProxyProtocol
+	}
+}
diff --git a/vendor/github.com/pires/go-proxyproto/policy.go b/vendor/github.com/pires/go-proxyproto/policy.go
new file mode 100644
index 00000000..71ad62ba
--- /dev/null
+++ b/vendor/github.com/pires/go-proxyproto/policy.go
@@ -0,0 +1,149 @@
+package proxyproto
+
+import (
+	"fmt"
+	"net"
+	"strings"
+)
+
+// PolicyFunc can be used to decide whether to trust the PROXY info from
+// upstream. If set, the connecting address is passed in as an argument.
+//
+// See below for the different policies.
+//
+// In case an error is returned the connection is denied.
+type PolicyFunc func(upstream net.Addr) (Policy, error)
+
+// Policy defines how a connection with a PROXY header address is treated.
+type Policy int
+
+const (
+	// USE address from PROXY header
+	USE Policy = iota
+	// IGNORE address from PROXY header, but accept connection
+	IGNORE
+	// REJECT connection when PROXY header is sent
+	// Note: even though the first read on the connection returns an error if
+	// a PROXY header is present, subsequent reads do not. It is the task of
+	// the code using the connection to handle that case properly.
+	REJECT
+	// REQUIRE connection to send PROXY header, reject if not present
+	// Note: even though the first read on the connection returns an error if
+	// a PROXY header is not present, subsequent reads do not. It is the task
+	// of the code using the connection to handle that case properly.
+	REQUIRE
+)
+
+// WithPolicy adds given policy to a connection when passed as option to NewConn()
+func WithPolicy(p Policy) func(*Conn) {
+	return func(c *Conn) {
+		c.ProxyHeaderPolicy = p
+	}
+}
+
+// LaxWhiteListPolicy returns a PolicyFunc which decides whether the
+// upstream ip is allowed to send a proxy header based on a list of allowed
+// IP addresses and IP ranges. In case upstream IP is not in list the proxy
+// header will be ignored. If one of the provided IP addresses or IP ranges
+// is invalid it will return an error instead of a PolicyFunc.
+func LaxWhiteListPolicy(allowed []string) (PolicyFunc, error) {
+	allowFrom, err := parse(allowed)
+	if err != nil {
+		return nil, err
+	}
+
+	return whitelistPolicy(allowFrom, IGNORE), nil
+}
+
+// MustLaxWhiteListPolicy returns a LaxWhiteListPolicy but will panic if one
+// of the provided IP addresses or IP ranges is invalid.
+func MustLaxWhiteListPolicy(allowed []string) PolicyFunc {
+	pfunc, err := LaxWhiteListPolicy(allowed)
+	if err != nil {
+		panic(err)
+	}
+
+	return pfunc
+}
+
+// StrictWhiteListPolicy returns a PolicyFunc which decides whether the
+// upstream ip is allowed to send a proxy header based on a list of allowed
+// IP addresses and IP ranges. In case upstream IP is not in list reading on
+// the connection will be refused on the first read. Please note: subsequent
+// reads do not error. It is the task of the code using the connection to
+// handle that case properly. If one of the provided IP addresses or IP
+// ranges is invalid it will return an error instead of a PolicyFunc.
+func StrictWhiteListPolicy(allowed []string) (PolicyFunc, error) {
+	allowFrom, err := parse(allowed)
+	if err != nil {
+		return nil, err
+	}
+
+	return whitelistPolicy(allowFrom, REJECT), nil
+}
+
+// MustStrictWhiteListPolicy returns a StrictWhiteListPolicy but will panic
+// if one of the provided IP addresses or IP ranges is invalid.
+func MustStrictWhiteListPolicy(allowed []string) PolicyFunc {
+	pfunc, err := StrictWhiteListPolicy(allowed)
+	if err != nil {
+		panic(err)
+	}
+
+	return pfunc
+}
+
+func whitelistPolicy(allowed []func(net.IP) bool, def Policy) PolicyFunc {
+	return func(upstream net.Addr) (Policy, error) {
+		upstreamIP, err := ipFromAddr(upstream)
+		if err != nil {
+			// something is wrong with the source IP, better reject the connection
+			return REJECT, err
+		}
+
+		for _, allowFrom := range allowed {
+			if allowFrom(upstreamIP) {
+				return USE, nil
+			}
+		}
+
+		return def, nil
+	}
+}
+
+func parse(allowed []string) ([]func(net.IP) bool, error) {
+	a := make([]func(net.IP) bool, len(allowed))
+	for i, allowFrom := range allowed {
+		if strings.LastIndex(allowFrom, "/") > 0 {
+			_, ipRange, err := net.ParseCIDR(allowFrom)
+			if err != nil {
+				return nil, fmt.Errorf("proxyproto: given string %q is not a valid IP range: %v", allowFrom, err)
+			}
+
+			a[i] = ipRange.Contains
+		} else {
+			allowed := net.ParseIP(allowFrom)
+			if allowed == nil {
+				return nil, fmt.Errorf("proxyproto: given string %q is not a valid IP address", allowFrom)
+			}
+
+			a[i] = allowed.Equal
+		}
+	}
+
+	return a, nil
+}
+
+func ipFromAddr(upstream net.Addr) (net.IP, error) {
+	upstreamString, _, err := net.SplitHostPort(upstream.String())
+	if err != nil {
+		return nil, err
+	}
+
+	upstreamIP := net.ParseIP(upstreamString)
+	if nil == upstreamIP {
+		return nil, fmt.Errorf("proxyproto: invalid IP address")
+	}
+
+	return upstreamIP, nil
+}
diff --git a/vendor/github.com/pires/go-proxyproto/protocol.go b/vendor/github.com/pires/go-proxyproto/protocol.go
new file mode 100644
index 00000000..bfc24e93
--- /dev/null
+++ b/vendor/github.com/pires/go-proxyproto/protocol.go
@@ -0,0 +1,306 @@
+package proxyproto
+
+import (
+	"bufio"
+	"io"
+	"net"
+	"sync"
+	"time"
+)
+
+// DefaultReadHeaderTimeout is how long header processing waits for header to
+// be read from the wire, if Listener.ReaderHeaderTimeout is not set.
+// It's kept as a global variable so to make it easier to find and override,
+// e.g. go build -ldflags -X "github.com/pires/go-proxyproto.DefaultReadHeaderTimeout=1s"
+var DefaultReadHeaderTimeout = 200 * time.Millisecond
+
+// Listener is used to wrap an underlying listener,
+// whose connections may be using the HAProxy Proxy Protocol.
+// If the connection is using the protocol, the RemoteAddr() will return
+// the correct client address. ReadHeaderTimeout will be applied to all
+// connections in order to prevent blocking operations. If no ReadHeaderTimeout
+// is set, a default of 200ms will be used. This can be disabled by setting the
+// timeout to < 0.
+type Listener struct {
+	Listener          net.Listener
+	Policy            PolicyFunc
+	ValidateHeader    Validator
+	ReadHeaderTimeout time.Duration
+}
+
+// Conn is used to wrap and underlying connection which
+// may be speaking the Proxy Protocol. If it is, the RemoteAddr() will
+// return the address of the client instead of the proxy address. Each connection
+// will have its own readHeaderTimeout and readDeadline set by the Accept() call.
+type Conn struct {
+	bufReader         *bufio.Reader
+	conn              net.Conn
+	header            *Header
+	once              sync.Once
+	ProxyHeaderPolicy Policy
+	Validate          Validator
+	readErr           error
+	readHeaderTimeout time.Duration
+	readDeadline      time.Time
+}
+
+// Validator receives a header and decides whether it is a valid one
+// In case the header is not deemed valid it should return an error.
+type Validator func(*Header) error
+
+// ValidateHeader adds given validator for proxy headers to a connection when passed as option to NewConn()
+func ValidateHeader(v Validator) func(*Conn) {
+	return func(c *Conn) {
+		if v != nil {
+			c.Validate = v
+		}
+	}
+}
+
+// Accept waits for and returns the next connection to the listener.
+func (p *Listener) Accept() (net.Conn, error) {
+	// Get the underlying connection
+	conn, err := p.Listener.Accept()
+	if err != nil {
+		return nil, err
+	}
+
+	proxyHeaderPolicy := USE
+	if p.Policy != nil {
+		proxyHeaderPolicy, err = p.Policy(conn.RemoteAddr())
+		if err != nil {
+			// can't decide the policy, we can't accept the connection
+			conn.Close()
+			return nil, err
+		}
+	}
+
+	newConn := NewConn(
+		conn,
+		WithPolicy(proxyHeaderPolicy),
+		ValidateHeader(p.ValidateHeader),
+	)
+
+	// If the ReadHeaderTimeout for the listener is unset, use the default timeout.
+	if p.ReadHeaderTimeout == 0 {
+		p.ReadHeaderTimeout = DefaultReadHeaderTimeout
+	}
+
+	// Set the readHeaderTimeout of the new conn to the value of the listener
+	newConn.readHeaderTimeout = p.ReadHeaderTimeout
+
+	return newConn, nil
+}
+
+// Close closes the underlying listener.
+func (p *Listener) Close() error {
+	return p.Listener.Close()
+}
+
+// Addr returns the underlying listener's network address.
+func (p *Listener) Addr() net.Addr {
+	return p.Listener.Addr()
+}
+
+// NewConn is used to wrap a net.Conn that may be speaking
+// the proxy protocol into a proxyproto.Conn
+func NewConn(conn net.Conn, opts ...func(*Conn)) *Conn {
+	pConn := &Conn{
+		bufReader: bufio.NewReader(conn),
+		conn:      conn,
+	}
+
+	for _, opt := range opts {
+		opt(pConn)
+	}
+
+	return pConn
+}
+
+// Read is check for the proxy protocol header when doing
+// the initial scan. If there is an error parsing the header,
+// it is returned and the socket is closed.
+func (p *Conn) Read(b []byte) (int, error) {
+	p.once.Do(func() {
+		p.readErr = p.readHeader()
+	})
+	if p.readErr != nil {
+		return 0, p.readErr
+	}
+
+	return p.bufReader.Read(b)
+}
+
+// Write wraps original conn.Write
+func (p *Conn) Write(b []byte) (int, error) {
+	return p.conn.Write(b)
+}
+
+// Close wraps original conn.Close
+func (p *Conn) Close() error {
+	return p.conn.Close()
+}
+
+// ProxyHeader returns the proxy protocol header, if any. If an error occurs
+// while reading the proxy header, nil is returned.
+func (p *Conn) ProxyHeader() *Header {
+	p.once.Do(func() { p.readErr = p.readHeader() })
+	return p.header
+}
+
+// LocalAddr returns the address of the server if the proxy
+// protocol is being used, otherwise just returns the address of
+// the socket server. In case an error happens on reading the
+// proxy header the original LocalAddr is returned, not the one
+// from the proxy header even if the proxy header itself is
+// syntactically correct.
+func (p *Conn) LocalAddr() net.Addr {
+	p.once.Do(func() { p.readErr = p.readHeader() })
+	if p.header == nil || p.header.Command.IsLocal() || p.readErr != nil {
+		return p.conn.LocalAddr()
+	}
+
+	return p.header.DestinationAddr
+}
+
+// RemoteAddr returns the address of the client if the proxy
+// protocol is being used, otherwise just returns the address of
+// the socket peer. In case an error happens on reading the
+// proxy header the original RemoteAddr is returned, not the one
+// from the proxy header even if the proxy header itself is
+// syntactically correct.
+func (p *Conn) RemoteAddr() net.Addr {
+	p.once.Do(func() { p.readErr = p.readHeader() })
+	if p.header == nil || p.header.Command.IsLocal() || p.readErr != nil {
+		return p.conn.RemoteAddr()
+	}
+
+	return p.header.SourceAddr
+}
+
+// Raw returns the underlying connection which can be casted to
+// a concrete type, allowing access to specialized functions.
+//
+// Use this ONLY if you know exactly what you are doing.
+func (p *Conn) Raw() net.Conn {
+	return p.conn
+}
+
+// TCPConn returns the underlying TCP connection,
+// allowing access to specialized functions.
+//
+// Use this ONLY if you know exactly what you are doing.
+func (p *Conn) TCPConn() (conn *net.TCPConn, ok bool) {
+	conn, ok = p.conn.(*net.TCPConn)
+	return
+}
+
+// UnixConn returns the underlying Unix socket connection,
+// allowing access to specialized functions.
+//
+// Use this ONLY if you know exactly what you are doing.
+func (p *Conn) UnixConn() (conn *net.UnixConn, ok bool) {
+	conn, ok = p.conn.(*net.UnixConn)
+	return
+}
+
+// UDPConn returns the underlying UDP connection,
+// allowing access to specialized functions.
+//
+// Use this ONLY if you know exactly what you are doing.
+func (p *Conn) UDPConn() (conn *net.UDPConn, ok bool) {
+	conn, ok = p.conn.(*net.UDPConn)
+	return
+}
+
+// SetDeadline wraps original conn.SetDeadline
+func (p *Conn) SetDeadline(t time.Time) error {
+	p.readDeadline = t
+	return p.conn.SetDeadline(t)
+}
+
+// SetReadDeadline wraps original conn.SetReadDeadline
+func (p *Conn) SetReadDeadline(t time.Time) error {
+	// Set a local var that tells us the desired deadline. This is
+	// needed in order to reset the read deadline to the one that is
+	// desired by the user, rather than an empty deadline.
+	p.readDeadline = t
+	return p.conn.SetReadDeadline(t)
+}
+
+// SetWriteDeadline wraps original conn.SetWriteDeadline
+func (p *Conn) SetWriteDeadline(t time.Time) error {
+	return p.conn.SetWriteDeadline(t)
+}
+
+func (p *Conn) readHeader() error {
+	// If the connection's readHeaderTimeout is more than 0,
+	// push our deadline back to now plus the timeout. This should only
+	// run on the connection, as we don't want to override the previous
+	// read deadline the user may have used.
+	if p.readHeaderTimeout > 0 {
+		p.conn.SetReadDeadline(time.Now().Add(p.readHeaderTimeout))
+	}
+
+	header, err := Read(p.bufReader)
+
+	// If the connection's readHeaderTimeout is more than 0, undo the change to the
+	// deadline that we made above. Because we retain the readDeadline as part of our
+	// SetReadDeadline override, we know the user's desired deadline so we use that.
+	// Therefore, we check whether the error is a net.Timeout and if it is, we decide
+	// the proxy proto does not exist and set the error accordingly.
+	if p.readHeaderTimeout > 0 {
+		p.conn.SetReadDeadline(p.readDeadline)
+		if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
+			err = ErrNoProxyProtocol
+		}
+	}
+
+	// For the purpose of this wrapper shamefully stolen from armon/go-proxyproto
+	// let's act as if there was no error when PROXY protocol is not present.
+	if err == ErrNoProxyProtocol {
+		// but not if it is required that the connection has one
+		if p.ProxyHeaderPolicy == REQUIRE {
+			return err
+		}
+
+		return nil
+	}
+
+	// proxy protocol header was found
+	if err == nil && header != nil {
+		switch p.ProxyHeaderPolicy {
+		case REJECT:
+			// this connection is not allowed to send one
+			return ErrSuperfluousProxyHeader
+		case USE, REQUIRE:
+			if p.Validate != nil {
+				err = p.Validate(header)
+				if err != nil {
+					return err
+				}
+			}
+
+			p.header = header
+		}
+	}
+
+	return err
+}
+
+// ReadFrom implements the io.ReaderFrom ReadFrom method
+func (p *Conn) ReadFrom(r io.Reader) (int64, error) {
+	if rf, ok := p.conn.(io.ReaderFrom); ok {
+		return rf.ReadFrom(r)
+	}
+	return io.Copy(p.conn, r)
+}
+
+// WriteTo implements io.WriterTo
+func (p *Conn) WriteTo(w io.Writer) (int64, error) {
+	p.once.Do(func() { p.readErr = p.readHeader() })
+	if p.readErr != nil {
+		return 0, p.readErr
+	}
+	return p.bufReader.WriteTo(w)
+}
diff --git a/vendor/github.com/pires/go-proxyproto/tlv.go b/vendor/github.com/pires/go-proxyproto/tlv.go
new file mode 100644
index 00000000..7cc2fb37
--- /dev/null
+++ b/vendor/github.com/pires/go-proxyproto/tlv.go
@@ -0,0 +1,132 @@
+// Type-Length-Value splitting and parsing for proxy protocol V2
+// See spec https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt sections 2.2 to 2.7 and
+
+package proxyproto
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"math"
+)
+
+const (
+	// Section 2.2
+	PP2_TYPE_ALPN           PP2Type = 0x01
+	PP2_TYPE_AUTHORITY      PP2Type = 0x02
+	PP2_TYPE_CRC32C         PP2Type = 0x03
+	PP2_TYPE_NOOP           PP2Type = 0x04
+	PP2_TYPE_UNIQUE_ID      PP2Type = 0x05
+	PP2_TYPE_SSL            PP2Type = 0x20
+	PP2_SUBTYPE_SSL_VERSION PP2Type = 0x21
+	PP2_SUBTYPE_SSL_CN      PP2Type = 0x22
+	PP2_SUBTYPE_SSL_CIPHER  PP2Type = 0x23
+	PP2_SUBTYPE_SSL_SIG_ALG PP2Type = 0x24
+	PP2_SUBTYPE_SSL_KEY_ALG PP2Type = 0x25
+	PP2_TYPE_NETNS          PP2Type = 0x30
+
+	// Section 2.2.7, reserved types
+	PP2_TYPE_MIN_CUSTOM     PP2Type = 0xE0
+	PP2_TYPE_MAX_CUSTOM     PP2Type = 0xEF
+	PP2_TYPE_MIN_EXPERIMENT PP2Type = 0xF0
+	PP2_TYPE_MAX_EXPERIMENT PP2Type = 0xF7
+	PP2_TYPE_MIN_FUTURE     PP2Type = 0xF8
+	PP2_TYPE_MAX_FUTURE     PP2Type = 0xFF
+)
+
+var (
+	ErrTruncatedTLV    = errors.New("proxyproto: truncated TLV")
+	ErrMalformedTLV    = errors.New("proxyproto: malformed TLV Value")
+	ErrIncompatibleTLV = errors.New("proxyproto: incompatible TLV type")
+)
+
+// PP2Type is the proxy protocol v2 type
+type PP2Type byte
+
+// TLV is a uninterpreted Type-Length-Value for V2 protocol, see section 2.2
+type TLV struct {
+	Type  PP2Type
+	Value []byte
+}
+
+// SplitTLVs splits the Type-Length-Value vector, returns the vector or an error.
+func SplitTLVs(raw []byte) ([]TLV, error) {
+	var tlvs []TLV
+	for i := 0; i < len(raw); {
+		tlv := TLV{
+			Type: PP2Type(raw[i]),
+		}
+		if len(raw)-i <= 2 {
+			return nil, ErrTruncatedTLV
+		}
+		tlvLen := int(binary.BigEndian.Uint16(raw[i+1 : i+3])) // Max length = 65K
+		i += 3
+		if i+tlvLen > len(raw) {
+			return nil, ErrTruncatedTLV
+		}
+		// Ignore no-op padding
+		if tlv.Type != PP2_TYPE_NOOP {
+			tlv.Value = make([]byte, tlvLen)
+			copy(tlv.Value, raw[i:i+tlvLen])
+		}
+		i += tlvLen
+		tlvs = append(tlvs, tlv)
+	}
+	return tlvs, nil
+}
+
+// JoinTLVs joins multiple Type-Length-Value records.
+func JoinTLVs(tlvs []TLV) ([]byte, error) {
+	var raw []byte
+	for _, tlv := range tlvs {
+		if len(tlv.Value) > math.MaxUint16 {
+			return nil, fmt.Errorf("proxyproto: cannot format TLV %v with length %d", tlv.Type, len(tlv.Value))
+		}
+		var length [2]byte
+		binary.BigEndian.PutUint16(length[:], uint16(len(tlv.Value)))
+		raw = append(raw, byte(tlv.Type))
+		raw = append(raw, length[:]...)
+		raw = append(raw, tlv.Value...)
+	}
+	return raw, nil
+}
+
+// Registered is true if the type is registered in the spec, see section 2.2
+func (p PP2Type) Registered() bool {
+	switch p {
+	case PP2_TYPE_ALPN,
+		PP2_TYPE_AUTHORITY,
+		PP2_TYPE_CRC32C,
+		PP2_TYPE_NOOP,
+		PP2_TYPE_UNIQUE_ID,
+		PP2_TYPE_SSL,
+		PP2_SUBTYPE_SSL_VERSION,
+		PP2_SUBTYPE_SSL_CN,
+		PP2_SUBTYPE_SSL_CIPHER,
+		PP2_SUBTYPE_SSL_SIG_ALG,
+		PP2_SUBTYPE_SSL_KEY_ALG,
+		PP2_TYPE_NETNS:
+		return true
+	}
+	return false
+}
+
+// App is true if the type is reserved for application specific data, see section 2.2.7
+func (p PP2Type) App() bool {
+	return p >= PP2_TYPE_MIN_CUSTOM && p <= PP2_TYPE_MAX_CUSTOM
+}
+
+// Experiment is true if the type is reserved for temporary experimental use by application developers, see section 2.2.7
+func (p PP2Type) Experiment() bool {
+	return p >= PP2_TYPE_MIN_EXPERIMENT && p <= PP2_TYPE_MAX_EXPERIMENT
+}
+
+// Future is true is the type is reserved for future use, see section 2.2.7
+func (p PP2Type) Future() bool {
+	return p >= PP2_TYPE_MIN_FUTURE
+}
+
+// Spec is true if the type is covered by the spec, see section 2.2 and 2.2.7
+func (p PP2Type) Spec() bool {
+	return p.Registered() || p.App() || p.Experiment() || p.Future()
+}
diff --git a/vendor/github.com/pires/go-proxyproto/v1.go b/vendor/github.com/pires/go-proxyproto/v1.go
new file mode 100644
index 00000000..23de95ec
--- /dev/null
+++ b/vendor/github.com/pires/go-proxyproto/v1.go
@@ -0,0 +1,231 @@
+package proxyproto
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"net"
+	"strconv"
+	"strings"
+)
+
+const (
+	crlf      = "\r\n"
+	separator = " "
+)
+
+func initVersion1() *Header {
+	header := new(Header)
+	header.Version = 1
+	// Command doesn't exist in v1
+	header.Command = PROXY
+	return header
+}
+
+func parseVersion1(reader *bufio.Reader) (*Header, error) {
+	//The header cannot be more than 107 bytes long. Per spec:
+	//
+	//   (...)
+	//   - worst case (optional fields set to 0xff) :
+	//     "PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n"
+	//     => 5 + 1 + 7 + 1 + 39 + 1 + 39 + 1 + 5 + 1 + 5 + 2 = 107 chars
+	//
+	//   So a 108-byte buffer is always enough to store all the line and a
+	//   trailing zero for string processing.
+	//
+	// It must also be CRLF terminated, as above. The header does not otherwise
+	// contain a CR or LF byte.
+	//
+	// ISSUE #69
+	// We can't use Peek here as it will block trying to fill the buffer, which
+	// will never happen if the header is TCP4 or TCP6 (max. 56 and 104 bytes
+	// respectively) and the server is expected to speak first.
+	//
+	// Similarly, we can't use ReadString or ReadBytes as these will keep reading
+	// until the delimiter is found; an abusive client could easily disrupt a
+	// server by sending a large amount of data that do not contain a LF byte.
+	// Another means of attack would be to start connections and simply not send
+	// data after the initial PROXY signature bytes, accumulating a large
+	// number of blocked goroutines on the server. ReadSlice will also block for
+	// a delimiter when the internal buffer does not fill up.
+	//
+	// A plain Read is also problematic since we risk reading past the end of the
+	// header without being able to easily put the excess bytes back into the reader's
+	// buffer (with the current implementation's design).
+	//
+	// So we use a ReadByte loop, which solves the overflow problem and avoids
+	// reading beyond the end of the header. However, we need one more trick to harden
+	// against partial header attacks (slow loris) - per spec:
+	//
+	//    (..) The sender must always ensure that the header is sent at once, so that
+	//    the transport layer maintains atomicity along the path to the receiver. The
+	//    receiver may be tolerant to partial headers or may simply drop the connection
+	//    when receiving a partial header. Recommendation is to be tolerant, but
+	//    implementation constraints may not always easily permit this.
+	//
+	// We are subject to such implementation constraints. So we return an error if
+	// the header cannot be fully extracted with a single read of the underlying
+	// reader.
+	buf := make([]byte, 0, 107)
+	for {
+		b, err := reader.ReadByte()
+		if err != nil {
+			return nil, fmt.Errorf(ErrCantReadVersion1Header.Error()+": %v", err)
+		}
+		buf = append(buf, b)
+		if b == '\n' {
+			// End of header found
+			break
+		}
+		if len(buf) == 107 {
+			// No delimiter in first 107 bytes
+			return nil, ErrVersion1HeaderTooLong
+		}
+		if reader.Buffered() == 0 {
+			// Header was not buffered in a single read. Since we can't
+			// differentiate between genuine slow writers and DoS agents,
+			// we abort. On healthy networks, this should never happen.
+			return nil, ErrCantReadVersion1Header
+		}
+	}
+
+	// Check for CR before LF.
+	if len(buf) < 2 || buf[len(buf)-2] != '\r' {
+		return nil, ErrLineMustEndWithCrlf
+	}
+
+	// Check full signature.
+	tokens := strings.Split(string(buf[:len(buf)-2]), separator)
+
+	// Expect at least 2 tokens: "PROXY" and the transport protocol.
+	if len(tokens) < 2 {
+		return nil, ErrCantReadAddressFamilyAndProtocol
+	}
+
+	// Read address family and protocol
+	var transportProtocol AddressFamilyAndProtocol
+	switch tokens[1] {
+	case "TCP4":
+		transportProtocol = TCPv4
+	case "TCP6":
+		transportProtocol = TCPv6
+	case "UNKNOWN":
+		transportProtocol = UNSPEC // doesn't exist in v1 but fits UNKNOWN
+	default:
+		return nil, ErrCantReadAddressFamilyAndProtocol
+	}
+
+	// Expect 6 tokens only when UNKNOWN is not present.
+	if transportProtocol != UNSPEC && len(tokens) < 6 {
+		return nil, ErrCantReadAddressFamilyAndProtocol
+	}
+
+	// When a signature is found, allocate a v1 header with Command set to PROXY.
+	// Command doesn't exist in v1 but set it for other parts of this library
+	// to rely on it for determining connection details.
+	header := initVersion1()
+
+	// Transport protocol has been processed already.
+	header.TransportProtocol = transportProtocol
+
+	// When UNKNOWN, set the command to LOCAL and return early
+	if header.TransportProtocol == UNSPEC {
+		header.Command = LOCAL
+		return header, nil
+	}
+
+	// Otherwise, continue to read addresses and ports
+	sourceIP, err := parseV1IPAddress(header.TransportProtocol, tokens[2])
+	if err != nil {
+		return nil, err
+	}
+	destIP, err := parseV1IPAddress(header.TransportProtocol, tokens[3])
+	if err != nil {
+		return nil, err
+	}
+	sourcePort, err := parseV1PortNumber(tokens[4])
+	if err != nil {
+		return nil, err
+	}
+	destPort, err := parseV1PortNumber(tokens[5])
+	if err != nil {
+		return nil, err
+	}
+	header.SourceAddr = &net.TCPAddr{
+		IP:   sourceIP,
+		Port: sourcePort,
+	}
+	header.DestinationAddr = &net.TCPAddr{
+		IP:   destIP,
+		Port: destPort,
+	}
+
+	return header, nil
+}
+
+func (header *Header) formatVersion1() ([]byte, error) {
+	// As of version 1, only "TCP4" ( \x54 \x43 \x50 \x34 ) for TCP over IPv4,
+	// and "TCP6" ( \x54 \x43 \x50 \x36 ) for TCP over IPv6 are allowed.
+	var proto string
+	switch header.TransportProtocol {
+	case TCPv4:
+		proto = "TCP4"
+	case TCPv6:
+		proto = "TCP6"
+	default:
+		// Unknown connection (short form)
+		return []byte("PROXY UNKNOWN" + crlf), nil
+	}
+
+	sourceAddr, sourceOK := header.SourceAddr.(*net.TCPAddr)
+	destAddr, destOK := header.DestinationAddr.(*net.TCPAddr)
+	if !sourceOK || !destOK {
+		return nil, ErrInvalidAddress
+	}
+
+	sourceIP, destIP := sourceAddr.IP, destAddr.IP
+	switch header.TransportProtocol {
+	case TCPv4:
+		sourceIP = sourceIP.To4()
+		destIP = destIP.To4()
+	case TCPv6:
+		sourceIP = sourceIP.To16()
+		destIP = destIP.To16()
+	}
+	if sourceIP == nil || destIP == nil {
+		return nil, ErrInvalidAddress
+	}
+
+	buf := bytes.NewBuffer(make([]byte, 0, 108))
+	buf.Write(SIGV1)
+	buf.WriteString(separator)
+	buf.WriteString(proto)
+	buf.WriteString(separator)
+	buf.WriteString(sourceIP.String())
+	buf.WriteString(separator)
+	buf.WriteString(destIP.String())
+	buf.WriteString(separator)
+	buf.WriteString(strconv.Itoa(sourceAddr.Port))
+	buf.WriteString(separator)
+	buf.WriteString(strconv.Itoa(destAddr.Port))
+	buf.WriteString(crlf)
+
+	return buf.Bytes(), nil
+}
+
+func parseV1PortNumber(portStr string) (int, error) {
+	port, err := strconv.Atoi(portStr)
+	if err != nil || port < 0 || port > 65535 {
+		return 0, ErrInvalidPortNumber
+	}
+	return port, nil
+}
+
+func parseV1IPAddress(protocol AddressFamilyAndProtocol, addrStr string) (addr net.IP, err error) {
+	addr = net.ParseIP(addrStr)
+	tryV4 := addr.To4()
+	if (protocol == TCPv4 && tryV4 == nil) || (protocol == TCPv6 && tryV4 != nil) {
+		err = ErrInvalidAddress
+	}
+	return
+}
diff --git a/vendor/github.com/pires/go-proxyproto/v2.go b/vendor/github.com/pires/go-proxyproto/v2.go
new file mode 100644
index 00000000..74bf3f07
--- /dev/null
+++ b/vendor/github.com/pires/go-proxyproto/v2.go
@@ -0,0 +1,285 @@
+package proxyproto
+
+import (
+	"bufio"
+	"bytes"
+	"encoding/binary"
+	"errors"
+	"io"
+	"net"
+)
+
+var (
+	lengthUnspec      = uint16(0)
+	lengthV4          = uint16(12)
+	lengthV6          = uint16(36)
+	lengthUnix        = uint16(216)
+	lengthUnspecBytes = func() []byte {
+		a := make([]byte, 2)
+		binary.BigEndian.PutUint16(a, lengthUnspec)
+		return a
+	}()
+	lengthV4Bytes = func() []byte {
+		a := make([]byte, 2)
+		binary.BigEndian.PutUint16(a, lengthV4)
+		return a
+	}()
+	lengthV6Bytes = func() []byte {
+		a := make([]byte, 2)
+		binary.BigEndian.PutUint16(a, lengthV6)
+		return a
+	}()
+	lengthUnixBytes = func() []byte {
+		a := make([]byte, 2)
+		binary.BigEndian.PutUint16(a, lengthUnix)
+		return a
+	}()
+	errUint16Overflow = errors.New("proxyproto: uint16 overflow")
+)
+
+type _ports struct {
+	SrcPort uint16
+	DstPort uint16
+}
+
+type _addr4 struct {
+	Src     [4]byte
+	Dst     [4]byte
+	SrcPort uint16
+	DstPort uint16
+}
+
+type _addr6 struct {
+	Src [16]byte
+	Dst [16]byte
+	_ports
+}
+
+type _addrUnix struct {
+	Src [108]byte
+	Dst [108]byte
+}
+
+func parseVersion2(reader *bufio.Reader) (header *Header, err error) {
+	// Skip first 12 bytes (signature)
+	for i := 0; i < 12; i++ {
+		if _, err = reader.ReadByte(); err != nil {
+			return nil, ErrCantReadProtocolVersionAndCommand
+		}
+	}
+
+	header = new(Header)
+	header.Version = 2
+
+	// Read the 13th byte, protocol version and command
+	b13, err := reader.ReadByte()
+	if err != nil {
+		return nil, ErrCantReadProtocolVersionAndCommand
+	}
+	header.Command = ProtocolVersionAndCommand(b13)
+	if _, ok := supportedCommand[header.Command]; !ok {
+		return nil, ErrUnsupportedProtocolVersionAndCommand
+	}
+
+	// Read the 14th byte, address family and protocol
+	b14, err := reader.ReadByte()
+	if err != nil {
+		return nil, ErrCantReadAddressFamilyAndProtocol
+	}
+	header.TransportProtocol = AddressFamilyAndProtocol(b14)
+	// UNSPEC is only supported when LOCAL is set.
+	if header.TransportProtocol == UNSPEC && header.Command != LOCAL {
+		return nil, ErrUnsupportedAddressFamilyAndProtocol
+	}
+
+	// Make sure there are bytes available as specified in length
+	var length uint16
+	if err := binary.Read(io.LimitReader(reader, 2), binary.BigEndian, &length); err != nil {
+		return nil, ErrCantReadLength
+	}
+	if !header.validateLength(length) {
+		return nil, ErrInvalidLength
+	}
+
+	// Return early if the length is zero, which means that
+	// there's no address information and TLVs present for UNSPEC.
+	if length == 0 {
+		return header, nil
+	}
+
+	if _, err := reader.Peek(int(length)); err != nil {
+		return nil, ErrInvalidLength
+	}
+
+	// Length-limited reader for payload section
+	payloadReader := io.LimitReader(reader, int64(length)).(*io.LimitedReader)
+
+	// Read addresses and ports for protocols other than UNSPEC.
+	// Ignore address information for UNSPEC, and skip straight to read TLVs,
+	// since the length is greater than zero.
+	if header.TransportProtocol != UNSPEC {
+		if header.TransportProtocol.IsIPv4() {
+			var addr _addr4
+			if err := binary.Read(payloadReader, binary.BigEndian, &addr); err != nil {
+				return nil, ErrInvalidAddress
+			}
+			header.SourceAddr = newIPAddr(header.TransportProtocol, addr.Src[:], addr.SrcPort)
+			header.DestinationAddr = newIPAddr(header.TransportProtocol, addr.Dst[:], addr.DstPort)
+		} else if header.TransportProtocol.IsIPv6() {
+			var addr _addr6
+			if err := binary.Read(payloadReader, binary.BigEndian, &addr); err != nil {
+				return nil, ErrInvalidAddress
+			}
+			header.SourceAddr = newIPAddr(header.TransportProtocol, addr.Src[:], addr.SrcPort)
+			header.DestinationAddr = newIPAddr(header.TransportProtocol, addr.Dst[:], addr.DstPort)
+		} else if header.TransportProtocol.IsUnix() {
+			var addr _addrUnix
+			if err := binary.Read(payloadReader, binary.BigEndian, &addr); err != nil {
+				return nil, ErrInvalidAddress
+			}
+
+			network := "unix"
+			if header.TransportProtocol.IsDatagram() {
+				network = "unixgram"
+			}
+
+			header.SourceAddr = &net.UnixAddr{
+				Net:  network,
+				Name: parseUnixName(addr.Src[:]),
+			}
+			header.DestinationAddr = &net.UnixAddr{
+				Net:  network,
+				Name: parseUnixName(addr.Dst[:]),
+			}
+		}
+	}
+
+	// Copy bytes for optional Type-Length-Value vector
+	header.rawTLVs = make([]byte, payloadReader.N) // Allocate minimum size slice
+	if _, err = io.ReadFull(payloadReader, header.rawTLVs); err != nil && err != io.EOF {
+		return nil, err
+	}
+
+	return header, nil
+}
+
+func (header *Header) formatVersion2() ([]byte, error) {
+	var buf bytes.Buffer
+	buf.Write(SIGV2)
+	buf.WriteByte(header.Command.toByte())
+	buf.WriteByte(header.TransportProtocol.toByte())
+	if header.TransportProtocol.IsUnspec() {
+		// For UNSPEC, write no addresses and ports but only TLVs if they are present
+		hdrLen, err := addTLVLen(lengthUnspecBytes, len(header.rawTLVs))
+		if err != nil {
+			return nil, err
+		}
+		buf.Write(hdrLen)
+	} else {
+		var addrSrc, addrDst []byte
+		if header.TransportProtocol.IsIPv4() {
+			hdrLen, err := addTLVLen(lengthV4Bytes, len(header.rawTLVs))
+			if err != nil {
+				return nil, err
+			}
+			buf.Write(hdrLen)
+			sourceIP, destIP, _ := header.IPs()
+			addrSrc = sourceIP.To4()
+			addrDst = destIP.To4()
+		} else if header.TransportProtocol.IsIPv6() {
+			hdrLen, err := addTLVLen(lengthV6Bytes, len(header.rawTLVs))
+			if err != nil {
+				return nil, err
+			}
+			buf.Write(hdrLen)
+			sourceIP, destIP, _ := header.IPs()
+			addrSrc = sourceIP.To16()
+			addrDst = destIP.To16()
+		} else if header.TransportProtocol.IsUnix() {
+			buf.Write(lengthUnixBytes)
+			sourceAddr, destAddr, ok := header.UnixAddrs()
+			if !ok {
+				return nil, ErrInvalidAddress
+			}
+			addrSrc = formatUnixName(sourceAddr.Name)
+			addrDst = formatUnixName(destAddr.Name)
+		}
+
+		if addrSrc == nil || addrDst == nil {
+			return nil, ErrInvalidAddress
+		}
+		buf.Write(addrSrc)
+		buf.Write(addrDst)
+
+		if sourcePort, destPort, ok := header.Ports(); ok {
+			portBytes := make([]byte, 2)
+
+			binary.BigEndian.PutUint16(portBytes, uint16(sourcePort))
+			buf.Write(portBytes)
+
+			binary.BigEndian.PutUint16(portBytes, uint16(destPort))
+			buf.Write(portBytes)
+		}
+	}
+
+	if len(header.rawTLVs) > 0 {
+		buf.Write(header.rawTLVs)
+	}
+
+	return buf.Bytes(), nil
+}
+
+func (header *Header) validateLength(length uint16) bool {
+	if header.TransportProtocol.IsIPv4() {
+		return length >= lengthV4
+	} else if header.TransportProtocol.IsIPv6() {
+		return length >= lengthV6
+	} else if header.TransportProtocol.IsUnix() {
+		return length >= lengthUnix
+	} else if header.TransportProtocol.IsUnspec() {
+		return length >= lengthUnspec
+	}
+	return false
+}
+
+// addTLVLen adds the length of the TLV to the header length or errors on uint16 overflow.
+func addTLVLen(cur []byte, tlvLen int) ([]byte, error) {
+	if tlvLen == 0 {
+		return cur, nil
+	}
+	curLen := binary.BigEndian.Uint16(cur)
+	newLen := int(curLen) + tlvLen
+	if newLen >= 1<<16 {
+		return nil, errUint16Overflow
+	}
+	a := make([]byte, 2)
+	binary.BigEndian.PutUint16(a, uint16(newLen))
+	return a, nil
+}
+
+func newIPAddr(transport AddressFamilyAndProtocol, ip net.IP, port uint16) net.Addr {
+	if transport.IsStream() {
+		return &net.TCPAddr{IP: ip, Port: int(port)}
+	} else if transport.IsDatagram() {
+		return &net.UDPAddr{IP: ip, Port: int(port)}
+	} else {
+		return nil
+	}
+}
+
+func parseUnixName(b []byte) string {
+	i := bytes.IndexByte(b, 0)
+	if i < 0 {
+		return string(b)
+	}
+	return string(b[:i])
+}
+
+func formatUnixName(name string) []byte {
+	n := int(lengthUnix) / 2
+	if len(name) >= n {
+		return []byte(name[:n])
+	}
+	pad := make([]byte, n-len(name))
+	return append([]byte(name), pad...)
+}
diff --git a/vendor/github.com/pires/go-proxyproto/version_cmd.go b/vendor/github.com/pires/go-proxyproto/version_cmd.go
new file mode 100644
index 00000000..59f20420
--- /dev/null
+++ b/vendor/github.com/pires/go-proxyproto/version_cmd.go
@@ -0,0 +1,47 @@
+package proxyproto
+
+// ProtocolVersionAndCommand represents the command in proxy protocol v2.
+// Command doesn't exist in v1 but it should be set since other parts of
+// this library may rely on it for determining connection details.
+type ProtocolVersionAndCommand byte
+
+const (
+	// LOCAL represents the LOCAL command in v2 or UNKNOWN transport in v1,
+	// in which case no address information is expected.
+	LOCAL ProtocolVersionAndCommand = '\x20'
+	// PROXY represents the PROXY command in v2 or transport is not UNKNOWN in v1,
+	// in which case valid local/remote address and port information is expected.
+	PROXY ProtocolVersionAndCommand = '\x21'
+)
+
+var supportedCommand = map[ProtocolVersionAndCommand]bool{
+	LOCAL: true,
+	PROXY: true,
+}
+
+// IsLocal returns true if the command in v2 is LOCAL or the transport in v1 is UNKNOWN,
+// i.e. when no address information is expected, false otherwise.
+func (pvc ProtocolVersionAndCommand) IsLocal() bool {
+	return LOCAL == pvc
+}
+
+// IsProxy returns true if the command in v2 is PROXY or the transport in v1 is not UNKNOWN,
+// i.e. when valid local/remote address and port information is expected, false otherwise.
+func (pvc ProtocolVersionAndCommand) IsProxy() bool {
+	return PROXY == pvc
+}
+
+// IsUnspec returns true if the command is unspecified, false otherwise.
+func (pvc ProtocolVersionAndCommand) IsUnspec() bool {
+	return !(pvc.IsLocal() || pvc.IsProxy())
+}
+
+func (pvc ProtocolVersionAndCommand) toByte() byte {
+	if pvc.IsLocal() {
+		return byte(LOCAL)
+	} else if pvc.IsProxy() {
+		return byte(PROXY)
+	}
+
+	return byte(LOCAL)
+}
diff --git a/vendor/golang.org/x/sys/plan9/syscall_plan9.go b/vendor/golang.org/x/sys/plan9/syscall_plan9.go
index 84e14714..723b1f40 100644
--- a/vendor/golang.org/x/sys/plan9/syscall_plan9.go
+++ b/vendor/golang.org/x/sys/plan9/syscall_plan9.go
@@ -132,8 +132,10 @@ func Pipe(p []int) (err error) {
 	}
 	var pp [2]int32
 	err = pipe(&pp)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
+	if err == nil {
+		p[0] = int(pp[0])
+		p[1] = int(pp[1])
+	}
 	return
 }
 
diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh
index 4945739e..a47b035f 100644
--- a/vendor/golang.org/x/sys/unix/mkerrors.sh
+++ b/vendor/golang.org/x/sys/unix/mkerrors.sh
@@ -261,6 +261,7 @@ struct ltchars {
 #include <linux/vm_sockets.h>
 #include <linux/wait.h>
 #include <linux/watchdog.h>
+#include <linux/wireguard.h>
 
 #include <mtd/ubi-user.h>
 #include <mtd/mtd-user.h>
@@ -606,6 +607,7 @@ ccflags="$@"
 		$2 ~ /^MTD/ ||
 		$2 ~ /^OTP/ ||
 		$2 ~ /^MEM/ ||
+		$2 ~ /^WG/ ||
 		$2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE)/ {printf("\t%s = C.%s\n", $2, $2)}
 		$2 ~ /^__WCOREFLAG$/ {next}
 		$2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)}
diff --git a/vendor/golang.org/x/sys/unix/syscall_aix.go b/vendor/golang.org/x/sys/unix/syscall_aix.go
index 6192750c..4f55c8d9 100644
--- a/vendor/golang.org/x/sys/unix/syscall_aix.go
+++ b/vendor/golang.org/x/sys/unix/syscall_aix.go
@@ -519,8 +519,10 @@ func Pipe(p []int) (err error) {
 	}
 	var pp [2]_C_int
 	err = pipe(&pp)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
+	if err == nil {
+		p[0] = int(pp[0])
+		p[1] = int(pp[1])
+	}
 	return
 }
 
diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin.go b/vendor/golang.org/x/sys/unix/syscall_darwin.go
index 8826f414..0eaab913 100644
--- a/vendor/golang.org/x/sys/unix/syscall_darwin.go
+++ b/vendor/golang.org/x/sys/unix/syscall_darwin.go
@@ -159,8 +159,10 @@ func Pipe(p []int) (err error) {
 	}
 	var x [2]int32
 	err = pipe(&x)
-	p[0] = int(x[0])
-	p[1] = int(x[1])
+	if err == nil {
+		p[0] = int(x[0])
+		p[1] = int(x[1])
+	}
 	return
 }
 
diff --git a/vendor/golang.org/x/sys/unix/syscall_dragonfly.go b/vendor/golang.org/x/sys/unix/syscall_dragonfly.go
index 5af108a5..2e37c316 100644
--- a/vendor/golang.org/x/sys/unix/syscall_dragonfly.go
+++ b/vendor/golang.org/x/sys/unix/syscall_dragonfly.go
@@ -101,7 +101,10 @@ func Pipe(p []int) (err error) {
 	if len(p) != 2 {
 		return EINVAL
 	}
-	p[0], p[1], err = pipe()
+	r, w, err := pipe()
+	if err == nil {
+		p[0], p[1] = r, w
+	}
 	return
 }
 
@@ -114,7 +117,10 @@ func Pipe2(p []int, flags int) (err error) {
 	var pp [2]_C_int
 	// pipe2 on dragonfly takes an fds array as an argument, but still
 	// returns the file descriptors.
-	p[0], p[1], err = pipe2(&pp, flags)
+	r, w, err := pipe2(&pp, flags)
+	if err == nil {
+		p[0], p[1] = r, w
+	}
 	return err
 }
 
diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd.go b/vendor/golang.org/x/sys/unix/syscall_freebsd.go
index 18c392cf..2f650ae6 100644
--- a/vendor/golang.org/x/sys/unix/syscall_freebsd.go
+++ b/vendor/golang.org/x/sys/unix/syscall_freebsd.go
@@ -110,8 +110,10 @@ func Pipe2(p []int, flags int) error {
 	}
 	var pp [2]_C_int
 	err := pipe2(&pp, flags)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
+	if err == nil {
+		p[0] = int(pp[0])
+		p[1] = int(pp[1])
+	}
 	return err
 }
 
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go
index 4bc5baf7..f432b068 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux.go
@@ -131,8 +131,10 @@ func Pipe2(p []int, flags int) error {
 	}
 	var pp [2]_C_int
 	err := pipe2(&pp, flags)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
+	if err == nil {
+		p[0] = int(pp[0])
+		p[1] = int(pp[1])
+	}
 	return err
 }
 
diff --git a/vendor/golang.org/x/sys/unix/syscall_netbsd.go b/vendor/golang.org/x/sys/unix/syscall_netbsd.go
index 853d5f0f..696fed49 100644
--- a/vendor/golang.org/x/sys/unix/syscall_netbsd.go
+++ b/vendor/golang.org/x/sys/unix/syscall_netbsd.go
@@ -110,14 +110,8 @@ func direntNamlen(buf []byte) (uint64, bool) {
 	return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
 }
 
-//sysnb	pipe() (fd1 int, fd2 int, err error)
-
 func Pipe(p []int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	p[0], p[1], err = pipe()
-	return
+	return Pipe2(p, 0)
 }
 
 //sysnb	pipe2(p *[2]_C_int, flags int) (err error)
@@ -128,8 +122,10 @@ func Pipe2(p []int, flags int) error {
 	}
 	var pp [2]_C_int
 	err := pipe2(&pp, flags)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
+	if err == nil {
+		p[0] = int(pp[0])
+		p[1] = int(pp[1])
+	}
 	return err
 }
 
diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd.go b/vendor/golang.org/x/sys/unix/syscall_openbsd.go
index 22b55038..11b1d419 100644
--- a/vendor/golang.org/x/sys/unix/syscall_openbsd.go
+++ b/vendor/golang.org/x/sys/unix/syscall_openbsd.go
@@ -87,8 +87,10 @@ func Pipe2(p []int, flags int) error {
 	}
 	var pp [2]_C_int
 	err := pipe2(&pp, flags)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
+	if err == nil {
+		p[0] = int(pp[0])
+		p[1] = int(pp[1])
+	}
 	return err
 }
 
diff --git a/vendor/golang.org/x/sys/unix/syscall_solaris.go b/vendor/golang.org/x/sys/unix/syscall_solaris.go
index 8b88ac21..5c813921 100644
--- a/vendor/golang.org/x/sys/unix/syscall_solaris.go
+++ b/vendor/golang.org/x/sys/unix/syscall_solaris.go
@@ -66,8 +66,10 @@ func Pipe(p []int) (err error) {
 	if n != 0 {
 		return err
 	}
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
+	if err == nil {
+		p[0] = int(pp[0])
+		p[1] = int(pp[1])
+	}
 	return nil
 }
 
@@ -79,8 +81,10 @@ func Pipe2(p []int, flags int) error {
 	}
 	var pp [2]_C_int
 	err := pipe2(&pp, flags)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
+	if err == nil {
+		p[0] = int(pp[0])
+		p[1] = int(pp[1])
+	}
 	return err
 }
 
diff --git a/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go b/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go
index 5fb76a14..f8616f45 100644
--- a/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go
+++ b/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go
@@ -579,8 +579,10 @@ func Pipe(p []int) (err error) {
 	}
 	var pp [2]_C_int
 	err = pipe(&pp)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
+	if err == nil {
+		p[0] = int(pp[0])
+		p[1] = int(pp[1])
+	}
 	return
 }
 
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go
index d175aae8..4e542058 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go
@@ -741,6 +741,7 @@ const (
 	ETH_P_QINQ2                                 = 0x9200
 	ETH_P_QINQ3                                 = 0x9300
 	ETH_P_RARP                                  = 0x8035
+	ETH_P_REALTEK                               = 0x8899
 	ETH_P_SCA                                   = 0x6007
 	ETH_P_SLOW                                  = 0x8809
 	ETH_P_SNAP                                  = 0x5
@@ -810,10 +811,12 @@ const (
 	FAN_EPIDFD                                  = -0x2
 	FAN_EVENT_INFO_TYPE_DFID                    = 0x3
 	FAN_EVENT_INFO_TYPE_DFID_NAME               = 0x2
+	FAN_EVENT_INFO_TYPE_ERROR                   = 0x5
 	FAN_EVENT_INFO_TYPE_FID                     = 0x1
 	FAN_EVENT_INFO_TYPE_PIDFD                   = 0x4
 	FAN_EVENT_METADATA_LEN                      = 0x18
 	FAN_EVENT_ON_CHILD                          = 0x8000000
+	FAN_FS_ERROR                                = 0x8000
 	FAN_MARK_ADD                                = 0x1
 	FAN_MARK_DONT_FOLLOW                        = 0x4
 	FAN_MARK_FILESYSTEM                         = 0x100
@@ -1827,6 +1830,8 @@ const (
 	PERF_MEM_BLK_DATA                           = 0x2
 	PERF_MEM_BLK_NA                             = 0x1
 	PERF_MEM_BLK_SHIFT                          = 0x28
+	PERF_MEM_HOPS_0                             = 0x1
+	PERF_MEM_HOPS_SHIFT                         = 0x2b
 	PERF_MEM_LOCK_LOCKED                        = 0x2
 	PERF_MEM_LOCK_NA                            = 0x1
 	PERF_MEM_LOCK_SHIFT                         = 0x18
@@ -1986,6 +1991,9 @@ const (
 	PR_SCHED_CORE_CREATE                        = 0x1
 	PR_SCHED_CORE_GET                           = 0x0
 	PR_SCHED_CORE_MAX                           = 0x4
+	PR_SCHED_CORE_SCOPE_PROCESS_GROUP           = 0x2
+	PR_SCHED_CORE_SCOPE_THREAD                  = 0x0
+	PR_SCHED_CORE_SCOPE_THREAD_GROUP            = 0x1
 	PR_SCHED_CORE_SHARE_FROM                    = 0x3
 	PR_SCHED_CORE_SHARE_TO                      = 0x2
 	PR_SET_CHILD_SUBREAPER                      = 0x24
@@ -2167,12 +2175,23 @@ const (
 	RTCF_NAT                                    = 0x800000
 	RTCF_VALVE                                  = 0x200000
 	RTC_AF                                      = 0x20
+	RTC_BSM_DIRECT                              = 0x1
+	RTC_BSM_DISABLED                            = 0x0
+	RTC_BSM_LEVEL                               = 0x2
+	RTC_BSM_STANDBY                             = 0x3
 	RTC_FEATURE_ALARM                           = 0x0
+	RTC_FEATURE_ALARM_RES_2S                    = 0x3
 	RTC_FEATURE_ALARM_RES_MINUTE                = 0x1
-	RTC_FEATURE_CNT                             = 0x3
+	RTC_FEATURE_BACKUP_SWITCH_MODE              = 0x6
+	RTC_FEATURE_CNT                             = 0x7
+	RTC_FEATURE_CORRECTION                      = 0x5
 	RTC_FEATURE_NEED_WEEK_DAY                   = 0x2
+	RTC_FEATURE_UPDATE_INTERRUPT                = 0x4
 	RTC_IRQF                                    = 0x80
 	RTC_MAX_FREQ                                = 0x2000
+	RTC_PARAM_BACKUP_SWITCH_MODE                = 0x2
+	RTC_PARAM_CORRECTION                        = 0x1
+	RTC_PARAM_FEATURES                          = 0x0
 	RTC_PF                                      = 0x40
 	RTC_UF                                      = 0x10
 	RTF_ADDRCLASSMASK                           = 0xf8000000
@@ -2532,6 +2551,8 @@ const (
 	SO_VM_SOCKETS_BUFFER_MIN_SIZE               = 0x1
 	SO_VM_SOCKETS_BUFFER_SIZE                   = 0x0
 	SO_VM_SOCKETS_CONNECT_TIMEOUT               = 0x6
+	SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW           = 0x8
+	SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD           = 0x6
 	SO_VM_SOCKETS_NONBLOCK_TXRX                 = 0x7
 	SO_VM_SOCKETS_PEER_HOST_VM_ID               = 0x3
 	SO_VM_SOCKETS_TRUSTED                       = 0x5
@@ -2826,6 +2847,13 @@ const (
 	WDIOS_TEMPPANIC                             = 0x4
 	WDIOS_UNKNOWN                               = -0x1
 	WEXITED                                     = 0x4
+	WGALLOWEDIP_A_MAX                           = 0x3
+	WGDEVICE_A_MAX                              = 0x8
+	WGPEER_A_MAX                                = 0xa
+	WG_CMD_MAX                                  = 0x1
+	WG_GENL_NAME                                = "wireguard"
+	WG_GENL_VERSION                             = 0x1
+	WG_KEY_LEN                                  = 0x20
 	WIN_ACKMEDIACHANGE                          = 0xdb
 	WIN_CHECKPOWERMODE1                         = 0xe5
 	WIN_CHECKPOWERMODE2                         = 0x98
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go
index 3ca40ca7..234fd4a5 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go
@@ -250,6 +250,8 @@ const (
 	RTC_EPOCH_SET                    = 0x4004700e
 	RTC_IRQP_READ                    = 0x8004700b
 	RTC_IRQP_SET                     = 0x4004700c
+	RTC_PARAM_GET                    = 0x40187013
+	RTC_PARAM_SET                    = 0x40187014
 	RTC_PIE_OFF                      = 0x7006
 	RTC_PIE_ON                       = 0x7005
 	RTC_PLL_GET                      = 0x801c7011
@@ -327,6 +329,7 @@ const (
 	SO_RCVTIMEO                      = 0x14
 	SO_RCVTIMEO_NEW                  = 0x42
 	SO_RCVTIMEO_OLD                  = 0x14
+	SO_RESERVE_MEM                   = 0x49
 	SO_REUSEADDR                     = 0x2
 	SO_REUSEPORT                     = 0xf
 	SO_RXQ_OVFL                      = 0x28
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go
index ead33209..58619b75 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go
@@ -251,6 +251,8 @@ const (
 	RTC_EPOCH_SET                    = 0x4008700e
 	RTC_IRQP_READ                    = 0x8008700b
 	RTC_IRQP_SET                     = 0x4008700c
+	RTC_PARAM_GET                    = 0x40187013
+	RTC_PARAM_SET                    = 0x40187014
 	RTC_PIE_OFF                      = 0x7006
 	RTC_PIE_ON                       = 0x7005
 	RTC_PLL_GET                      = 0x80207011
@@ -328,6 +330,7 @@ const (
 	SO_RCVTIMEO                      = 0x14
 	SO_RCVTIMEO_NEW                  = 0x42
 	SO_RCVTIMEO_OLD                  = 0x14
+	SO_RESERVE_MEM                   = 0x49
 	SO_REUSEADDR                     = 0x2
 	SO_REUSEPORT                     = 0xf
 	SO_RXQ_OVFL                      = 0x28
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go
index 39bdc945..3a64ff59 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go
@@ -257,6 +257,8 @@ const (
 	RTC_EPOCH_SET                    = 0x4004700e
 	RTC_IRQP_READ                    = 0x8004700b
 	RTC_IRQP_SET                     = 0x4004700c
+	RTC_PARAM_GET                    = 0x40187013
+	RTC_PARAM_SET                    = 0x40187014
 	RTC_PIE_OFF                      = 0x7006
 	RTC_PIE_ON                       = 0x7005
 	RTC_PLL_GET                      = 0x801c7011
@@ -334,6 +336,7 @@ const (
 	SO_RCVTIMEO                      = 0x14
 	SO_RCVTIMEO_NEW                  = 0x42
 	SO_RCVTIMEO_OLD                  = 0x14
+	SO_RESERVE_MEM                   = 0x49
 	SO_REUSEADDR                     = 0x2
 	SO_REUSEPORT                     = 0xf
 	SO_RXQ_OVFL                      = 0x28
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go
index 9aec987d..abe0b925 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go
@@ -247,6 +247,8 @@ const (
 	RTC_EPOCH_SET                    = 0x4008700e
 	RTC_IRQP_READ                    = 0x8008700b
 	RTC_IRQP_SET                     = 0x4008700c
+	RTC_PARAM_GET                    = 0x40187013
+	RTC_PARAM_SET                    = 0x40187014
 	RTC_PIE_OFF                      = 0x7006
 	RTC_PIE_ON                       = 0x7005
 	RTC_PLL_GET                      = 0x80207011
@@ -324,6 +326,7 @@ const (
 	SO_RCVTIMEO                      = 0x14
 	SO_RCVTIMEO_NEW                  = 0x42
 	SO_RCVTIMEO_OLD                  = 0x14
+	SO_RESERVE_MEM                   = 0x49
 	SO_REUSEADDR                     = 0x2
 	SO_REUSEPORT                     = 0xf
 	SO_RXQ_OVFL                      = 0x28
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go
index a8bba949..14d7a843 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go
@@ -250,6 +250,8 @@ const (
 	RTC_EPOCH_SET                    = 0x8004700e
 	RTC_IRQP_READ                    = 0x4004700b
 	RTC_IRQP_SET                     = 0x8004700c
+	RTC_PARAM_GET                    = 0x80187013
+	RTC_PARAM_SET                    = 0x80187014
 	RTC_PIE_OFF                      = 0x20007006
 	RTC_PIE_ON                       = 0x20007005
 	RTC_PLL_GET                      = 0x401c7011
@@ -327,6 +329,7 @@ const (
 	SO_RCVTIMEO                      = 0x1006
 	SO_RCVTIMEO_NEW                  = 0x42
 	SO_RCVTIMEO_OLD                  = 0x1006
+	SO_RESERVE_MEM                   = 0x49
 	SO_REUSEADDR                     = 0x4
 	SO_REUSEPORT                     = 0x200
 	SO_RXQ_OVFL                      = 0x28
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go
index ee9e7e20..99e7c4ac 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go
@@ -250,6 +250,8 @@ const (
 	RTC_EPOCH_SET                    = 0x8008700e
 	RTC_IRQP_READ                    = 0x4008700b
 	RTC_IRQP_SET                     = 0x8008700c
+	RTC_PARAM_GET                    = 0x80187013
+	RTC_PARAM_SET                    = 0x80187014
 	RTC_PIE_OFF                      = 0x20007006
 	RTC_PIE_ON                       = 0x20007005
 	RTC_PLL_GET                      = 0x40207011
@@ -327,6 +329,7 @@ const (
 	SO_RCVTIMEO                      = 0x1006
 	SO_RCVTIMEO_NEW                  = 0x42
 	SO_RCVTIMEO_OLD                  = 0x1006
+	SO_RESERVE_MEM                   = 0x49
 	SO_REUSEADDR                     = 0x4
 	SO_REUSEPORT                     = 0x200
 	SO_RXQ_OVFL                      = 0x28
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go
index ba4b288a..496364c3 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go
@@ -250,6 +250,8 @@ const (
 	RTC_EPOCH_SET                    = 0x8008700e
 	RTC_IRQP_READ                    = 0x4008700b
 	RTC_IRQP_SET                     = 0x8008700c
+	RTC_PARAM_GET                    = 0x80187013
+	RTC_PARAM_SET                    = 0x80187014
 	RTC_PIE_OFF                      = 0x20007006
 	RTC_PIE_ON                       = 0x20007005
 	RTC_PLL_GET                      = 0x40207011
@@ -327,6 +329,7 @@ const (
 	SO_RCVTIMEO                      = 0x1006
 	SO_RCVTIMEO_NEW                  = 0x42
 	SO_RCVTIMEO_OLD                  = 0x1006
+	SO_RESERVE_MEM                   = 0x49
 	SO_REUSEADDR                     = 0x4
 	SO_REUSEPORT                     = 0x200
 	SO_RXQ_OVFL                      = 0x28
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go
index bc93afc3..3e408308 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go
@@ -250,6 +250,8 @@ const (
 	RTC_EPOCH_SET                    = 0x8004700e
 	RTC_IRQP_READ                    = 0x4004700b
 	RTC_IRQP_SET                     = 0x8004700c
+	RTC_PARAM_GET                    = 0x80187013
+	RTC_PARAM_SET                    = 0x80187014
 	RTC_PIE_OFF                      = 0x20007006
 	RTC_PIE_ON                       = 0x20007005
 	RTC_PLL_GET                      = 0x401c7011
@@ -327,6 +329,7 @@ const (
 	SO_RCVTIMEO                      = 0x1006
 	SO_RCVTIMEO_NEW                  = 0x42
 	SO_RCVTIMEO_OLD                  = 0x1006
+	SO_RESERVE_MEM                   = 0x49
 	SO_REUSEADDR                     = 0x4
 	SO_REUSEPORT                     = 0x200
 	SO_RXQ_OVFL                      = 0x28
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go
index 9295e694..1151a7df 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go
@@ -305,6 +305,8 @@ const (
 	RTC_EPOCH_SET                    = 0x8004700e
 	RTC_IRQP_READ                    = 0x4004700b
 	RTC_IRQP_SET                     = 0x8004700c
+	RTC_PARAM_GET                    = 0x80187013
+	RTC_PARAM_SET                    = 0x80187014
 	RTC_PIE_OFF                      = 0x20007006
 	RTC_PIE_ON                       = 0x20007005
 	RTC_PLL_GET                      = 0x401c7011
@@ -382,6 +384,7 @@ const (
 	SO_RCVTIMEO                      = 0x12
 	SO_RCVTIMEO_NEW                  = 0x42
 	SO_RCVTIMEO_OLD                  = 0x12
+	SO_RESERVE_MEM                   = 0x49
 	SO_REUSEADDR                     = 0x2
 	SO_REUSEPORT                     = 0xf
 	SO_RXQ_OVFL                      = 0x28
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go
index 1fa081c9..ed17f249 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go
@@ -309,6 +309,8 @@ const (
 	RTC_EPOCH_SET                    = 0x8008700e
 	RTC_IRQP_READ                    = 0x4008700b
 	RTC_IRQP_SET                     = 0x8008700c
+	RTC_PARAM_GET                    = 0x80187013
+	RTC_PARAM_SET                    = 0x80187014
 	RTC_PIE_OFF                      = 0x20007006
 	RTC_PIE_ON                       = 0x20007005
 	RTC_PLL_GET                      = 0x40207011
@@ -386,6 +388,7 @@ const (
 	SO_RCVTIMEO                      = 0x12
 	SO_RCVTIMEO_NEW                  = 0x42
 	SO_RCVTIMEO_OLD                  = 0x12
+	SO_RESERVE_MEM                   = 0x49
 	SO_REUSEADDR                     = 0x2
 	SO_REUSEPORT                     = 0xf
 	SO_RXQ_OVFL                      = 0x28
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go
index 74b32114..d84a37c1 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go
@@ -309,6 +309,8 @@ const (
 	RTC_EPOCH_SET                    = 0x8008700e
 	RTC_IRQP_READ                    = 0x4008700b
 	RTC_IRQP_SET                     = 0x8008700c
+	RTC_PARAM_GET                    = 0x80187013
+	RTC_PARAM_SET                    = 0x80187014
 	RTC_PIE_OFF                      = 0x20007006
 	RTC_PIE_ON                       = 0x20007005
 	RTC_PLL_GET                      = 0x40207011
@@ -386,6 +388,7 @@ const (
 	SO_RCVTIMEO                      = 0x12
 	SO_RCVTIMEO_NEW                  = 0x42
 	SO_RCVTIMEO_OLD                  = 0x12
+	SO_RESERVE_MEM                   = 0x49
 	SO_REUSEADDR                     = 0x2
 	SO_REUSEPORT                     = 0xf
 	SO_RXQ_OVFL                      = 0x28
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go
index c91c8ac5..5cafba83 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go
@@ -238,6 +238,8 @@ const (
 	RTC_EPOCH_SET                    = 0x4008700e
 	RTC_IRQP_READ                    = 0x8008700b
 	RTC_IRQP_SET                     = 0x4008700c
+	RTC_PARAM_GET                    = 0x40187013
+	RTC_PARAM_SET                    = 0x40187014
 	RTC_PIE_OFF                      = 0x7006
 	RTC_PIE_ON                       = 0x7005
 	RTC_PLL_GET                      = 0x80207011
@@ -315,6 +317,7 @@ const (
 	SO_RCVTIMEO                      = 0x14
 	SO_RCVTIMEO_NEW                  = 0x42
 	SO_RCVTIMEO_OLD                  = 0x14
+	SO_RESERVE_MEM                   = 0x49
 	SO_REUSEADDR                     = 0x2
 	SO_REUSEPORT                     = 0xf
 	SO_RXQ_OVFL                      = 0x28
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go
index b66bf222..6d122da4 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go
@@ -313,6 +313,8 @@ const (
 	RTC_EPOCH_SET                    = 0x4008700e
 	RTC_IRQP_READ                    = 0x8008700b
 	RTC_IRQP_SET                     = 0x4008700c
+	RTC_PARAM_GET                    = 0x40187013
+	RTC_PARAM_SET                    = 0x40187014
 	RTC_PIE_OFF                      = 0x7006
 	RTC_PIE_ON                       = 0x7005
 	RTC_PLL_GET                      = 0x80207011
@@ -390,6 +392,7 @@ const (
 	SO_RCVTIMEO                      = 0x14
 	SO_RCVTIMEO_NEW                  = 0x42
 	SO_RCVTIMEO_OLD                  = 0x14
+	SO_RESERVE_MEM                   = 0x49
 	SO_REUSEADDR                     = 0x2
 	SO_REUSEPORT                     = 0xf
 	SO_RXQ_OVFL                      = 0x28
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go
index f7fb149b..6bd19e51 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go
@@ -304,6 +304,8 @@ const (
 	RTC_EPOCH_SET                    = 0x8008700e
 	RTC_IRQP_READ                    = 0x4008700b
 	RTC_IRQP_SET                     = 0x8008700c
+	RTC_PARAM_GET                    = 0x80187013
+	RTC_PARAM_SET                    = 0x80187014
 	RTC_PIE_OFF                      = 0x20007006
 	RTC_PIE_ON                       = 0x20007005
 	RTC_PLL_GET                      = 0x40207011
@@ -381,6 +383,7 @@ const (
 	SO_RCVTIMEO                      = 0x2000
 	SO_RCVTIMEO_NEW                  = 0x44
 	SO_RCVTIMEO_OLD                  = 0x2000
+	SO_RESERVE_MEM                   = 0x52
 	SO_REUSEADDR                     = 0x4
 	SO_REUSEPORT                     = 0x200
 	SO_RXQ_OVFL                      = 0x24
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go
index 4726ab30..51d0c074 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go
@@ -351,18 +351,6 @@ func Munlockall() (err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func pipe() (fd1 int, fd2 int, err error) {
-	r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0)
-	fd1 = int(r0)
-	fd2 = int(r1)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
 func pipe2(p *[2]_C_int, flags int) (err error) {
 	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
 	if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go
index fe71456d..df2efb6d 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go
@@ -351,18 +351,6 @@ func Munlockall() (err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func pipe() (fd1 int, fd2 int, err error) {
-	r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0)
-	fd1 = int(r0)
-	fd2 = int(r1)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
 func pipe2(p *[2]_C_int, flags int) (err error) {
 	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
 	if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go
index 0b5b2f01..c8536c2c 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go
@@ -351,18 +351,6 @@ func Munlockall() (err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func pipe() (fd1 int, fd2 int, err error) {
-	r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0)
-	fd1 = int(r0)
-	fd2 = int(r1)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
 func pipe2(p *[2]_C_int, flags int) (err error) {
 	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
 	if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go
index bfca2864..8b981bfc 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go
@@ -351,18 +351,6 @@ func Munlockall() (err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func pipe() (fd1 int, fd2 int, err error) {
-	r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0)
-	fd1 = int(r0)
-	fd2 = int(r1)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
 func pipe2(p *[2]_C_int, flags int) (err error) {
 	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
 	if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go
index 31847d23..cac1f758 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go
@@ -445,4 +445,5 @@ const (
 	SYS_LANDLOCK_RESTRICT_SELF       = 446
 	SYS_MEMFD_SECRET                 = 447
 	SYS_PROCESS_MRELEASE             = 448
+	SYS_FUTEX_WAITV                  = 449
 )
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go
index 3503cbbd..f327e4a0 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go
@@ -367,4 +367,5 @@ const (
 	SYS_LANDLOCK_RESTRICT_SELF  = 446
 	SYS_MEMFD_SECRET            = 447
 	SYS_PROCESS_MRELEASE        = 448
+	SYS_FUTEX_WAITV             = 449
 )
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go
index 5ecd24bf..fb06a08d 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go
@@ -409,4 +409,5 @@ const (
 	SYS_LANDLOCK_ADD_RULE            = 445
 	SYS_LANDLOCK_RESTRICT_SELF       = 446
 	SYS_PROCESS_MRELEASE             = 448
+	SYS_FUTEX_WAITV                  = 449
 )
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go
index 7e5c94cc..58285646 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go
@@ -312,4 +312,5 @@ const (
 	SYS_LANDLOCK_RESTRICT_SELF  = 446
 	SYS_MEMFD_SECRET            = 447
 	SYS_PROCESS_MRELEASE        = 448
+	SYS_FUTEX_WAITV             = 449
 )
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go
index e1e2a2bf..3b0418e6 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go
@@ -429,4 +429,5 @@ const (
 	SYS_LANDLOCK_ADD_RULE            = 4445
 	SYS_LANDLOCK_RESTRICT_SELF       = 4446
 	SYS_PROCESS_MRELEASE             = 4448
+	SYS_FUTEX_WAITV                  = 4449
 )
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go
index 7651915a..314ebf16 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go
@@ -359,4 +359,5 @@ const (
 	SYS_LANDLOCK_ADD_RULE       = 5445
 	SYS_LANDLOCK_RESTRICT_SELF  = 5446
 	SYS_PROCESS_MRELEASE        = 5448
+	SYS_FUTEX_WAITV             = 5449
 )
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go
index a26a2c05..b8fbb937 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go
@@ -359,4 +359,5 @@ const (
 	SYS_LANDLOCK_ADD_RULE       = 5445
 	SYS_LANDLOCK_RESTRICT_SELF  = 5446
 	SYS_PROCESS_MRELEASE        = 5448
+	SYS_FUTEX_WAITV             = 5449
 )
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go
index fda9a6a9..ee309b2b 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go
@@ -429,4 +429,5 @@ const (
 	SYS_LANDLOCK_ADD_RULE            = 4445
 	SYS_LANDLOCK_RESTRICT_SELF       = 4446
 	SYS_PROCESS_MRELEASE             = 4448
+	SYS_FUTEX_WAITV                  = 4449
 )
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go
index e8496150..ac374810 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go
@@ -436,4 +436,5 @@ const (
 	SYS_LANDLOCK_ADD_RULE            = 445
 	SYS_LANDLOCK_RESTRICT_SELF       = 446
 	SYS_PROCESS_MRELEASE             = 448
+	SYS_FUTEX_WAITV                  = 449
 )
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go
index 5ee0678a..5aa47211 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go
@@ -408,4 +408,5 @@ const (
 	SYS_LANDLOCK_ADD_RULE       = 445
 	SYS_LANDLOCK_RESTRICT_SELF  = 446
 	SYS_PROCESS_MRELEASE        = 448
+	SYS_FUTEX_WAITV             = 449
 )
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go
index 29c0f9a3..0793ac1a 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go
@@ -408,4 +408,5 @@ const (
 	SYS_LANDLOCK_ADD_RULE       = 445
 	SYS_LANDLOCK_RESTRICT_SELF  = 446
 	SYS_PROCESS_MRELEASE        = 448
+	SYS_FUTEX_WAITV             = 449
 )
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go
index 5c9a9a3b..a520962e 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go
@@ -310,4 +310,5 @@ const (
 	SYS_LANDLOCK_ADD_RULE       = 445
 	SYS_LANDLOCK_RESTRICT_SELF  = 446
 	SYS_PROCESS_MRELEASE        = 448
+	SYS_FUTEX_WAITV             = 449
 )
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go
index 913f50f9..d1738586 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go
@@ -373,4 +373,5 @@ const (
 	SYS_LANDLOCK_ADD_RULE       = 445
 	SYS_LANDLOCK_RESTRICT_SELF  = 446
 	SYS_PROCESS_MRELEASE        = 448
+	SYS_FUTEX_WAITV             = 449
 )
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go
index 0de03a72..dfd5660f 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go
@@ -387,4 +387,5 @@ const (
 	SYS_LANDLOCK_ADD_RULE       = 445
 	SYS_LANDLOCK_RESTRICT_SELF  = 446
 	SYS_PROCESS_MRELEASE        = 448
+	SYS_FUTEX_WAITV             = 449
 )
diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go
index 37b52143..66788f15 100644
--- a/vendor/golang.org/x/sys/unix/ztypes_linux.go
+++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go
@@ -867,6 +867,7 @@ const (
 	CTRL_CMD_NEWMCAST_GRP      = 0x7
 	CTRL_CMD_DELMCAST_GRP      = 0x8
 	CTRL_CMD_GETMCAST_GRP      = 0x9
+	CTRL_CMD_GETPOLICY         = 0xa
 	CTRL_ATTR_UNSPEC           = 0x0
 	CTRL_ATTR_FAMILY_ID        = 0x1
 	CTRL_ATTR_FAMILY_NAME      = 0x2
@@ -875,12 +876,19 @@ const (
 	CTRL_ATTR_MAXATTR          = 0x5
 	CTRL_ATTR_OPS              = 0x6
 	CTRL_ATTR_MCAST_GROUPS     = 0x7
+	CTRL_ATTR_POLICY           = 0x8
+	CTRL_ATTR_OP_POLICY        = 0x9
+	CTRL_ATTR_OP               = 0xa
 	CTRL_ATTR_OP_UNSPEC        = 0x0
 	CTRL_ATTR_OP_ID            = 0x1
 	CTRL_ATTR_OP_FLAGS         = 0x2
 	CTRL_ATTR_MCAST_GRP_UNSPEC = 0x0
 	CTRL_ATTR_MCAST_GRP_NAME   = 0x1
 	CTRL_ATTR_MCAST_GRP_ID     = 0x2
+	CTRL_ATTR_POLICY_UNSPEC    = 0x0
+	CTRL_ATTR_POLICY_DO        = 0x1
+	CTRL_ATTR_POLICY_DUMP      = 0x2
+	CTRL_ATTR_POLICY_DUMP_MAX  = 0x2
 )
 
 const (
@@ -1136,7 +1144,8 @@ const (
 	PERF_RECORD_BPF_EVENT                 = 0x12
 	PERF_RECORD_CGROUP                    = 0x13
 	PERF_RECORD_TEXT_POKE                 = 0x14
-	PERF_RECORD_MAX                       = 0x15
+	PERF_RECORD_AUX_OUTPUT_HW_ID          = 0x15
+	PERF_RECORD_MAX                       = 0x16
 	PERF_RECORD_KSYMBOL_TYPE_UNKNOWN      = 0x0
 	PERF_RECORD_KSYMBOL_TYPE_BPF          = 0x1
 	PERF_RECORD_KSYMBOL_TYPE_OOL          = 0x2
@@ -1776,7 +1785,8 @@ const (
 
 const (
 	NF_NETDEV_INGRESS  = 0x0
-	NF_NETDEV_NUMHOOKS = 0x1
+	NF_NETDEV_EGRESS   = 0x1
+	NF_NETDEV_NUMHOOKS = 0x2
 )
 
 const (
@@ -3158,7 +3168,13 @@ const (
 	DEVLINK_ATTR_RELOAD_ACTION_INFO                    = 0xa2
 	DEVLINK_ATTR_RELOAD_ACTION_STATS                   = 0xa3
 	DEVLINK_ATTR_PORT_PCI_SF_NUMBER                    = 0xa4
-	DEVLINK_ATTR_MAX                                   = 0xa9
+	DEVLINK_ATTR_RATE_TYPE                             = 0xa5
+	DEVLINK_ATTR_RATE_TX_SHARE                         = 0xa6
+	DEVLINK_ATTR_RATE_TX_MAX                           = 0xa7
+	DEVLINK_ATTR_RATE_NODE_NAME                        = 0xa8
+	DEVLINK_ATTR_RATE_PARENT_NODE_NAME                 = 0xa9
+	DEVLINK_ATTR_REGION_MAX_SNAPSHOTS                  = 0xaa
+	DEVLINK_ATTR_MAX                                   = 0xaa
 	DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE              = 0x0
 	DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX           = 0x1
 	DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT               = 0x0
@@ -3455,7 +3471,14 @@ const (
 	ETHTOOL_MSG_CABLE_TEST_ACT                = 0x1a
 	ETHTOOL_MSG_CABLE_TEST_TDR_ACT            = 0x1b
 	ETHTOOL_MSG_TUNNEL_INFO_GET               = 0x1c
-	ETHTOOL_MSG_USER_MAX                      = 0x21
+	ETHTOOL_MSG_FEC_GET                       = 0x1d
+	ETHTOOL_MSG_FEC_SET                       = 0x1e
+	ETHTOOL_MSG_MODULE_EEPROM_GET             = 0x1f
+	ETHTOOL_MSG_STATS_GET                     = 0x20
+	ETHTOOL_MSG_PHC_VCLOCKS_GET               = 0x21
+	ETHTOOL_MSG_MODULE_GET                    = 0x22
+	ETHTOOL_MSG_MODULE_SET                    = 0x23
+	ETHTOOL_MSG_USER_MAX                      = 0x23
 	ETHTOOL_MSG_KERNEL_NONE                   = 0x0
 	ETHTOOL_MSG_STRSET_GET_REPLY              = 0x1
 	ETHTOOL_MSG_LINKINFO_GET_REPLY            = 0x2
@@ -3486,7 +3509,14 @@ const (
 	ETHTOOL_MSG_CABLE_TEST_NTF                = 0x1b
 	ETHTOOL_MSG_CABLE_TEST_TDR_NTF            = 0x1c
 	ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY         = 0x1d
-	ETHTOOL_MSG_KERNEL_MAX                    = 0x22
+	ETHTOOL_MSG_FEC_GET_REPLY                 = 0x1e
+	ETHTOOL_MSG_FEC_NTF                       = 0x1f
+	ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY       = 0x20
+	ETHTOOL_MSG_STATS_GET_REPLY               = 0x21
+	ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY         = 0x22
+	ETHTOOL_MSG_MODULE_GET_REPLY              = 0x23
+	ETHTOOL_MSG_MODULE_NTF                    = 0x24
+	ETHTOOL_MSG_KERNEL_MAX                    = 0x24
 	ETHTOOL_A_HEADER_UNSPEC                   = 0x0
 	ETHTOOL_A_HEADER_DEV_INDEX                = 0x1
 	ETHTOOL_A_HEADER_DEV_NAME                 = 0x2
@@ -3968,3 +3998,70 @@ type MountAttr struct {
 	Propagation uint64
 	Userns_fd   uint64
 }
+
+const (
+	WG_CMD_GET_DEVICE                      = 0x0
+	WG_CMD_SET_DEVICE                      = 0x1
+	WGDEVICE_F_REPLACE_PEERS               = 0x1
+	WGDEVICE_A_UNSPEC                      = 0x0
+	WGDEVICE_A_IFINDEX                     = 0x1
+	WGDEVICE_A_IFNAME                      = 0x2
+	WGDEVICE_A_PRIVATE_KEY                 = 0x3
+	WGDEVICE_A_PUBLIC_KEY                  = 0x4
+	WGDEVICE_A_FLAGS                       = 0x5
+	WGDEVICE_A_LISTEN_PORT                 = 0x6
+	WGDEVICE_A_FWMARK                      = 0x7
+	WGDEVICE_A_PEERS                       = 0x8
+	WGPEER_F_REMOVE_ME                     = 0x1
+	WGPEER_F_REPLACE_ALLOWEDIPS            = 0x2
+	WGPEER_F_UPDATE_ONLY                   = 0x4
+	WGPEER_A_UNSPEC                        = 0x0
+	WGPEER_A_PUBLIC_KEY                    = 0x1
+	WGPEER_A_PRESHARED_KEY                 = 0x2
+	WGPEER_A_FLAGS                         = 0x3
+	WGPEER_A_ENDPOINT                      = 0x4
+	WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL = 0x5
+	WGPEER_A_LAST_HANDSHAKE_TIME           = 0x6
+	WGPEER_A_RX_BYTES                      = 0x7
+	WGPEER_A_TX_BYTES                      = 0x8
+	WGPEER_A_ALLOWEDIPS                    = 0x9
+	WGPEER_A_PROTOCOL_VERSION              = 0xa
+	WGALLOWEDIP_A_UNSPEC                   = 0x0
+	WGALLOWEDIP_A_FAMILY                   = 0x1
+	WGALLOWEDIP_A_IPADDR                   = 0x2
+	WGALLOWEDIP_A_CIDR_MASK                = 0x3
+)
+
+const (
+	NL_ATTR_TYPE_INVALID      = 0x0
+	NL_ATTR_TYPE_FLAG         = 0x1
+	NL_ATTR_TYPE_U8           = 0x2
+	NL_ATTR_TYPE_U16          = 0x3
+	NL_ATTR_TYPE_U32          = 0x4
+	NL_ATTR_TYPE_U64          = 0x5
+	NL_ATTR_TYPE_S8           = 0x6
+	NL_ATTR_TYPE_S16          = 0x7
+	NL_ATTR_TYPE_S32          = 0x8
+	NL_ATTR_TYPE_S64          = 0x9
+	NL_ATTR_TYPE_BINARY       = 0xa
+	NL_ATTR_TYPE_STRING       = 0xb
+	NL_ATTR_TYPE_NUL_STRING   = 0xc
+	NL_ATTR_TYPE_NESTED       = 0xd
+	NL_ATTR_TYPE_NESTED_ARRAY = 0xe
+	NL_ATTR_TYPE_BITFIELD32   = 0xf
+
+	NL_POLICY_TYPE_ATTR_UNSPEC          = 0x0
+	NL_POLICY_TYPE_ATTR_TYPE            = 0x1
+	NL_POLICY_TYPE_ATTR_MIN_VALUE_S     = 0x2
+	NL_POLICY_TYPE_ATTR_MAX_VALUE_S     = 0x3
+	NL_POLICY_TYPE_ATTR_MIN_VALUE_U     = 0x4
+	NL_POLICY_TYPE_ATTR_MAX_VALUE_U     = 0x5
+	NL_POLICY_TYPE_ATTR_MIN_LENGTH      = 0x6
+	NL_POLICY_TYPE_ATTR_MAX_LENGTH      = 0x7
+	NL_POLICY_TYPE_ATTR_POLICY_IDX      = 0x8
+	NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE  = 0x9
+	NL_POLICY_TYPE_ATTR_BITFIELD32_MASK = 0xa
+	NL_POLICY_TYPE_ATTR_PAD             = 0xb
+	NL_POLICY_TYPE_ATTR_MASK            = 0xc
+	NL_POLICY_TYPE_ATTR_MAX             = 0xc
+)
diff --git a/vendor/golang.org/x/sys/windows/exec_windows.go b/vendor/golang.org/x/sys/windows/exec_windows.go
index 7a11e83b..855698bb 100644
--- a/vendor/golang.org/x/sys/windows/exec_windows.go
+++ b/vendor/golang.org/x/sys/windows/exec_windows.go
@@ -9,8 +9,6 @@ package windows
 import (
 	errorspkg "errors"
 	"unsafe"
-
-	"golang.org/x/sys/internal/unsafeheader"
 )
 
 // EscapeArg rewrites command line argument s as prescribed
@@ -147,8 +145,12 @@ func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeListCo
 		}
 		return nil, err
 	}
+	alloc, err := LocalAlloc(LMEM_FIXED, uint32(size))
+	if err != nil {
+		return nil, err
+	}
 	// size is guaranteed to be ≥1 by InitializeProcThreadAttributeList.
-	al := &ProcThreadAttributeListContainer{data: (*ProcThreadAttributeList)(unsafe.Pointer(&make([]byte, size)[0]))}
+	al := &ProcThreadAttributeListContainer{data: (*ProcThreadAttributeList)(unsafe.Pointer(alloc))}
 	err = initializeProcThreadAttributeList(al.data, maxAttrCount, 0, &size)
 	if err != nil {
 		return nil, err
@@ -157,36 +159,17 @@ func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeListCo
 }
 
 // Update modifies the ProcThreadAttributeList using UpdateProcThreadAttribute.
-// Note that the value passed to this function will be copied into memory
-// allocated by LocalAlloc, the contents of which should not contain any
-// Go-managed pointers, even if the passed value itself is a Go-managed
-// pointer.
 func (al *ProcThreadAttributeListContainer) Update(attribute uintptr, value unsafe.Pointer, size uintptr) error {
-	alloc, err := LocalAlloc(LMEM_FIXED, uint32(size))
-	if err != nil {
-		return err
-	}
-	var src, dst []byte
-	hdr := (*unsafeheader.Slice)(unsafe.Pointer(&src))
-	hdr.Data = value
-	hdr.Cap = int(size)
-	hdr.Len = int(size)
-	hdr = (*unsafeheader.Slice)(unsafe.Pointer(&dst))
-	hdr.Data = unsafe.Pointer(alloc)
-	hdr.Cap = int(size)
-	hdr.Len = int(size)
-	copy(dst, src)
-	al.heapAllocations = append(al.heapAllocations, alloc)
-	return updateProcThreadAttribute(al.data, 0, attribute, unsafe.Pointer(alloc), size, nil, nil)
+	al.pointers = append(al.pointers, value)
+	return updateProcThreadAttribute(al.data, 0, attribute, value, size, nil, nil)
 }
 
 // Delete frees ProcThreadAttributeList's resources.
 func (al *ProcThreadAttributeListContainer) Delete() {
 	deleteProcThreadAttributeList(al.data)
-	for i := range al.heapAllocations {
-		LocalFree(Handle(al.heapAllocations[i]))
-	}
-	al.heapAllocations = nil
+	LocalFree(Handle(unsafe.Pointer(al.data)))
+	al.data = nil
+	al.pointers = nil
 }
 
 // List returns the actual ProcThreadAttributeList to be passed to StartupInfoEx.
diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go
index 7702bbb8..200b62a0 100644
--- a/vendor/golang.org/x/sys/windows/syscall_windows.go
+++ b/vendor/golang.org/x/sys/windows/syscall_windows.go
@@ -323,6 +323,8 @@ func NewCallbackCDecl(fn interface{}) uintptr {
 //sys	WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) = kernel32.WriteConsoleW
 //sys	ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) = kernel32.ReadConsoleW
 //sys	CreateToolhelp32Snapshot(flags uint32, processId uint32) (handle Handle, err error) [failretval==InvalidHandle] = kernel32.CreateToolhelp32Snapshot
+//sys	Module32First(snapshot Handle, moduleEntry *ModuleEntry32) (err error) = kernel32.Module32FirstW
+//sys	Module32Next(snapshot Handle, moduleEntry *ModuleEntry32) (err error) = kernel32.Module32NextW
 //sys	Process32First(snapshot Handle, procEntry *ProcessEntry32) (err error) = kernel32.Process32FirstW
 //sys	Process32Next(snapshot Handle, procEntry *ProcessEntry32) (err error) = kernel32.Process32NextW
 //sys	Thread32First(snapshot Handle, threadEntry *ThreadEntry32) (err error)
diff --git a/vendor/golang.org/x/sys/windows/types_windows.go b/vendor/golang.org/x/sys/windows/types_windows.go
index 286dd1ea..bb31abda 100644
--- a/vendor/golang.org/x/sys/windows/types_windows.go
+++ b/vendor/golang.org/x/sys/windows/types_windows.go
@@ -156,6 +156,8 @@ const (
 	MAX_PATH      = 260
 	MAX_LONG_PATH = 32768
 
+	MAX_MODULE_NAME32 = 255
+
 	MAX_COMPUTERNAME_LENGTH = 15
 
 	TIME_ZONE_ID_UNKNOWN  = 0
@@ -936,8 +938,8 @@ type StartupInfoEx struct {
 type ProcThreadAttributeList struct{}
 
 type ProcThreadAttributeListContainer struct {
-	data            *ProcThreadAttributeList
-	heapAllocations []uintptr
+	data     *ProcThreadAttributeList
+	pointers []unsafe.Pointer
 }
 
 type ProcessInformation struct {
@@ -970,6 +972,21 @@ type ThreadEntry32 struct {
 	Flags          uint32
 }
 
+type ModuleEntry32 struct {
+	Size         uint32
+	ModuleID     uint32
+	ProcessID    uint32
+	GlblcntUsage uint32
+	ProccntUsage uint32
+	ModBaseAddr  uintptr
+	ModBaseSize  uint32
+	ModuleHandle Handle
+	Module       [MAX_MODULE_NAME32 + 1]uint16
+	ExePath      [MAX_PATH]uint16
+}
+
+const SizeofModuleEntry32 = unsafe.Sizeof(ModuleEntry32{})
+
 type Systemtime struct {
 	Year         uint16
 	Month        uint16
@@ -2732,6 +2749,43 @@ type PROCESS_BASIC_INFORMATION struct {
 	InheritedFromUniqueProcessId uintptr
 }
 
+type SYSTEM_PROCESS_INFORMATION struct {
+	NextEntryOffset              uint32
+	NumberOfThreads              uint32
+	WorkingSetPrivateSize        int64
+	HardFaultCount               uint32
+	NumberOfThreadsHighWatermark uint32
+	CycleTime                    uint64
+	CreateTime                   int64
+	UserTime                     int64
+	KernelTime                   int64
+	ImageName                    NTUnicodeString
+	BasePriority                 int32
+	UniqueProcessID              uintptr
+	InheritedFromUniqueProcessID uintptr
+	HandleCount                  uint32
+	SessionID                    uint32
+	UniqueProcessKey             *uint32
+	PeakVirtualSize              uintptr
+	VirtualSize                  uintptr
+	PageFaultCount               uint32
+	PeakWorkingSetSize           uintptr
+	WorkingSetSize               uintptr
+	QuotaPeakPagedPoolUsage      uintptr
+	QuotaPagedPoolUsage          uintptr
+	QuotaPeakNonPagedPoolUsage   uintptr
+	QuotaNonPagedPoolUsage       uintptr
+	PagefileUsage                uintptr
+	PeakPagefileUsage            uintptr
+	PrivatePageCount             uintptr
+	ReadOperationCount           int64
+	WriteOperationCount          int64
+	OtherOperationCount          int64
+	ReadTransferCount            int64
+	WriteTransferCount           int64
+	OtherTransferCount           int64
+}
+
 // SystemInformationClasses for NtQuerySystemInformation and NtSetSystemInformation
 const (
 	SystemBasicInformation = iota
diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go
index bf0dd8fc..1055d47e 100644
--- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go
+++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go
@@ -295,6 +295,8 @@ var (
 	procLockFileEx                                           = modkernel32.NewProc("LockFileEx")
 	procLockResource                                         = modkernel32.NewProc("LockResource")
 	procMapViewOfFile                                        = modkernel32.NewProc("MapViewOfFile")
+	procModule32FirstW                                       = modkernel32.NewProc("Module32FirstW")
+	procModule32NextW                                        = modkernel32.NewProc("Module32NextW")
 	procMoveFileExW                                          = modkernel32.NewProc("MoveFileExW")
 	procMoveFileW                                            = modkernel32.NewProc("MoveFileW")
 	procMultiByteToWideChar                                  = modkernel32.NewProc("MultiByteToWideChar")
@@ -2567,6 +2569,22 @@ func MapViewOfFile(handle Handle, access uint32, offsetHigh uint32, offsetLow ui
 	return
 }
 
+func Module32First(snapshot Handle, moduleEntry *ModuleEntry32) (err error) {
+	r1, _, e1 := syscall.Syscall(procModule32FirstW.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(moduleEntry)), 0)
+	if r1 == 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+func Module32Next(snapshot Handle, moduleEntry *ModuleEntry32) (err error) {
+	r1, _, e1 := syscall.Syscall(procModule32NextW.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(moduleEntry)), 0)
+	if r1 == 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
 func MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) {
 	r1, _, e1 := syscall.Syscall(procMoveFileExW.Addr(), 3, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), uintptr(flags))
 	if r1 == 0 {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index cf74a1f0..bf4c4a46 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -46,6 +46,7 @@ github.com/envoyproxy/go-control-plane/envoy/config/route/v3
 github.com/envoyproxy/go-control-plane/envoy/config/trace/v3
 github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3
 github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3
+github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/proxy_protocol/v3
 github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3
 github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3
 github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3
@@ -163,6 +164,9 @@ github.com/openzipkin/zipkin-go/model
 # github.com/patrickmn/go-cache v2.1.0+incompatible
 ## explicit
 github.com/patrickmn/go-cache
+# github.com/pires/go-proxyproto v0.6.1
+## explicit
+github.com/pires/go-proxyproto
 # github.com/pkg/errors v0.9.1
 github.com/pkg/errors
 # github.com/prometheus/client_golang v1.11.0
@@ -250,7 +254,8 @@ golang.org/x/oauth2/jwt
 golang.org/x/sync/errgroup
 golang.org/x/sync/semaphore
 golang.org/x/sync/singleflight
-# golang.org/x/sys v0.0.0-20211124211545-fe61309f8881
+# golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe
+## explicit
 golang.org/x/sys/execabs
 golang.org/x/sys/internal/unsafeheader
 golang.org/x/sys/plan9


Test patch:
diff --git a/pkg/config/configmap_test.go b/pkg/config/configmap_test.go
index 49c70b8e..47ddff1d 100644
--- a/pkg/config/configmap_test.go
+++ b/pkg/config/configmap_test.go
@@ -49,6 +49,32 @@ func TestKourierConfig(t *testing.T) {
 		data: map[string]string{
 			enableServiceAccessLoggingKey: "foo",
 		},
+	}, {
+		name: "enable proxy protocol and logging",
+		want: &Kourier{
+			EnableServiceAccessLogging: true,
+			EnableProxyProtocol:        true,
+		},
+		data: map[string]string{
+			enableServiceAccessLoggingKey: "true",
+			enableProxyProtocol:           "true",
+		},
+	}, {
+		name: "enable proxy protocol and disable logging",
+		want: &Kourier{
+			EnableServiceAccessLogging: false,
+			EnableProxyProtocol:        true,
+		},
+		data: map[string]string{
+			enableServiceAccessLoggingKey: "false",
+			enableProxyProtocol:           "true",
+		},
+	}, {
+		name:    "not a bool for proxy protocol",
+		wantErr: true,
+		data: map[string]string{
+			enableProxyProtocol: "foo",
+		},
 	}}
 
 	for _, tt := range configTests {
diff --git a/pkg/envoy/api/http_connection_manager_test.go b/pkg/envoy/api/http_connection_manager_test.go
index 086107a2..dc477c10 100644
--- a/pkg/envoy/api/http_connection_manager_test.go
+++ b/pkg/envoy/api/http_connection_manager_test.go
@@ -29,13 +29,38 @@ import (
 	"gotest.tools/v3/assert"
 )
 
-func TestNewHTTPConnectionManagerWithoutAccessLog(t *testing.T) {
-	connManager := NewHTTPConnectionManager("test", false /*enableAccessLog*/)
+func TestNewHTTPConnectionManagerWithoutAccessLogWithoutProxyProtocol(t *testing.T) {
+	connManager := NewHTTPConnectionManager("test", false /*enableAccessLog*/, false /*enableProxyProtocol*/)
 	assert.Check(t, len(connManager.AccessLog) == 0)
+	assert.Check(t, connManager.UseRemoteAddress == nil)
 }
 
-func TestNewHTTPConnectionManagerWithAccessLog(t *testing.T) {
-	connManager := NewHTTPConnectionManager("test", true /*enableAccessLog*/)
+func TestNewHTTPConnectionManagerWithAccessLogWithoutProxyProtocol(t *testing.T) {
+	connManager := NewHTTPConnectionManager("test", true /*enableAccessLog*/, false /*enableProxyProtocol*/)
+	assert.Check(t, connManager.UseRemoteAddress == nil)
+	accessLog := connManager.AccessLog[0]
+	accessLogPathAny := accessLog.ConfigType.(*envoy_config_filter_accesslog_v3.AccessLog_TypedConfig).TypedConfig
+	fileAccesLog := &fileaccesslog.FileAccessLog{}
+
+	err := anypb.UnmarshalTo(accessLogPathAny, fileAccesLog, proto.UnmarshalOptions{})
+	if err != nil {
+		t.Error(err)
+	}
+
+	assert.Equal(t, "/dev/stdout", fileAccesLog.Path)
+}
+
+func TestNewHTTPConnectionManagerWithoutAccessLogWithProxyProtocol(t *testing.T) {
+	connManager := NewHTTPConnectionManager("test", false /*enableAccessLog*/, true /*enableProxyProtocol*/)
+	assert.Check(t, len(connManager.AccessLog) == 0)
+	assert.Check(t, connManager.UseRemoteAddress != nil)
+	assert.Check(t, connManager.UseRemoteAddress.Value)
+}
+
+func TestNewHTTPConnectionManagerWithAccessLogWithProxyProtocol(t *testing.T) {
+	connManager := NewHTTPConnectionManager("test", true /*enableAccessLog*/, true /*enableProxyProtocol*/)
+	assert.Check(t, connManager.UseRemoteAddress != nil)
+	assert.Check(t, connManager.UseRemoteAddress.Value)
 	accessLog := connManager.AccessLog[0]
 	accessLogPathAny := accessLog.ConfigType.(*envoy_config_filter_accesslog_v3.AccessLog_TypedConfig).TypedConfig
 	fileAccesLog := &fileaccesslog.FileAccessLog{}
diff --git a/pkg/envoy/api/listener_test.go b/pkg/envoy/api/listener_test.go
index a8d233d4..6037a8d1 100644
--- a/pkg/envoy/api/listener_test.go
+++ b/pkg/envoy/api/listener_test.go
@@ -23,6 +23,7 @@ import (
 
 	core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
 	envoy_api_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+	prx "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/proxy_protocol/v3"
 	auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
 	"github.com/envoyproxy/go-control-plane/pkg/wellknown"
 	"google.golang.org/protobuf/proto"
@@ -31,20 +32,40 @@ import (
 	is "gotest.tools/v3/assert/cmp"
 )
 
+const urlPrefix = "type.googleapis.com/"
+
 func TestNewHTTPListener(t *testing.T) {
-	manager := NewHTTPConnectionManager("test", true /*enableAccessLog*/)
+	manager := NewHTTPConnectionManager("test", true /*enableAccessLog*/, false /*enableProxyProtocol*/)
 
-	l, err := NewHTTPListener(manager, 8080)
+	l, err := NewHTTPListener(manager, 8080, false)
 	assert.NilError(t, err)
 
 	assert.Equal(t, core.SocketAddress_TCP, l.Address.GetSocketAddress().Protocol)
 	assert.Equal(t, "0.0.0.0", l.Address.GetSocketAddress().Address)
 	assert.Equal(t, uint32(8080), l.Address.GetSocketAddress().GetPortValue())
 	assert.Assert(t, is.Nil(l.FilterChains[0].TransportSocket)) // TLS not configured
+
+	// check proxy protocol is not configured
+	assert.Check(t, len(l.ListenerFilters) == 0)
+}
+
+func TestNewHTTPListenerWithProxyProtocol(t *testing.T) {
+	manager := NewHTTPConnectionManager("test", true /*enableAccessLog*/, true /*enableProxyProtocol*/)
+
+	l, err := NewHTTPListener(manager, 8080, true)
+	assert.NilError(t, err)
+
+	assert.Equal(t, core.SocketAddress_TCP, l.Address.GetSocketAddress().Protocol)
+	assert.Equal(t, "0.0.0.0", l.Address.GetSocketAddress().Address)
+	assert.Equal(t, uint32(8080), l.Address.GetSocketAddress().GetPortValue())
+	assert.Assert(t, is.Nil(l.FilterChains[0].TransportSocket)) // TLS not configured
+
+	// check proxy protocol is configured
+	assertListenerHasProxyProtocolConfigured(t, l.ListenerFilters[0])
 }
 
 func TestNewHTTPSListener(t *testing.T) {
-	manager := NewHTTPConnectionManager("test", true /*enableAccessLog*/)
+	manager := NewHTTPConnectionManager("test", true /*enableAccessLog*/, false /*enableProxyProtocol*/)
 
 	certChain := []byte("some_certificate_chain")
 	privateKey := []byte("some_private_key")
@@ -52,7 +73,7 @@ func TestNewHTTPSListener(t *testing.T) {
 	filterChain, err := CreateFilterChainFromCertificateAndPrivateKey(manager, certChain, privateKey)
 	assert.NilError(t, err)
 
-	l, err := NewHTTPSListener(8081, filterChain)
+	l, err := NewHTTPSListener(8081, []*envoy_api_v3.FilterChain{filterChain}, false)
 	assert.NilError(t, err)
 
 	assert.Equal(t, core.SocketAddress_TCP, l.Address.GetSocketAddress().Protocol)
@@ -65,6 +86,36 @@ func TestNewHTTPSListener(t *testing.T) {
 
 	assert.DeepEqual(t, certChain, gotCertChain)
 	assert.DeepEqual(t, privateKey, gotPrivateKey)
+
+	// check proxy protocol is not configured
+	assert.Check(t, len(l.ListenerFilters) == 0)
+}
+
+func TestNewHTTPSListenerWithProxyProtocol(t *testing.T) {
+	manager := NewHTTPConnectionManager("test", true /*enableAccessLog*/, true /*enableProxyProtocol*/)
+
+	certChain := []byte("some_certificate_chain")
+	privateKey := []byte("some_private_key")
+
+	filterChain, err := CreateFilterChainFromCertificateAndPrivateKey(manager, certChain, privateKey)
+	assert.NilError(t, err)
+
+	l, err := NewHTTPSListener(8081, []*envoy_api_v3.FilterChain{filterChain}, true)
+	assert.NilError(t, err)
+
+	assert.Equal(t, core.SocketAddress_TCP, l.Address.GetSocketAddress().Protocol)
+	assert.Equal(t, "0.0.0.0", l.Address.GetSocketAddress().Address)
+	assert.Equal(t, uint32(8081), l.Address.GetSocketAddress().GetPortValue())
+
+	// Check that TLS is configured
+	gotCertChain, gotPrivateKey, err := getTLSCreds(l.FilterChains[0])
+	assert.NilError(t, err)
+
+	assert.DeepEqual(t, certChain, gotCertChain)
+	assert.DeepEqual(t, privateKey, gotPrivateKey)
+
+	// check proxy protocol is configured
+	assertListenerHasProxyProtocolConfigured(t, l.ListenerFilters[0])
 }
 
 func TestNewHTTPSListenerWithSNI(t *testing.T) {
@@ -78,8 +129,8 @@ func TestNewHTTPSListenerWithSNI(t *testing.T) {
 		PrivateKey:       []byte("key2"),
 	}}
 
-	manager := NewHTTPConnectionManager("test", true /*enableAccessLog*/)
-	listener, err := NewHTTPSListenerWithSNI(manager, 8443, sniMatches)
+	manager := NewHTTPConnectionManager("test", true /*enableAccessLog*/, false /*enableProxyProtocol*/)
+	listener, err := NewHTTPSListenerWithSNI(manager, 8443, sniMatches, false)
 	assert.NilError(t, err)
 
 	assert.Equal(t, core.SocketAddress_TCP, listener.Address.GetSocketAddress().Protocol)
@@ -87,12 +138,41 @@ func TestNewHTTPSListenerWithSNI(t *testing.T) {
 	assert.Equal(t, uint32(8443), listener.Address.GetSocketAddress().GetPortValue())
 
 	// Listener Filter required for SNI
+	// we should only have one listener filter when proxy protocol is disabled
+	assert.Check(t, listener.ListenerFilters[0].Name != wellknown.ProxyProtocol)
 	assert.Equal(t, listener.ListenerFilters[0].Name, wellknown.TlsInspector)
 
 	assertListenerHasSNIMatchConfigured(t, listener, sniMatches[0])
 	assertListenerHasSNIMatchConfigured(t, listener, sniMatches[1])
 }
 
+func TestNewHTTPSListenerWithSNIWithProxyProtocol(t *testing.T) {
+	sniMatches := []*SNIMatch{{
+		Hosts:            []string{"some_host.com"},
+		CertificateChain: []byte("cert1"),
+		PrivateKey:       []byte("key1"),
+	}, {
+		Hosts:            []string{"another_host.com"},
+		CertificateChain: []byte("cert2"),
+		PrivateKey:       []byte("key2"),
+	}}
+
+	manager := NewHTTPConnectionManager("test", true /*enableAccessLog*/, true /*enableProxyProtocol*/)
+	listener, err := NewHTTPSListenerWithSNI(manager, 8443, sniMatches, true)
+	assert.NilError(t, err)
+
+	assert.Equal(t, core.SocketAddress_TCP, listener.Address.GetSocketAddress().Protocol)
+	assert.Equal(t, "0.0.0.0", listener.Address.GetSocketAddress().Address)
+	assert.Equal(t, uint32(8443), listener.Address.GetSocketAddress().GetPortValue())
+
+	// check both tls inspector and proxy protocol are configured
+	assert.Equal(t, listener.ListenerFilters[0].Name, wellknown.TlsInspector)
+	assertListenerHasProxyProtocolConfigured(t, listener.ListenerFilters[1])
+
+	assertListenerHasSNIMatchConfigured(t, listener, sniMatches[0])
+	assertListenerHasSNIMatchConfigured(t, listener, sniMatches[1])
+}
+
 func assertListenerHasSNIMatchConfigured(t *testing.T, listener *envoy_api_v3.Listener, match *SNIMatch) {
 	filterChainFirstSNIMatch := getFilterChainByServerName(listener, match.Hosts)
 	assert.Assert(t, filterChainFirstSNIMatch != nil)
@@ -103,6 +183,12 @@ func assertListenerHasSNIMatchConfigured(t *testing.T, listener *envoy_api_v3.Li
 	assert.DeepEqual(t, match.PrivateKey, privateKey)
 }
 
+func assertListenerHasProxyProtocolConfigured(t *testing.T, listenerFilter *envoy_api_v3.ListenerFilter) {
+	typeURL := urlPrefix + string((&prx.ProxyProtocol{}).ProtoReflect().Descriptor().FullName())
+	assert.Equal(t, wellknown.ProxyProtocol, listenerFilter.GetName())
+	assert.Equal(t, typeURL, listenerFilter.GetTypedConfig().GetTypeUrl())
+}
+
 func getFilterChainByServerName(listener *envoy_api_v3.Listener, serverNames []string) *envoy_api_v3.FilterChain {
 	for _, filterChain := range listener.FilterChains {
 		filterChainMatch := filterChain.GetFilterChainMatch()
diff --git a/test/e2e-kind.sh b/test/e2e-kind.sh
index a7610896..82301a4b 100755
--- a/test/e2e-kind.sh
+++ b/test/e2e-kind.sh
@@ -75,3 +75,18 @@ go test -race -count=1 -timeout=20m -tags=e2e ./test/extauthz/... \
   --ingressendpoint="${IPS[0]}" \
   --ingressClass=kourier.ingress.networking.knative.dev \
   --cluster-suffix="$CLUSTER_SUFFIX"
+
+echo ">> Unset ExtAuthz"
+kubectl -n "${KOURIER_CONTROL_NAMESPACE}" set env deployment net-kourier-controller KOURIER_EXTAUTHZ_HOST-
+kubectl -n "${KOURIER_CONTROL_NAMESPACE}" rollout status deployment/net-kourier-controller
+
+echo ">> Setup Proxy Protocol"
+kubectl -n "${KOURIER_CONTROL_NAMESPACE}" patch configmap/config-kourier --type merge -p '{"data":{"enable-proxy-protocol":"true"}}'
+kubectl -n "${KOURIER_CONTROL_NAMESPACE}" rollout restart deployment/net-kourier-controller
+kubectl -n "${KOURIER_CONTROL_NAMESPACE}" rollout status deployment/net-kourier-controller --timeout=300s
+
+echo ">> Running Proxy Protocol tests"
+go test -race -count=1 -timeout=5m -tags=e2e ./test/proxyprotocol/... \
+  --ingressendpoint="${IPS[0]}" \
+  --ingressClass=kourier.ingress.networking.knative.dev \
+  --cluster-suffix="$CLUSTER_SUFFIX"
diff --git a/test/proxyprotocol/proxyprotocol_test.go b/test/proxyprotocol/proxyprotocol_test.go
new file mode 100644
index 00000000..46467685
--- /dev/null
+++ b/test/proxyprotocol/proxyprotocol_test.go
@@ -0,0 +1,199 @@
+//go:build e2e
+// +build e2e
+
+/*
+Copyright 2020 The Knative Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package proxyprotocol
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net"
+	"net/http"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/pires/go-proxyproto"
+	"gotest.tools/v3/assert"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/intstr"
+	"k8s.io/apimachinery/pkg/util/wait"
+	"knative.dev/networking/pkg/apis/networking"
+	"knative.dev/networking/pkg/apis/networking/v1alpha1"
+	"knative.dev/networking/test"
+	"knative.dev/networking/test/conformance/ingress"
+	"knative.dev/pkg/network"
+	"knative.dev/pkg/reconciler"
+	pkgTest "knative.dev/pkg/test"
+)
+
+// TestProxyProtocol verifies that the kourier is configured with proxy protocol.
+func TestProxyProtocol(t *testing.T) {
+	ctx, clients := context.Background(), test.Setup(t)
+
+	name, port, _ := ingress.CreateRuntimeService(ctx, t, clients, networking.ServicePortNameHTTP1)
+
+	ing, client, _ := ingress.CreateIngressReady(ctx, t, clients, v1alpha1.IngressSpec{
+		Rules: []v1alpha1.IngressRule{{
+			Hosts:      []string{name + ".example.com"},
+			Visibility: v1alpha1.IngressVisibilityExternalIP,
+			HTTP: &v1alpha1.HTTPIngressRuleValue{
+				Paths: []v1alpha1.HTTPIngressPath{{
+					Splits: []v1alpha1.IngressBackendSplit{{
+						IngressBackend: v1alpha1.IngressBackend{
+							ServiceName:      name,
+							ServiceNamespace: test.ServingNamespace,
+							ServicePort:      intstr.FromInt(port),
+						},
+					}},
+				}},
+			},
+		}},
+	})
+
+	// testing without proxy protocol headers
+	req, err := http.NewRequest("GET", "http://"+name+".example.com", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	resp, err := client.Do(req)
+	assert.Check(t, resp == nil)
+	assert.Check(t, err != nil)
+
+	// testing with proxy protocol
+	client = &http.Client{
+		Transport: &http.Transport{
+			DialContext: createDialContextProxyProtocol(ctx, t, ing, clients),
+		},
+	}
+
+	resp, err = client.Do(req)
+	assert.Check(t, err == nil)
+	defer resp.Body.Close()
+	assert.Equal(t, resp.StatusCode, http.StatusOK)
+}
+
+// createDialContextProxyProtocol - create a custom dialer
+// it's the same function defined in knative test ingress package, adding to it proxy protocol headers
+func createDialContextProxyProtocol(ctx context.Context, t *testing.T, ing *v1alpha1.Ingress, clients *test.Clients) func(context.Context, string, string) (net.Conn, error) {
+	t.Helper()
+	if ing.Status.PublicLoadBalancer == nil || len(ing.Status.PublicLoadBalancer.Ingress) < 1 {
+		t.Fatal("Ingress does not have a public load balancer assigned.")
+	}
+
+	// Create a proxy protocol header
+	headerProxyProto := &proxyproto.Header{
+		Version:           2,
+		Command:           proxyproto.PROXY,
+		TransportProtocol: proxyproto.TCPv4,
+		SourceAddr: &net.TCPAddr{
+			IP:   net.ParseIP("10.1.1.1"),
+			Port: 1000,
+		},
+		DestinationAddr: &net.TCPAddr{
+			IP:   net.ParseIP("20.2.2.2"),
+			Port: 2000,
+		},
+	}
+
+	// TODO(mattmoor): I'm open to tricks that would let us cleanly test multiple
+	// public load balancers or LBs with multiple ingresses (below), but want to
+	// keep our simple tests simple, thus the [0]s...
+
+	// We expect an ingress LB with the form foo.bar.svc.cluster.local (though
+	// we aren't strictly sensitive to the suffix, this is just illustrative.
+	internalDomain := ing.Status.PublicLoadBalancer.Ingress[0].DomainInternal
+	parts := strings.SplitN(internalDomain, ".", 3)
+	if len(parts) < 3 {
+		t.Fatal("Too few parts in internal domain:", internalDomain)
+	}
+	name, namespace := parts[0], parts[1]
+
+	var svc *corev1.Service
+	err := reconciler.RetryTestErrors(func(attempts int) (err error) {
+		svc, err = clients.KubeClient.CoreV1().Services(namespace).Get(ctx, name, metav1.GetOptions{})
+		return err
+	})
+	if err != nil {
+		t.Fatalf("Unable to retrieve Kubernetes service %s/%s: %v", namespace, name, err)
+	}
+
+	var dialBackoff = wait.Backoff{
+		Duration: 50 * time.Millisecond,
+		Factor:   1.4,
+		Jitter:   0.1, // At most 10% jitter.
+		Steps:    100,
+		Cap:      10 * time.Second,
+	}
+
+	dial := network.NewBackoffDialer(dialBackoff)
+	if pkgTest.Flags.IngressEndpoint != "" {
+		t.Logf("ingressendpoint: %q", pkgTest.Flags.IngressEndpoint)
+
+		// If we're using a manual --ingressendpoint then don't require
+		// "type: LoadBalancer", which may not play nice with KinD
+		return func(ctx context.Context, _ string, address string) (net.Conn, error) {
+			_, port, err := net.SplitHostPort(address)
+			if err != nil {
+				return nil, err
+			}
+			for _, sp := range svc.Spec.Ports {
+				if fmt.Sprint(sp.Port) == port {
+					conn, err := dial(ctx, "tcp", fmt.Sprintf("%s:%d", pkgTest.Flags.IngressEndpoint, sp.NodePort))
+					if err != nil {
+						return nil, err
+					}
+					_, err = headerProxyProto.WriteTo(conn)
+					return conn, err
+				}
+			}
+			return nil, fmt.Errorf("service doesn't contain a matching port: %s", port)
+		}
+	} else if len(svc.Status.LoadBalancer.Ingress) >= 1 {
+		ingress := svc.Status.LoadBalancer.Ingress[0]
+		return func(ctx context.Context, _ string, address string) (net.Conn, error) {
+			_, port, err := net.SplitHostPort(address)
+			if err != nil {
+				return nil, err
+			}
+			if ingress.IP != "" {
+				conn, err := dial(ctx, "tcp", ingress.IP+":"+port)
+				if err != nil {
+					return nil, err
+				}
+				_, err = headerProxyProto.WriteTo(conn)
+				return conn, err
+			}
+			if ingress.Hostname != "" {
+				conn, err := dial(ctx, "tcp", ingress.Hostname+":"+port)
+				if err != nil {
+					return nil, err
+				}
+				_, err = headerProxyProto.WriteTo(conn)
+				return conn, err
+			}
+			return nil, errors.New("service ingress does not contain dialing information")
+		}
+	} else {
+		t.Fatal("Service does not have a supported shape (not type LoadBalancer? missing --ingressendpoint?).")
+		return nil // Unreachable
+	}
+}