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
|
||||
164
README.md
164
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.
|
||||
|
|
@ -23,27 +97,27 @@ Here's an example of all properties in use (note that the syntax is subject to c
|
|||
```
|
||||
:443, example.com
|
||||
route {
|
||||
forward_proxy {
|
||||
basicauth user1 password1
|
||||
basicauth user2 password2
|
||||
ports 80 443
|
||||
hide_ip
|
||||
hide_via
|
||||
probe_resistance secret-link-kWWL9Q.com # alternatively you can use a real domain, such as caddyserver.com
|
||||
serve_pac /secret-proxy.pac
|
||||
dial_timeout 30
|
||||
upstream https://user:password@extra-upstream-hop.com
|
||||
acl {
|
||||
allow *.caddyserver.com
|
||||
deny 192.168.1.1/32 192.168.0.0/16 *.prohibitedsite.com *.localhost
|
||||
allow ::1/128 8.8.8.8 github.com *.github.io
|
||||
allow_file /path/to/whitelist.txt
|
||||
deny_file /path/to/blacklist.txt
|
||||
allow all
|
||||
deny all # unreachable rule, remaining requests are matched by `allow all` above
|
||||
}
|
||||
}
|
||||
file_server
|
||||
forward_proxy {
|
||||
basic_auth user1 0NtCL2JPJBgPPMmlPcJ
|
||||
basic_auth user2 密码
|
||||
ports 80 443
|
||||
hide_ip
|
||||
hide_via
|
||||
probe_resistance secret-link-kWWL9Q.com # alternatively you can use a real domain, such as caddyserver.com
|
||||
serve_pac /secret-proxy.pac
|
||||
dial_timeout 30
|
||||
upstream https://user:password@extra-upstream-hop.com
|
||||
acl {
|
||||
allow *.caddyserver.com
|
||||
deny 192.168.1.1/32 192.168.0.0/16 *.prohibitedsite.com *.localhost
|
||||
allow ::1/128 8.8.8.8 github.com *.github.io
|
||||
allow_file /path/to/whitelist.txt
|
||||
deny_file /path/to/blacklist.txt
|
||||
allow all
|
||||
deny all # unreachable rule, remaining requests are matched by `allow all` above
|
||||
}
|
||||
}
|
||||
file_server
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -95,24 +169,24 @@ Specifies **order** and rules for allowed destination IP networks, IP addresses
|
|||
The hostname in each forwardproxy request will be resolved to an IP address,
|
||||
and caddy will check the IP address and hostname against the directives in order until a directive matches the request.
|
||||
acl_directive may be:
|
||||
- **allow [ip or subnet or hostname] [ip or subnet or hostname]...**
|
||||
- **allow_file /path/to/whitelist.txt**
|
||||
- **deny [ip or subnet or hostname] [ip or subnet or hostname]...**
|
||||
- **deny_file /path/to/blacklist.txt**
|
||||
- **allow [ip or subnet or hostname] [ip or subnet or hostname]...**
|
||||
- **allow_file /path/to/whitelist.txt**
|
||||
- **deny [ip or subnet or hostname] [ip or subnet or hostname]...**
|
||||
- **deny_file /path/to/blacklist.txt**
|
||||
|
||||
If you don't want unmatched requests to be subject to the default policy, you could finish
|
||||
your acl rules with one of the following to specify action on unmatched requests:
|
||||
- **allow all**
|
||||
- **deny all**
|
||||
|
||||
For hostname, you can specify `*.` as a prefix to match domain and subdomains. For example,
|
||||
`*.caddyserver.com` will match `caddyserver.com`, `subdomain.caddyserver.com`, but not `fakecaddyserver.com`.
|
||||
Note that hostname rules, matched early in the chain, will override later IP rules,
|
||||
so it is advised to put IP rules first, unless domains are highly trusted and should override the
|
||||
IP rules. Also note that domain-based blacklists are easily circumventable by directly specifying the IP.
|
||||
For `allow_file`/`deny_file` directives, syntax is the same, and each entry must be separated by newline.
|
||||
This policy applies to all requests except requests to the proxy's own domain and port.
|
||||
Whitelisting/blacklisting of ports on per-host/IP basis is not supported.
|
||||
If you don't want unmatched requests to be subject to the default policy, you could finish
|
||||
your acl rules with one of the following to specify action on unmatched requests:
|
||||
- **allow all**
|
||||
- **deny all**
|
||||
|
||||
For hostname, you can specify `*.` as a prefix to match domain and subdomains. For example,
|
||||
`*.caddyserver.com` will match `caddyserver.com`, `subdomain.caddyserver.com`, but not `fakecaddyserver.com`.
|
||||
Note that hostname rules, matched early in the chain, will override later IP rules,
|
||||
so it is advised to put IP rules first, unless domains are highly trusted and should override the
|
||||
IP rules. Also note that domain-based blacklists are easily circumventable by directly specifying the IP.
|
||||
For `allow_file`/`deny_file` directives, syntax is the same, and each entry must be separated by newline.
|
||||
This policy applies to all requests except requests to the proxy's own domain and port.
|
||||
Whitelisting/blacklisting of ports on per-host/IP basis is not supported.
|
||||
_Default policy:_
|
||||
acl {
|
||||
deny 10.0.0.0/8 127.0.0.0/8 172.16.0.0/12 192.168.0.0/16 ::1/128 fe80::/10
|
||||
|
|
@ -147,11 +221,11 @@ Don't forget to add `http.forwardproxy` plugin.
|
|||
|
||||
#### 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
|
||||
go install github.com/caddyserver/forwardproxy/cmd/caddy
|
||||
```
|
||||
Built `caddy` binary will be stored in $GOPATH/bin.
|
||||
go install github.com/caddyserver/forwardproxy/cmd/caddy@latest
|
||||
```
|
||||
Built `caddy` binary will be stored in $GOPATH/bin.
|
||||
|
||||
## Client Configuration
|
||||
|
||||
|
|
|
|||
6
acl.go
6
acl.go
|
|
@ -37,7 +37,6 @@ func (a *aclIPRule) tryMatch(ip net.IP, domain string) aclDecision {
|
|||
return aclDecisionAllow
|
||||
}
|
||||
return aclDecisionDeny
|
||||
|
||||
}
|
||||
|
||||
type aclDomainRule struct {
|
||||
|
|
@ -47,9 +46,8 @@ type aclDomainRule struct {
|
|||
}
|
||||
|
||||
func (a *aclDomainRule) tryMatch(ip net.IP, domain string) aclDecision {
|
||||
if strings.HasSuffix(domain, ".") {
|
||||
domain = domain[:len(domain)-1]
|
||||
}
|
||||
domain = strings.TrimPrefix(domain, ".")
|
||||
|
||||
if domain == a.domain ||
|
||||
a.subdomainsAllowed && strings.HasSuffix(domain, "."+a.domain) {
|
||||
if a.allow {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import (
|
|||
"strconv"
|
||||
"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/httpcaddyfile"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
|
|
@ -147,7 +147,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
|||
aclAllow := false
|
||||
switch aclDirective {
|
||||
case "allow":
|
||||
ruleSubjects = args[:]
|
||||
ruleSubjects = args
|
||||
aclAllow = true
|
||||
case "allow_file":
|
||||
if len(args) != 1 {
|
||||
|
|
@ -159,7 +159,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
|||
}
|
||||
aclAllow = true
|
||||
case "deny":
|
||||
ruleSubjects = args[:]
|
||||
ruleSubjects = args
|
||||
case "deny_file":
|
||||
if len(args) != 1 {
|
||||
return d.Err("denyfile accepts a single filename argument")
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
|
|
@ -25,19 +24,21 @@ import (
|
|||
"github.com/caddyserver/caddy/v2/modules/caddytls"
|
||||
)
|
||||
|
||||
var credentialsEmpty = ""
|
||||
var credentialsCorrectPlain = "test:pass"
|
||||
var credentialsCorrect = "Basic dGVzdDpwYXNz" // test:pass
|
||||
var credentialsUpstreamCorrect = "basic dXBzdHJlYW10ZXN0OnVwc3RyZWFtcGFzcw==" // upstreamtest:upstreampass
|
||||
var credentialsWrong = []string{
|
||||
"",
|
||||
"\"\"",
|
||||
"Basic dzp3",
|
||||
"Basic \"\"",
|
||||
"Foo bar",
|
||||
"Tssssssss",
|
||||
"Basic dpz3 asp",
|
||||
}
|
||||
var (
|
||||
credentialsEmpty = ""
|
||||
credentialsCorrectPlain = "test:pass"
|
||||
credentialsCorrect = "Basic dGVzdDpwYXNz" // test:pass
|
||||
credentialsUpstreamCorrect = "basic dXBzdHJlYW10ZXN0OnVwc3RyZWFtcGFzcw==" // upstreamtest:upstreampass
|
||||
credentialsWrong = []string{
|
||||
"",
|
||||
"\"\"",
|
||||
"Basic dzp3",
|
||||
"Basic \"\"",
|
||||
"Foo bar",
|
||||
"Tssssssss",
|
||||
"Basic dpz3 asp",
|
||||
}
|
||||
)
|
||||
|
||||
/*
|
||||
Test naming: Test{httpVer}Proxy{Method}{Auth}{Credentials}{httpVer}
|
||||
|
|
@ -45,17 +46,21 @@ GET/CONNECT -- get gets, connect connects and gets
|
|||
Auth/NoAuth
|
||||
Empty/Correct/Wrong -- tries different credentials
|
||||
*/
|
||||
var testResources = []string{"/", "/pic.png"}
|
||||
var testHTTPProxyVersions = []string{"HTTP/2.0", "HTTP/1.1"}
|
||||
var testHTTPTargetVersions = []string{"HTTP/1.1"}
|
||||
var httpVersionToALPN = map[string]string{
|
||||
"HTTP/1.1": "http/1.1",
|
||||
"HTTP/2.0": "h2",
|
||||
}
|
||||
var (
|
||||
testResources = []string{"/", "/pic.png"}
|
||||
testHTTPProxyVersions = []string{"HTTP/2.0", "HTTP/1.1"}
|
||||
testHTTPTargetVersions = []string{"HTTP/1.1"}
|
||||
httpVersionToALPN = map[string]string{
|
||||
"HTTP/1.1": "http/1.1",
|
||||
"HTTP/2.0": "h2",
|
||||
}
|
||||
)
|
||||
|
||||
var blacklistedDomain = "google-public-dns-a.google.com" // supposed to ever resolve to one of 2 IP addresses below
|
||||
var blacklistedIPv4 = "8.8.8.8"
|
||||
var blacklistedIPv6 = "2001:4860:4860::8888"
|
||||
var (
|
||||
blacklistedDomain = "google-public-dns-a.google.com" // supposed to ever resolve to one of 2 IP addresses below
|
||||
blacklistedIPv4 = "8.8.8.8"
|
||||
blacklistedIPv6 = "2001:4860:4860::8888"
|
||||
)
|
||||
|
||||
type caddyTestServer struct {
|
||||
addr string
|
||||
|
|
@ -144,14 +149,14 @@ func (c *caddyTestServer) server() *caddyhttp.Server {
|
|||
if c.contents == nil {
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
c.contents[""] = index
|
||||
c.contents["/"] = 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 {
|
||||
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.
|
||||
|
||||
func (c *caddyTestServer) redirServer() *caddyhttp.Server {
|
||||
return &caddyhttp.Server{
|
||||
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 maintainer="SergeyFrolov@colorado.edu"
|
||||
|
|
|
|||
|
|
@ -26,17 +26,17 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
caddy "github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
"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.
|
||||
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"`
|
||||
|
||||
// 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
|
||||
h.logger.Info("Localhost upstream detected, disabling verification of TLS certificate")
|
||||
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 {
|
||||
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
|
||||
// None of those methods are supposed to have body,
|
||||
// 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 {
|
||||
return caddyhttp.Error(http.StatusBadRequest,
|
||||
fmt.Errorf("failed to read request body: %v", err))
|
||||
}
|
||||
r.GetBody = func() (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(bytes.NewReader(rBodyBuf)), nil
|
||||
return io.NopCloser(bytes.NewReader(rBodyBuf)), nil
|
||||
}
|
||||
r.Body, _ = r.GetBody()
|
||||
}
|
||||
response, err = h.httpTransport.RoundTrip(r)
|
||||
response, _ = h.httpTransport.RoundTrip(r)
|
||||
} else {
|
||||
// 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
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
r.Body.Close()
|
||||
err = r.Body.Close()
|
||||
|
||||
if response != nil {
|
||||
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>")
|
||||
}
|
||||
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 {
|
||||
if subtle.ConstantTimeCompare(creds, []byte(pa[1])) == 1 {
|
||||
|
|
@ -421,7 +421,7 @@ func (h Handler) checkCredentials(r *http.Request) error {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("Invalid credentials")
|
||||
return errors.New("invalid credentials")
|
||||
}
|
||||
|
||||
func (h Handler) shouldServePACFile(r *http.Request) bool {
|
||||
|
|
@ -543,10 +543,10 @@ func serveHiddenPage(w http.ResponseWriter, authErr error) error {
|
|||
if authErr != nil {
|
||||
w.Header().Set("Proxy-Authenticate", "Basic realm=\"Caddy Secure Web Proxy\"")
|
||||
w.WriteHeader(http.StatusProxyAuthRequired)
|
||||
w.Write([]byte(fmt.Sprintf(hiddenPage, AuthFail)))
|
||||
_, _ = w.Write([]byte(fmt.Sprintf(hiddenPage, AuthFail)))
|
||||
return authErr
|
||||
}
|
||||
w.Write([]byte(fmt.Sprintf(hiddenPage, AuthOk)))
|
||||
_, _ = w.Write([]byte(fmt.Sprintf(hiddenPage, AuthOk)))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -572,12 +572,14 @@ func serveHijack(w http.ResponseWriter, targetConn net.Conn) error {
|
|||
if err != nil {
|
||||
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.
|
||||
// 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",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
|
|
@ -585,7 +587,13 @@ func serveHijack(w http.ResponseWriter, targetConn net.Conn) error {
|
|||
}
|
||||
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 {
|
||||
return caddyhttp.Error(http.StatusInternalServerError,
|
||||
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 {
|
||||
stream := func(w io.Writer, r io.Reader) error {
|
||||
// copy bytes from r to w
|
||||
buf := bufferPool.Get().([]byte)
|
||||
bufPtr := bufferPool.Get().(*[]byte)
|
||||
buf := *bufPtr
|
||||
buf = buf[0:cap(buf)]
|
||||
_, _err := flushingIoCopy(w, r, buf)
|
||||
bufferPool.Put(buf)
|
||||
bufferPool.Put(bufPtr)
|
||||
|
||||
if cw, ok := w.(closeWriter); ok {
|
||||
cw.CloseWrite()
|
||||
_ = cw.CloseWrite()
|
||||
}
|
||||
return _err
|
||||
}
|
||||
go stream(target, clientReader)
|
||||
go stream(target, clientReader) //nolint: errcheck
|
||||
return stream(clientWriter, target)
|
||||
}
|
||||
|
||||
|
|
@ -664,10 +674,11 @@ func forwardResponse(w http.ResponseWriter, response *http.Response) error {
|
|||
}
|
||||
removeHopByHop(w.Header())
|
||||
w.WriteHeader(response.StatusCode)
|
||||
buf := bufferPool.Get().([]byte)
|
||||
bufPtr := bufferPool.Get().(*[]byte)
|
||||
buf := *bufPtr
|
||||
buf = buf[0:cap(buf)]
|
||||
_, err := io.CopyBuffer(w, response.Body, buf)
|
||||
bufferPool.Put(buf)
|
||||
bufferPool.Put(bufPtr)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -703,7 +714,8 @@ function FindProxyForURL(url, host) {
|
|||
|
||||
var bufferPool = sync.Pool{
|
||||
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) {
|
||||
file, err := os.Open(filename)
|
||||
cleanFilename := filepath.Clean(filename)
|
||||
file, err := os.Open(cleanFilename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import (
|
|||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
|
@ -75,7 +74,7 @@ func connectAndGetViaProxy(targetHost, resource, proxyAddr, httpTargetVer, proxy
|
|||
req.ProtoMajor = 2
|
||||
req.ProtoMinor = 0
|
||||
pr, pw := io.Pipe()
|
||||
req.Body = ioutil.NopCloser(pr)
|
||||
req.Body = io.NopCloser(pr)
|
||||
t := http2.Transport{}
|
||||
clientConn, err := t.NewClientConn(proxyConn)
|
||||
if err != nil {
|
||||
|
|
|
|||
115
go.mod
115
go.mod
|
|
@ -1,9 +1,116 @@
|
|||
module github.com/caddyserver/forwardproxy
|
||||
|
||||
go 1.14
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/caddyserver/caddy/v2 v2.4.0-beta.1
|
||||
go.uber.org/zap v1.16.0
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777
|
||||
github.com/caddyserver/caddy/v2 v2.6.4
|
||||
go.uber.org/zap v1.24.0
|
||||
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)
|
||||
if err != nil {
|
||||
rawConn.Close()
|
||||
err = rawConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
rawConn.Close()
|
||||
_ = rawConn.Close()
|
||||
return nil, errors.New("Proxy responded with non 200 code: " + resp.Status)
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
rawConn.Close()
|
||||
err = rawConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(rawConn), req)
|
||||
if err != nil {
|
||||
rawConn.Close()
|
||||
err = rawConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
rawConn.Close()
|
||||
_ = rawConn.Close()
|
||||
return nil, errors.New("Proxy responded with non 200 code: " + resp.Status)
|
||||
}
|
||||
return rawConn, nil
|
||||
|
|
@ -207,6 +207,7 @@ func (c *HTTPConnectDialer) DialContext(ctx context.Context, network, address st
|
|||
tlsConf := tls.Config{
|
||||
NextProtos: []string{"h2", "http/1.1"},
|
||||
ServerName: c.ProxyURL.Hostname(),
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
tlsConn, err := tls.Dial(network, c.ProxyURL.Host, &tlsConf)
|
||||
if err != nil {
|
||||
|
|
@ -232,13 +233,13 @@ func (c *HTTPConnectDialer) DialContext(ctx context.Context, network, address st
|
|||
t := http2.Transport{}
|
||||
h2clientConn, err := t.NewClientConn(rawConn)
|
||||
if err != nil {
|
||||
rawConn.Close()
|
||||
err = rawConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proxyConn, err := connectHttp2(rawConn, h2clientConn)
|
||||
if err != nil {
|
||||
rawConn.Close()
|
||||
err = rawConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
if c.EnableH2ConnReuse {
|
||||
|
|
@ -249,7 +250,7 @@ func (c *HTTPConnectDialer) DialContext(ctx context.Context, network, address st
|
|||
}
|
||||
return proxyConn, err
|
||||
default:
|
||||
rawConn.Close()
|
||||
_ = rawConn.Close()
|
||||
return nil, errors.New("negotiated unsupported application layer protocol: " +
|
||||
negotiatedProtocol)
|
||||
}
|
||||
|
|
@ -274,8 +275,13 @@ func (h *http2Conn) Write(p []byte) (n int, err error) {
|
|||
}
|
||||
|
||||
func (h *http2Conn) Close() error {
|
||||
h.in.Close()
|
||||
return h.out.Close()
|
||||
inErr := h.in.Close()
|
||||
outErr := h.out.Close()
|
||||
|
||||
if inErr != nil {
|
||||
return inErr
|
||||
}
|
||||
return outErr
|
||||
}
|
||||
|
||||
func (h *http2Conn) CloseConn() error {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
|
@ -44,6 +44,7 @@ func TestGETAuthWrongProbeResist(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if responseProbeResist.StatusCode != responseReference.StatusCode {
|
||||
t.Fatalf("Expected response: %d, Got: %d\n",
|
||||
responseReference.StatusCode, responseProbeResist.StatusCode)
|
||||
|
|
@ -314,6 +315,7 @@ func responsesAreEqual(res1, res2 *http.Response) error {
|
|||
if len(s1) != len(s2) {
|
||||
return fmt.Sprintf("different length: %d vs %d", len(s1), len(s2))
|
||||
}
|
||||
|
||||
for i := range s1 {
|
||||
if 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) {
|
||||
return errors.New("Headers have different length")
|
||||
}
|
||||
|
||||
for k1, v1 := range res1.Header {
|
||||
k1Lower := strings.ToLower(k1)
|
||||
if k1Lower == "date" {
|
||||
|
|
@ -339,21 +342,13 @@ func responsesAreEqual(res1, res2 *http.Response) error {
|
|||
if !ok {
|
||||
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 != "" {
|
||||
return fmt.Errorf("header \"%s\" is different: %s", k1, errStr)
|
||||
}
|
||||
}
|
||||
// Compare bodies
|
||||
buf1, err1 := ioutil.ReadAll(res1.Body)
|
||||
buf2, err2 := ioutil.ReadAll(res2.Body)
|
||||
buf1, err1 := io.ReadAll(res1.Body)
|
||||
buf2, err2 := io.ReadAll(res2.Body)
|
||||
n1 := len(buf1)
|
||||
n2 := len(buf2)
|
||||
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.
|
||||
// To avoid false positives - remove addresses before comparing.
|
||||
func removeAddressesByte(b []byte) []byte {
|
||||
b = bytes.Replace(b, []byte(caddyForwardProxyProbeResist.addr),
|
||||
bytes.Repeat([]byte{'#'}, len(caddyForwardProxyProbeResist.addr)), -1)
|
||||
b = bytes.Replace(b, []byte(caddyDummyProbeResist.addr),
|
||||
bytes.Repeat([]byte{'#'}, len(caddyDummyProbeResist.addr)), -1)
|
||||
b = bytes.ReplaceAll(b, []byte(caddyForwardProxyProbeResist.addr),
|
||||
bytes.Repeat([]byte{'#'}, len(caddyForwardProxyProbeResist.addr)))
|
||||
b = bytes.ReplaceAll(b, []byte(caddyDummyProbeResist.addr),
|
||||
bytes.Repeat([]byte{'#'}, len(caddyDummyProbeResist.addr)))
|
||||
return b
|
||||
}
|
||||
|
||||
func removeAddressesStr(s string) string {
|
||||
return string(removeAddressesByte([]byte(s)))
|
||||
}
|
||||
|
||||
func changePort(inputAddr, toPort string) string {
|
||||
host, _, err := net.SplitHostPort(inputAddr)
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue