Merge branch 'caddy2' into v2multiauth
This commit is contained in:
commit
a29f011386
20 changed files with 1071 additions and 698 deletions
14
.github/dependabot.yml
vendored
Normal file
14
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "gomod"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
- package-ecosystem: "docker"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
33
.github/workflows/go-critic.yml
vendored
Normal file
33
.github/workflows/go-critic.yml
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
name: Gocritic Scan
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "master"
|
||||||
|
- "main"
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
gocritic-scan:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO111MODULE: on
|
||||||
|
steps:
|
||||||
|
- name: Fetch Repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: 'stable'
|
||||||
|
check-latest: true
|
||||||
|
cache: false
|
||||||
|
- name: Install go-critic
|
||||||
|
run: go install -v github.com/go-critic/go-critic/cmd/gocritic@latest
|
||||||
|
- name: Run gocritic
|
||||||
|
run: gocritic check -checkTests=False ./...
|
||||||
25
.github/workflows/golangci-lint.yml
vendored
Normal file
25
.github/workflows/golangci-lint.yml
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
name: Golangci Lint Check
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "master"
|
||||||
|
- "main"
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
golangci-lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Fetch Repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Run golangci-lint
|
||||||
|
uses: reviewdog/action-golangci-lint@v2
|
||||||
|
with:
|
||||||
|
golangci_lint_flags: "--tests=false"
|
||||||
33
.github/workflows/gosec.yml
vendored
Normal file
33
.github/workflows/gosec.yml
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
name: Gosec Security Scan
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "master"
|
||||||
|
- "main"
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
gosec-scan:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO111MODULE: on
|
||||||
|
steps:
|
||||||
|
- name: Fetch Repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: 'stable'
|
||||||
|
check-latest: true
|
||||||
|
cache: false
|
||||||
|
- name: Install Gosec
|
||||||
|
run: go install github.com/securego/gosec/v2/cmd/gosec@latest
|
||||||
|
- name: Run Gosec
|
||||||
|
run: gosec ./...
|
||||||
33
.github/workflows/govulncheck.yml
vendored
Normal file
33
.github/workflows/govulncheck.yml
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
name: Govulncheck Security Scan
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "master"
|
||||||
|
- "main"
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
govulncheck-check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO111MODULE: on
|
||||||
|
steps:
|
||||||
|
- name: Fetch Repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: 'stable'
|
||||||
|
check-latest: true
|
||||||
|
cache: false
|
||||||
|
- name: Install Govulncheck
|
||||||
|
run: go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||||
|
- name: Run Govulncheck
|
||||||
|
run: govulncheck ./...
|
||||||
33
.github/workflows/staticcheck.yml
vendored
Normal file
33
.github/workflows/staticcheck.yml
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
name: Staticcheck Scan
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "master"
|
||||||
|
- "main"
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
staticcheck-scan:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO111MODULE: on
|
||||||
|
steps:
|
||||||
|
- name: Fetch Repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: 'stable'
|
||||||
|
check-latest: true
|
||||||
|
cache: false
|
||||||
|
- name: Install staticcheck
|
||||||
|
run: go install honnef.co/go/tools/cmd/staticcheck@latest
|
||||||
|
- name: Run staticcheck
|
||||||
|
run: staticcheck -tests=False ./...
|
||||||
35
.github/workflows/tests.yml
vendored
Normal file
35
.github/workflows/tests.yml
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
name: "Tests"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "master"
|
||||||
|
- "main"
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Tests:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
go-version:
|
||||||
|
- stable
|
||||||
|
platform:
|
||||||
|
- ubuntu-latest
|
||||||
|
runs-on: ${{ matrix.platform }}
|
||||||
|
steps:
|
||||||
|
- name: Fetch Repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: '${{ matrix.go-version }}'
|
||||||
|
- name: Run test
|
||||||
|
run: go test -v ./...
|
||||||
|
- name: Run test -race
|
||||||
|
run: go test -v -race ./...
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
# golangci linter currently requires a config file to
|
|
||||||
# disable checking of the test files
|
|
||||||
run:
|
|
||||||
tests: false
|
|
||||||
16
.travis.yml
16
.travis.yml
|
|
@ -1,16 +0,0 @@
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.15.x
|
|
||||||
env:
|
|
||||||
- GO111MODULE=on
|
|
||||||
|
|
||||||
dist: trusty
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go get -v -t -d ./...
|
|
||||||
- go get -v github.com/golangci/golangci-lint/cmd/golangci-lint
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go test -race -v .
|
|
||||||
- golangci-lint run -E gofmt -E goimports -E misspell -E ineffassign -E staticcheck -E gosimple -D errcheck
|
|
||||||
88
README.md
88
README.md
|
|
@ -1,7 +1,81 @@
|
||||||
# Secure forward proxy plugin for the Caddy web server
|
# Secure forward proxy for the Caddy web server
|
||||||
|
|
||||||
|
This package registers the `http.handlers.forward_proxy` module, which acts as an HTTPS proxy for accessing remote networks.
|
||||||
|
|
||||||
|
## :warning: Experimental!
|
||||||
|
|
||||||
|
This module is EXPERIMENTAL. We need more users to test this module for bugs and weaknesses before we recommend its use from within surveilled networks or regions with active censorship. Do not rely on this code in situations where personal safety, freedom, or privacy are at risk.
|
||||||
|
|
||||||
|
**You can help by:**
|
||||||
|
|
||||||
|
- Safely deploying this module
|
||||||
|
- Trying to break it
|
||||||
|
- Contributing to the code and tests in this repo to make it better
|
||||||
|
|
||||||
|
We are also seeking experienced maintainers who have experience with these kinds of technologies and who are interested in continuing its development.
|
||||||
|
|
||||||
|
**Expect breaking changes.**
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- HTTP/1.1 and HTTP/2 support
|
||||||
|
- Authentication
|
||||||
|
- Access control lists
|
||||||
|
- Optional probe resistance
|
||||||
|
- PAC file
|
||||||
|
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
This Caddy module allows you to use your web server as a proxy server, configurable by numerous HTTP clients such as operating systems, web browsers, mobile devices, and apps. However, the feature set of each client varies widely, as does their correctness and security guarantees. You will have to be aware of each clients' individual weaknesses or shortcomings.
|
||||||
|
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
First, you will have to know [how to use Caddy](https://caddyserver.com/docs/getting-started).
|
||||||
|
|
||||||
|
Build Caddy with this plugin. You can add it from [Caddy's download page](https://caddyserver.com/download) or build it yourself with [xcaddy](https://github.com/caddyserver/xcaddy):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ xcaddy build --with github.com/caddyserver/forwardproxy@caddy2
|
||||||
|
```
|
||||||
|
|
||||||
|
Most people prefer the [Caddyfile](https://caddyserver.com/docs/caddyfile) for configuration. You can stand up a simple, wide-open unauthenticated forward proxy like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
example.com
|
||||||
|
|
||||||
|
route {
|
||||||
|
# UNAUTHENTICATED! USE ONLY FOR TESTING
|
||||||
|
forward_proxy
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
(Obviously, replace `example.com` with your domain name which is pointed at your machine.)
|
||||||
|
|
||||||
|
Because `forward_proxy` is not a standard directive, its ordering relative to other handler directives is not defined, so we put it inside a `route` block. You can alternatively do something like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
order forward_proxy before file_server
|
||||||
|
}
|
||||||
|
|
||||||
|
example.com
|
||||||
|
|
||||||
|
# UNAUTHENTICATED! USE ONLY FOR TESTING
|
||||||
|
forward_proxy
|
||||||
|
```
|
||||||
|
|
||||||
|
to define its position globally; then you don't need `route` blocks. The correct order is up to you and depends on your config.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[](https://travis-ci.org/caddyserver/forwardproxy)
|
|
||||||
[](https://gitter.im/forwardproxy/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
|
|
||||||
|
|
||||||
This plugin enables [Caddy](https://caddyserver.com) to act as a forward proxy, with support for HTTP/2.0 and HTTP/1.1 requests. HTTP/2.0 will usually improve performance due to multiplexing.
|
This plugin enables [Caddy](https://caddyserver.com) to act as a forward proxy, with support for HTTP/2.0 and HTTP/1.1 requests. HTTP/2.0 will usually improve performance due to multiplexing.
|
||||||
|
|
@ -24,8 +98,8 @@ Here's an example of all properties in use (note that the syntax is subject to c
|
||||||
:443, example.com
|
:443, example.com
|
||||||
route {
|
route {
|
||||||
forward_proxy {
|
forward_proxy {
|
||||||
basicauth user1 password1
|
basic_auth user1 0NtCL2JPJBgPPMmlPcJ
|
||||||
basicauth user2 password2
|
basic_auth user2 密码
|
||||||
ports 80 443
|
ports 80 443
|
||||||
hide_ip
|
hide_ip
|
||||||
hide_via
|
hide_via
|
||||||
|
|
@ -147,9 +221,9 @@ Don't forget to add `http.forwardproxy` plugin.
|
||||||
|
|
||||||
#### Build from source
|
#### Build from source
|
||||||
|
|
||||||
0. Install latest Golang 1.12 or above and set export GO111MODULE=on
|
0. Install latest Golang 1.20 or above and set export GO111MODULE=on
|
||||||
1. ```bash
|
1. ```bash
|
||||||
go install github.com/caddyserver/forwardproxy/cmd/caddy
|
go install github.com/caddyserver/forwardproxy/cmd/caddy@latest
|
||||||
```
|
```
|
||||||
Built `caddy` binary will be stored in $GOPATH/bin.
|
Built `caddy` binary will be stored in $GOPATH/bin.
|
||||||
|
|
||||||
|
|
|
||||||
6
acl.go
6
acl.go
|
|
@ -37,7 +37,6 @@ func (a *aclIPRule) tryMatch(ip net.IP, domain string) aclDecision {
|
||||||
return aclDecisionAllow
|
return aclDecisionAllow
|
||||||
}
|
}
|
||||||
return aclDecisionDeny
|
return aclDecisionDeny
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type aclDomainRule struct {
|
type aclDomainRule struct {
|
||||||
|
|
@ -47,9 +46,8 @@ type aclDomainRule struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *aclDomainRule) tryMatch(ip net.IP, domain string) aclDecision {
|
func (a *aclDomainRule) tryMatch(ip net.IP, domain string) aclDecision {
|
||||||
if strings.HasSuffix(domain, ".") {
|
domain = strings.TrimPrefix(domain, ".")
|
||||||
domain = domain[:len(domain)-1]
|
|
||||||
}
|
|
||||||
if domain == a.domain ||
|
if domain == a.domain ||
|
||||||
a.subdomainsAllowed && strings.HasSuffix(domain, "."+a.domain) {
|
a.subdomainsAllowed && strings.HasSuffix(domain, "."+a.domain) {
|
||||||
if a.allow {
|
if a.allow {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
caddy "github.com/caddyserver/caddy/v2"
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
|
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||||
|
|
@ -147,7 +147,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
aclAllow := false
|
aclAllow := false
|
||||||
switch aclDirective {
|
switch aclDirective {
|
||||||
case "allow":
|
case "allow":
|
||||||
ruleSubjects = args[:]
|
ruleSubjects = args
|
||||||
aclAllow = true
|
aclAllow = true
|
||||||
case "allow_file":
|
case "allow_file":
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
|
|
@ -159,7 +159,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
}
|
}
|
||||||
aclAllow = true
|
aclAllow = true
|
||||||
case "deny":
|
case "deny":
|
||||||
ruleSubjects = args[:]
|
ruleSubjects = args
|
||||||
case "deny_file":
|
case "deny_file":
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return d.Err("denyfile accepts a single filename argument")
|
return d.Err("denyfile accepts a single filename argument")
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
|
|
@ -25,11 +24,12 @@ import (
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddytls"
|
"github.com/caddyserver/caddy/v2/modules/caddytls"
|
||||||
)
|
)
|
||||||
|
|
||||||
var credentialsEmpty = ""
|
var (
|
||||||
var credentialsCorrectPlain = "test:pass"
|
credentialsEmpty = ""
|
||||||
var credentialsCorrect = "Basic dGVzdDpwYXNz" // test:pass
|
credentialsCorrectPlain = "test:pass"
|
||||||
var credentialsUpstreamCorrect = "basic dXBzdHJlYW10ZXN0OnVwc3RyZWFtcGFzcw==" // upstreamtest:upstreampass
|
credentialsCorrect = "Basic dGVzdDpwYXNz" // test:pass
|
||||||
var credentialsWrong = []string{
|
credentialsUpstreamCorrect = "basic dXBzdHJlYW10ZXN0OnVwc3RyZWFtcGFzcw==" // upstreamtest:upstreampass
|
||||||
|
credentialsWrong = []string{
|
||||||
"",
|
"",
|
||||||
"\"\"",
|
"\"\"",
|
||||||
"Basic dzp3",
|
"Basic dzp3",
|
||||||
|
|
@ -37,7 +37,8 @@ var credentialsWrong = []string{
|
||||||
"Foo bar",
|
"Foo bar",
|
||||||
"Tssssssss",
|
"Tssssssss",
|
||||||
"Basic dpz3 asp",
|
"Basic dpz3 asp",
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Test naming: Test{httpVer}Proxy{Method}{Auth}{Credentials}{httpVer}
|
Test naming: Test{httpVer}Proxy{Method}{Auth}{Credentials}{httpVer}
|
||||||
|
|
@ -45,17 +46,21 @@ GET/CONNECT -- get gets, connect connects and gets
|
||||||
Auth/NoAuth
|
Auth/NoAuth
|
||||||
Empty/Correct/Wrong -- tries different credentials
|
Empty/Correct/Wrong -- tries different credentials
|
||||||
*/
|
*/
|
||||||
var testResources = []string{"/", "/pic.png"}
|
var (
|
||||||
var testHTTPProxyVersions = []string{"HTTP/2.0", "HTTP/1.1"}
|
testResources = []string{"/", "/pic.png"}
|
||||||
var testHTTPTargetVersions = []string{"HTTP/1.1"}
|
testHTTPProxyVersions = []string{"HTTP/2.0", "HTTP/1.1"}
|
||||||
var httpVersionToALPN = map[string]string{
|
testHTTPTargetVersions = []string{"HTTP/1.1"}
|
||||||
|
httpVersionToALPN = map[string]string{
|
||||||
"HTTP/1.1": "http/1.1",
|
"HTTP/1.1": "http/1.1",
|
||||||
"HTTP/2.0": "h2",
|
"HTTP/2.0": "h2",
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
var blacklistedDomain = "google-public-dns-a.google.com" // supposed to ever resolve to one of 2 IP addresses below
|
var (
|
||||||
var blacklistedIPv4 = "8.8.8.8"
|
blacklistedDomain = "google-public-dns-a.google.com" // supposed to ever resolve to one of 2 IP addresses below
|
||||||
var blacklistedIPv6 = "2001:4860:4860::8888"
|
blacklistedIPv4 = "8.8.8.8"
|
||||||
|
blacklistedIPv6 = "2001:4860:4860::8888"
|
||||||
|
)
|
||||||
|
|
||||||
type caddyTestServer struct {
|
type caddyTestServer struct {
|
||||||
addr string
|
addr string
|
||||||
|
|
@ -144,14 +149,14 @@ func (c *caddyTestServer) server() *caddyhttp.Server {
|
||||||
if c.contents == nil {
|
if c.contents == nil {
|
||||||
c.contents = make(map[string][]byte)
|
c.contents = make(map[string][]byte)
|
||||||
}
|
}
|
||||||
index, err := ioutil.ReadFile(c.root + "/index.html")
|
index, err := os.ReadFile(c.root + "/index.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
c.contents[""] = index
|
c.contents[""] = index
|
||||||
c.contents["/"] = index
|
c.contents["/"] = index
|
||||||
c.contents["/index.html"] = index
|
c.contents["/index.html"] = index
|
||||||
c.contents["/pic.png"], err = ioutil.ReadFile(c.root + "/pic.png")
|
c.contents["/pic.png"], err = os.ReadFile(c.root + "/pic.png")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -160,6 +165,7 @@ func (c *caddyTestServer) server() *caddyhttp.Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
// For simulating/mimicing Caddy's built-in auto-HTTPS redirects. Super hacky but w/e.
|
// For simulating/mimicing Caddy's built-in auto-HTTPS redirects. Super hacky but w/e.
|
||||||
|
|
||||||
func (c *caddyTestServer) redirServer() *caddyhttp.Server {
|
func (c *caddyTestServer) redirServer() *caddyhttp.Server {
|
||||||
return &caddyhttp.Server{
|
return &caddyhttp.Server{
|
||||||
Listen: []string{":" + c.httpRedirPort},
|
Listen: []string{":" + c.httpRedirPort},
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
FROM alpine:3.6
|
FROM alpine:3.18.2
|
||||||
|
|
||||||
LABEL description="Docker image for caddy+forwardproxy plugin."
|
LABEL description="Docker image for caddy+forwardproxy plugin."
|
||||||
LABEL maintainer="SergeyFrolov@colorado.edu"
|
LABEL maintainer="SergeyFrolov@colorado.edu"
|
||||||
|
|
|
||||||
|
|
@ -26,17 +26,17 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
caddy "github.com/caddyserver/caddy/v2"
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||||
"github.com/caddyserver/forwardproxy/httpclient"
|
"github.com/caddyserver/forwardproxy/httpclient"
|
||||||
|
|
@ -60,7 +60,7 @@ type Handler struct {
|
||||||
// If true, the Forwarded header will not be augmented with your IP address.
|
// If true, the Forwarded header will not be augmented with your IP address.
|
||||||
HideIP bool `json:"hide_ip,omitempty"`
|
HideIP bool `json:"hide_ip,omitempty"`
|
||||||
|
|
||||||
// If true, the Via heaeder will not be added.
|
// If true, the Via header will not be added.
|
||||||
HideVia bool `json:"hide_via,omitempty"`
|
HideVia bool `json:"hide_via,omitempty"`
|
||||||
|
|
||||||
// Host(s) (and ports) of the proxy. When you configure a client,
|
// Host(s) (and ports) of the proxy. When you configure a client,
|
||||||
|
|
@ -188,7 +188,7 @@ func (h *Handler) Provision(ctx caddy.Context) error {
|
||||||
// either way, it's impossible to have a legit TLS certificate for "127.0.0.1" - TODO: not true anymore
|
// either way, it's impossible to have a legit TLS certificate for "127.0.0.1" - TODO: not true anymore
|
||||||
h.logger.Info("Localhost upstream detected, disabling verification of TLS certificate")
|
h.logger.Info("Localhost upstream detected, disabling verification of TLS certificate")
|
||||||
d.DialTLS = func(network string, address string) (net.Conn, string, error) {
|
d.DialTLS = func(network string, address string) (net.Conn, string, error) {
|
||||||
conn, err := tls.Dial(network, address, &tls.Config{InsecureSkipVerify: true})
|
conn, err := tls.Dial(network, address, &tls.Config{InsecureSkipVerify: true}) // #nosec G402
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|
@ -345,17 +345,17 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
|
||||||
// make sure request is idempotent and could be retried by saving the Body
|
// make sure request is idempotent and could be retried by saving the Body
|
||||||
// None of those methods are supposed to have body,
|
// None of those methods are supposed to have body,
|
||||||
// but we still need to copy the r.Body, even if it's empty
|
// but we still need to copy the r.Body, even if it's empty
|
||||||
rBodyBuf, err := ioutil.ReadAll(r.Body)
|
rBodyBuf, err := io.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddyhttp.Error(http.StatusBadRequest,
|
return caddyhttp.Error(http.StatusBadRequest,
|
||||||
fmt.Errorf("failed to read request body: %v", err))
|
fmt.Errorf("failed to read request body: %v", err))
|
||||||
}
|
}
|
||||||
r.GetBody = func() (io.ReadCloser, error) {
|
r.GetBody = func() (io.ReadCloser, error) {
|
||||||
return ioutil.NopCloser(bytes.NewReader(rBodyBuf)), nil
|
return io.NopCloser(bytes.NewReader(rBodyBuf)), nil
|
||||||
}
|
}
|
||||||
r.Body, _ = r.GetBody()
|
r.Body, _ = r.GetBody()
|
||||||
}
|
}
|
||||||
response, err = h.httpTransport.RoundTrip(r)
|
response, _ = h.httpTransport.RoundTrip(r)
|
||||||
} else {
|
} else {
|
||||||
// Upstream requests don't interact well with Transport: connections could always be
|
// Upstream requests don't interact well with Transport: connections could always be
|
||||||
// reused, but Transport thinks they go to different Hosts, so it spawns tons of
|
// reused, but Transport thinks they go to different Hosts, so it spawns tons of
|
||||||
|
|
@ -384,7 +384,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
|
||||||
fmt.Errorf("failed to read upstream response: %v", err))
|
fmt.Errorf("failed to read upstream response: %v", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r.Body.Close()
|
err = r.Body.Close()
|
||||||
|
|
||||||
if response != nil {
|
if response != nil {
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
|
|
@ -406,7 +406,7 @@ func (h Handler) checkCredentials(r *http.Request) error {
|
||||||
return errors.New("Proxy-Authorization is required! Expected format: <type> <credentials>")
|
return errors.New("Proxy-Authorization is required! Expected format: <type> <credentials>")
|
||||||
}
|
}
|
||||||
if strings.ToLower(pa[0]) != "basic" {
|
if strings.ToLower(pa[0]) != "basic" {
|
||||||
return errors.New("Auth type is not supported")
|
return errors.New("auth type is not supported")
|
||||||
}
|
}
|
||||||
for _, creds := range h.AuthCredentials {
|
for _, creds := range h.AuthCredentials {
|
||||||
if subtle.ConstantTimeCompare(creds, []byte(pa[1])) == 1 {
|
if subtle.ConstantTimeCompare(creds, []byte(pa[1])) == 1 {
|
||||||
|
|
@ -421,7 +421,7 @@ func (h Handler) checkCredentials(r *http.Request) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return errors.New("Invalid credentials")
|
return errors.New("invalid credentials")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h Handler) shouldServePACFile(r *http.Request) bool {
|
func (h Handler) shouldServePACFile(r *http.Request) bool {
|
||||||
|
|
@ -543,10 +543,10 @@ func serveHiddenPage(w http.ResponseWriter, authErr error) error {
|
||||||
if authErr != nil {
|
if authErr != nil {
|
||||||
w.Header().Set("Proxy-Authenticate", "Basic realm=\"Caddy Secure Web Proxy\"")
|
w.Header().Set("Proxy-Authenticate", "Basic realm=\"Caddy Secure Web Proxy\"")
|
||||||
w.WriteHeader(http.StatusProxyAuthRequired)
|
w.WriteHeader(http.StatusProxyAuthRequired)
|
||||||
w.Write([]byte(fmt.Sprintf(hiddenPage, AuthFail)))
|
_, _ = w.Write([]byte(fmt.Sprintf(hiddenPage, AuthFail)))
|
||||||
return authErr
|
return authErr
|
||||||
}
|
}
|
||||||
w.Write([]byte(fmt.Sprintf(hiddenPage, AuthOk)))
|
_, _ = w.Write([]byte(fmt.Sprintf(hiddenPage, AuthOk)))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -572,12 +572,14 @@ func serveHijack(w http.ResponseWriter, targetConn net.Conn) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddyhttp.Error(http.StatusBadGateway, err)
|
return caddyhttp.Error(http.StatusBadGateway, err)
|
||||||
}
|
}
|
||||||
targetConn.Write(rbuf)
|
_, _ = targetConn.Write(rbuf)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Since we hijacked the connection, we lost the ability to write and flush headers via w.
|
// Since we hijacked the connection, we lost the ability to write and flush headers via w.
|
||||||
// Let's handcraft the response and send it manually.
|
// Let's handcraft the response and send it manually.
|
||||||
res := &http.Response{StatusCode: http.StatusOK,
|
res := &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
Proto: "HTTP/1.1",
|
Proto: "HTTP/1.1",
|
||||||
ProtoMajor: 1,
|
ProtoMajor: 1,
|
||||||
ProtoMinor: 1,
|
ProtoMinor: 1,
|
||||||
|
|
@ -585,7 +587,13 @@ func serveHijack(w http.ResponseWriter, targetConn net.Conn) error {
|
||||||
}
|
}
|
||||||
res.Header.Set("Server", "Caddy")
|
res.Header.Set("Server", "Caddy")
|
||||||
|
|
||||||
err = res.Write(clientConn)
|
buf := bufio.NewWriter(clientConn)
|
||||||
|
err = res.Write(buf)
|
||||||
|
if err != nil {
|
||||||
|
return caddyhttp.Error(http.StatusInternalServerError,
|
||||||
|
fmt.Errorf("failed to write response: %v", err))
|
||||||
|
}
|
||||||
|
err = buf.Flush()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddyhttp.Error(http.StatusInternalServerError,
|
return caddyhttp.Error(http.StatusInternalServerError,
|
||||||
fmt.Errorf("failed to send response to client: %v", err))
|
fmt.Errorf("failed to send response to client: %v", err))
|
||||||
|
|
@ -600,16 +608,18 @@ func serveHijack(w http.ResponseWriter, targetConn net.Conn) error {
|
||||||
func dualStream(target net.Conn, clientReader io.ReadCloser, clientWriter io.Writer) error {
|
func dualStream(target net.Conn, clientReader io.ReadCloser, clientWriter io.Writer) error {
|
||||||
stream := func(w io.Writer, r io.Reader) error {
|
stream := func(w io.Writer, r io.Reader) error {
|
||||||
// copy bytes from r to w
|
// copy bytes from r to w
|
||||||
buf := bufferPool.Get().([]byte)
|
bufPtr := bufferPool.Get().(*[]byte)
|
||||||
|
buf := *bufPtr
|
||||||
buf = buf[0:cap(buf)]
|
buf = buf[0:cap(buf)]
|
||||||
_, _err := flushingIoCopy(w, r, buf)
|
_, _err := flushingIoCopy(w, r, buf)
|
||||||
bufferPool.Put(buf)
|
bufferPool.Put(bufPtr)
|
||||||
|
|
||||||
if cw, ok := w.(closeWriter); ok {
|
if cw, ok := w.(closeWriter); ok {
|
||||||
cw.CloseWrite()
|
_ = cw.CloseWrite()
|
||||||
}
|
}
|
||||||
return _err
|
return _err
|
||||||
}
|
}
|
||||||
go stream(target, clientReader)
|
go stream(target, clientReader) //nolint: errcheck
|
||||||
return stream(clientWriter, target)
|
return stream(clientWriter, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -664,10 +674,11 @@ func forwardResponse(w http.ResponseWriter, response *http.Response) error {
|
||||||
}
|
}
|
||||||
removeHopByHop(w.Header())
|
removeHopByHop(w.Header())
|
||||||
w.WriteHeader(response.StatusCode)
|
w.WriteHeader(response.StatusCode)
|
||||||
buf := bufferPool.Get().([]byte)
|
bufPtr := bufferPool.Get().(*[]byte)
|
||||||
|
buf := *bufPtr
|
||||||
buf = buf[0:cap(buf)]
|
buf = buf[0:cap(buf)]
|
||||||
_, err := io.CopyBuffer(w, response.Body, buf)
|
_, err := io.CopyBuffer(w, response.Body, buf)
|
||||||
bufferPool.Put(buf)
|
bufferPool.Put(bufPtr)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -703,7 +714,8 @@ function FindProxyForURL(url, host) {
|
||||||
|
|
||||||
var bufferPool = sync.Pool{
|
var bufferPool = sync.Pool{
|
||||||
New: func() interface{} {
|
New: func() interface{} {
|
||||||
return make([]byte, 0, 32*1024)
|
buffer := make([]byte, 0, 32*1024)
|
||||||
|
return &buffer
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -725,7 +737,8 @@ type ProbeResistance struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func readLinesFromFile(filename string) ([]string, error) {
|
func readLinesFromFile(filename string) ([]string, error) {
|
||||||
file, err := os.Open(filename)
|
cleanFilename := filepath.Clean(filename)
|
||||||
|
file, err := os.Open(cleanFilename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
@ -75,7 +74,7 @@ func connectAndGetViaProxy(targetHost, resource, proxyAddr, httpTargetVer, proxy
|
||||||
req.ProtoMajor = 2
|
req.ProtoMajor = 2
|
||||||
req.ProtoMinor = 0
|
req.ProtoMinor = 0
|
||||||
pr, pw := io.Pipe()
|
pr, pw := io.Pipe()
|
||||||
req.Body = ioutil.NopCloser(pr)
|
req.Body = io.NopCloser(pr)
|
||||||
t := http2.Transport{}
|
t := http2.Transport{}
|
||||||
clientConn, err := t.NewClientConn(proxyConn)
|
clientConn, err := t.NewClientConn(proxyConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
115
go.mod
115
go.mod
|
|
@ -1,9 +1,116 @@
|
||||||
module github.com/caddyserver/forwardproxy
|
module github.com/caddyserver/forwardproxy
|
||||||
|
|
||||||
go 1.14
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/caddyserver/caddy/v2 v2.4.0-beta.1
|
github.com/caddyserver/caddy/v2 v2.6.4
|
||||||
go.uber.org/zap v1.16.0
|
go.uber.org/zap v1.24.0
|
||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777
|
golang.org/x/net v0.11.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
filippo.io/edwards25519 v1.0.0 // indirect
|
||||||
|
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
|
||||||
|
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||||
|
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||||
|
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
||||||
|
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||||
|
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||||
|
github.com/alecthomas/chroma/v2 v2.5.0 // indirect
|
||||||
|
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
|
||||||
|
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/caddyserver/certmagic v0.17.2 // indirect
|
||||||
|
github.com/cespare/xxhash v1.1.0 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
|
github.com/dgraph-io/badger v1.6.2 // indirect
|
||||||
|
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
|
||||||
|
github.com/dgraph-io/ristretto v0.1.0 // indirect
|
||||||
|
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
||||||
|
github.com/dlclark/regexp2 v1.7.0 // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/go-kit/kit v0.10.0 // indirect
|
||||||
|
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||||
|
github.com/golang/glog v1.0.0 // indirect
|
||||||
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/google/cel-go v0.13.0 // indirect
|
||||||
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||||
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
|
github.com/huandu/xstrings v1.3.3 // indirect
|
||||||
|
github.com/imdario/mergo v0.3.12 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||||
|
github.com/jackc/pgconn v1.13.0 // indirect
|
||||||
|
github.com/jackc/pgio v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgproto3/v2 v2.3.1 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||||
|
github.com/jackc/pgtype v1.12.0 // indirect
|
||||||
|
github.com/jackc/pgx/v4 v4.17.2 // indirect
|
||||||
|
github.com/klauspost/compress v1.15.15 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
|
||||||
|
github.com/libdns/libdns v0.2.1 // indirect
|
||||||
|
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||||
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||||
|
github.com/mholt/acmez v1.1.0 // indirect
|
||||||
|
github.com/micromdm/scep/v2 v2.1.0 // indirect
|
||||||
|
github.com/miekg/dns v1.1.50 // indirect
|
||||||
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
|
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
|
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/prometheus/client_golang v1.14.0 // indirect
|
||||||
|
github.com/prometheus/client_model v0.3.0 // indirect
|
||||||
|
github.com/prometheus/common v0.37.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.8.0 // indirect
|
||||||
|
github.com/quic-go/qpack v0.4.0 // indirect
|
||||||
|
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||||
|
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
||||||
|
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
||||||
|
github.com/quic-go/quic-go v0.32.0 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
github.com/shopspring/decimal v1.2.0 // indirect
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||||
|
github.com/slackhq/nebula v1.6.1 // indirect
|
||||||
|
github.com/smallstep/certificates v0.23.2 // indirect
|
||||||
|
github.com/smallstep/nosql v0.5.0 // indirect
|
||||||
|
github.com/smallstep/truststore v0.12.1 // indirect
|
||||||
|
github.com/spf13/cast v1.4.1 // indirect
|
||||||
|
github.com/spf13/cobra v1.6.1 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/stoewer/go-strcase v1.2.0 // indirect
|
||||||
|
github.com/tailscale/tscert v0.0.0-20230124224810-c6dc1f4049b2 // indirect
|
||||||
|
github.com/urfave/cli v1.22.12 // indirect
|
||||||
|
github.com/yuin/goldmark v1.5.4 // indirect
|
||||||
|
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20220924101305-151362477c87 // indirect
|
||||||
|
go.etcd.io/bbolt v1.3.6 // indirect
|
||||||
|
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
|
||||||
|
go.step.sm/cli-utils v0.7.5 // indirect
|
||||||
|
go.step.sm/crypto v0.23.2 // indirect
|
||||||
|
go.step.sm/linkedca v0.19.0 // indirect
|
||||||
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
|
golang.org/x/crypto v0.10.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
|
||||||
|
golang.org/x/mod v0.8.0 // indirect
|
||||||
|
golang.org/x/sys v0.9.0 // indirect
|
||||||
|
golang.org/x/term v0.9.0 // indirect
|
||||||
|
golang.org/x/text v0.10.0 // indirect
|
||||||
|
golang.org/x/tools v0.6.0 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 // indirect
|
||||||
|
google.golang.org/grpc v1.52.3 // indirect
|
||||||
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
|
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
howett.net/plist v1.0.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -132,12 +132,12 @@ func (c *HTTPConnectDialer) DialContext(ctx context.Context, network, address st
|
||||||
|
|
||||||
resp, err := h2clientConn.RoundTrip(req)
|
resp, err := h2clientConn.RoundTrip(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rawConn.Close()
|
err = rawConn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
rawConn.Close()
|
_ = rawConn.Close()
|
||||||
return nil, errors.New("Proxy responded with non 200 code: " + resp.Status)
|
return nil, errors.New("Proxy responded with non 200 code: " + resp.Status)
|
||||||
}
|
}
|
||||||
return NewHttp2Conn(rawConn, pw, resp.Body), nil
|
return NewHttp2Conn(rawConn, pw, resp.Body), nil
|
||||||
|
|
@ -150,18 +150,18 @@ func (c *HTTPConnectDialer) DialContext(ctx context.Context, network, address st
|
||||||
|
|
||||||
err := req.Write(rawConn)
|
err := req.Write(rawConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rawConn.Close()
|
err = rawConn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := http.ReadResponse(bufio.NewReader(rawConn), req)
|
resp, err := http.ReadResponse(bufio.NewReader(rawConn), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rawConn.Close()
|
err = rawConn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
rawConn.Close()
|
_ = rawConn.Close()
|
||||||
return nil, errors.New("Proxy responded with non 200 code: " + resp.Status)
|
return nil, errors.New("Proxy responded with non 200 code: " + resp.Status)
|
||||||
}
|
}
|
||||||
return rawConn, nil
|
return rawConn, nil
|
||||||
|
|
@ -207,6 +207,7 @@ func (c *HTTPConnectDialer) DialContext(ctx context.Context, network, address st
|
||||||
tlsConf := tls.Config{
|
tlsConf := tls.Config{
|
||||||
NextProtos: []string{"h2", "http/1.1"},
|
NextProtos: []string{"h2", "http/1.1"},
|
||||||
ServerName: c.ProxyURL.Hostname(),
|
ServerName: c.ProxyURL.Hostname(),
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
}
|
}
|
||||||
tlsConn, err := tls.Dial(network, c.ProxyURL.Host, &tlsConf)
|
tlsConn, err := tls.Dial(network, c.ProxyURL.Host, &tlsConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -232,13 +233,13 @@ func (c *HTTPConnectDialer) DialContext(ctx context.Context, network, address st
|
||||||
t := http2.Transport{}
|
t := http2.Transport{}
|
||||||
h2clientConn, err := t.NewClientConn(rawConn)
|
h2clientConn, err := t.NewClientConn(rawConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rawConn.Close()
|
err = rawConn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyConn, err := connectHttp2(rawConn, h2clientConn)
|
proxyConn, err := connectHttp2(rawConn, h2clientConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rawConn.Close()
|
err = rawConn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if c.EnableH2ConnReuse {
|
if c.EnableH2ConnReuse {
|
||||||
|
|
@ -249,7 +250,7 @@ func (c *HTTPConnectDialer) DialContext(ctx context.Context, network, address st
|
||||||
}
|
}
|
||||||
return proxyConn, err
|
return proxyConn, err
|
||||||
default:
|
default:
|
||||||
rawConn.Close()
|
_ = rawConn.Close()
|
||||||
return nil, errors.New("negotiated unsupported application layer protocol: " +
|
return nil, errors.New("negotiated unsupported application layer protocol: " +
|
||||||
negotiatedProtocol)
|
negotiatedProtocol)
|
||||||
}
|
}
|
||||||
|
|
@ -274,8 +275,13 @@ func (h *http2Conn) Write(p []byte) (n int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *http2Conn) Close() error {
|
func (h *http2Conn) Close() error {
|
||||||
h.in.Close()
|
inErr := h.in.Close()
|
||||||
return h.out.Close()
|
outErr := h.out.Close()
|
||||||
|
|
||||||
|
if inErr != nil {
|
||||||
|
return inErr
|
||||||
|
}
|
||||||
|
return outErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *http2Conn) CloseConn() error {
|
func (h *http2Conn) CloseConn() error {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -44,6 +44,7 @@ func TestGETAuthWrongProbeResist(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if responseProbeResist.StatusCode != responseReference.StatusCode {
|
if responseProbeResist.StatusCode != responseReference.StatusCode {
|
||||||
t.Fatalf("Expected response: %d, Got: %d\n",
|
t.Fatalf("Expected response: %d, Got: %d\n",
|
||||||
responseReference.StatusCode, responseProbeResist.StatusCode)
|
responseReference.StatusCode, responseProbeResist.StatusCode)
|
||||||
|
|
@ -314,6 +315,7 @@ func responsesAreEqual(res1, res2 *http.Response) error {
|
||||||
if len(s1) != len(s2) {
|
if len(s1) != len(s2) {
|
||||||
return fmt.Sprintf("different length: %d vs %d", len(s1), len(s2))
|
return fmt.Sprintf("different length: %d vs %d", len(s1), len(s2))
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range s1 {
|
for i := range s1 {
|
||||||
if s1[i] != s2[i] {
|
if s1[i] != s2[i] {
|
||||||
return fmt.Sprintf("different string at position %d: %s vs %s", i, s1[i], s2[i])
|
return fmt.Sprintf("different string at position %d: %s vs %s", i, s1[i], s2[i])
|
||||||
|
|
@ -330,6 +332,7 @@ func responsesAreEqual(res1, res2 *http.Response) error {
|
||||||
if len(res1.Header) != len(res2.Header) {
|
if len(res1.Header) != len(res2.Header) {
|
||||||
return errors.New("Headers have different length")
|
return errors.New("Headers have different length")
|
||||||
}
|
}
|
||||||
|
|
||||||
for k1, v1 := range res1.Header {
|
for k1, v1 := range res1.Header {
|
||||||
k1Lower := strings.ToLower(k1)
|
k1Lower := strings.ToLower(k1)
|
||||||
if k1Lower == "date" {
|
if k1Lower == "date" {
|
||||||
|
|
@ -339,21 +342,13 @@ func responsesAreEqual(res1, res2 *http.Response) error {
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("header \"%s: %s\" is absent in res2", k1, v1)
|
return fmt.Errorf("header \"%s: %s\" is absent in res2", k1, v1)
|
||||||
}
|
}
|
||||||
// if k1Lower == "location" {
|
|
||||||
// for i, h := range v2 {
|
|
||||||
// v2[i] = removeAddressesStr(h)
|
|
||||||
// }
|
|
||||||
// for i, h := range v1 {
|
|
||||||
// v1[i] = removeAddressesStr(h)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
if errStr = stringSlicesAreEqual(v1, v2); errStr != "" {
|
if errStr = stringSlicesAreEqual(v1, v2); errStr != "" {
|
||||||
return fmt.Errorf("header \"%s\" is different: %s", k1, errStr)
|
return fmt.Errorf("header \"%s\" is different: %s", k1, errStr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Compare bodies
|
// Compare bodies
|
||||||
buf1, err1 := ioutil.ReadAll(res1.Body)
|
buf1, err1 := io.ReadAll(res1.Body)
|
||||||
buf2, err2 := ioutil.ReadAll(res2.Body)
|
buf2, err2 := io.ReadAll(res2.Body)
|
||||||
n1 := len(buf1)
|
n1 := len(buf1)
|
||||||
n2 := len(buf2)
|
n2 := len(buf2)
|
||||||
makeBodyError := func(s string) error {
|
makeBodyError := func(s string) error {
|
||||||
|
|
@ -379,17 +374,13 @@ func responsesAreEqual(res1, res2 *http.Response) error {
|
||||||
// Responses from forwardproxy + proberesist and generic caddy can have different addresses present in headers.
|
// Responses from forwardproxy + proberesist and generic caddy can have different addresses present in headers.
|
||||||
// To avoid false positives - remove addresses before comparing.
|
// To avoid false positives - remove addresses before comparing.
|
||||||
func removeAddressesByte(b []byte) []byte {
|
func removeAddressesByte(b []byte) []byte {
|
||||||
b = bytes.Replace(b, []byte(caddyForwardProxyProbeResist.addr),
|
b = bytes.ReplaceAll(b, []byte(caddyForwardProxyProbeResist.addr),
|
||||||
bytes.Repeat([]byte{'#'}, len(caddyForwardProxyProbeResist.addr)), -1)
|
bytes.Repeat([]byte{'#'}, len(caddyForwardProxyProbeResist.addr)))
|
||||||
b = bytes.Replace(b, []byte(caddyDummyProbeResist.addr),
|
b = bytes.ReplaceAll(b, []byte(caddyDummyProbeResist.addr),
|
||||||
bytes.Repeat([]byte{'#'}, len(caddyDummyProbeResist.addr)), -1)
|
bytes.Repeat([]byte{'#'}, len(caddyDummyProbeResist.addr)))
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeAddressesStr(s string) string {
|
|
||||||
return string(removeAddressesByte([]byte(s)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func changePort(inputAddr, toPort string) string {
|
func changePort(inputAddr, toPort string) string {
|
||||||
host, _, err := net.SplitHostPort(inputAddr)
|
host, _, err := net.SplitHostPort(inputAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue