diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..70e7af43 --- /dev/null +++ b/.github/dependabot.yml @@ -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" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..4ae74b30 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,61 @@ +name: Lint + +on: + push: + branches: + - master + - main + paths-ignore: + - "**.md" + pull_request: + branches: + - master + - main + paths-ignore: + - "**.md" + +permissions: + contents: read + +jobs: + # From https://github.com/golangci/golangci-lint-action + golangci: + permissions: + contents: read # for actions/checkout to fetch code + pull-requests: read # for golangci/golangci-lint-action to fetch pull requests + name: lint + strategy: + matrix: + os: + - linux + - windows + + include: + - os: linux + OS_LABEL: ubuntu-latest + + - os: windows + OS_LABEL: windows-latest + runs-on: ${{ matrix.OS_LABEL }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: '~1.22.0' + check-latest: true + cache: false + + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.55.2 + args: --timeout 10m + + govulncheck: + runs-on: ubuntu-latest + steps: + - name: govulncheck + uses: golang/govulncheck-action@v1 + with: + go-version-input: '~1.22.0' + check-latest: true \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..4b45d923 --- /dev/null +++ b/.github/workflows/tests.yml @@ -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: + - 1.21.x + - 1.22.x + platform: + - ubuntu-latest + - windows-latest + runs-on: ${{ matrix.platform }} + steps: + - name: Fetch Repository + uses: actions/checkout@v4 + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: '${{ matrix.go-version }}' + - name: Run test + run: go test -v -race ./... diff --git a/.golanci-lint.yml b/.golanci-lint.yml new file mode 100644 index 00000000..d144395d --- /dev/null +++ b/.golanci-lint.yml @@ -0,0 +1,168 @@ +linters-settings: + errcheck: + ignore: fmt:.*,go.uber.org/zap/zapcore:^Add.* + ignoretests: true + gci: + sections: + - standard # Standard section: captures all standard packages. + - default # Default section: contains all imports that could not be matched to another section type. + - prefix(github.com/caddyserver/caddy/v2/cmd) # ensure that this is always at the top and always has a line break. + - prefix(github.com/caddyserver/caddy) # Custom section: groups all imports with the specified Prefix. + # Skip generated files. + # Default: true + skip-generated: true + # Enable custom order of sections. + # If `true`, make the section order the same as the order of `sections`. + # Default: false + custom-order: true + exhaustive: + ignore-enum-types: reflect.Kind|svc.Cmd + +linters: + disable-all: true + enable: + - asasalint + - asciicheck + - bidichk + - bodyclose + - decorder + - dogsled + - dupl + - dupword + - durationcheck + - errcheck + - errname + - exhaustive + - exportloopref + - gci + - gofmt + - goimports + - gofumpt + - gosec + - gosimple + - govet + - ineffassign + - importas + - misspell + - prealloc + - promlinter + - sloglint + - sqlclosecheck + - staticcheck + - tenv + - testableexamples + - testifylint + - tparallel + - typecheck + - unconvert + - unused + - wastedassign + - whitespace + - zerologlint + # these are implicitly disabled: + # - containedctx + # - contextcheck + # - cyclop + # - depguard + # - errchkjson + # - errorlint + # - exhaustruct + # - execinquery + # - exhaustruct + # - forbidigo + # - forcetypeassert + # - funlen + # - ginkgolinter + # - gocheckcompilerdirectives + # - gochecknoglobals + # - gochecknoinits + # - gochecksumtype + # - gocognit + # - goconst + # - gocritic + # - gocyclo + # - godot + # - godox + # - goerr113 + # - goheader + # - gomnd + # - gomoddirectives + # - gomodguard + # - goprintffuncname + # - gosmopolitan + # - grouper + # - inamedparam + # - interfacebloat + # - ireturn + # - lll + # - loggercheck + # - maintidx + # - makezero + # - mirror + # - musttag + # - nakedret + # - nestif + # - nilerr + # - nilnil + # - nlreturn + # - noctx + # - nolintlint + # - nonamedreturns + # - nosprintfhostport + # - paralleltest + # - perfsprint + # - predeclared + # - protogetter + # - reassign + # - revive + # - rowserrcheck + # - stylecheck + # - tagalign + # - tagliatelle + # - testpackage + # - thelper + # - unparam + # - usestdlibvars + # - varnamelen + # - wrapcheck + # - wsl + +run: + # default concurrency is a available CPU number. + # concurrency: 4 # explicitly omit this value to fully utilize available resources. + deadline: 5m + issues-exit-code: 1 + tests: false + +# output configuration options +output: + format: 'colored-line-number' + print-issued-lines: true + print-linter-name: true + +issues: + exclude-rules: + # we aren't calling unknown URL + - text: 'G107' # G107: Url provided to HTTP request as taint input + linters: + - gosec + # as a web server that's expected to handle any template, this is totally in the hands of the user. + - text: 'G203' # G203: Use of unescaped data in HTML templates + linters: + - gosec + # we're shelling out to known commands, not relying on user-defined input. + - text: 'G204' # G204: Audit use of command execution + linters: + - gosec + # the choice of weakrand is deliberate, hence the named import "weakrand" + - path: modules/caddyhttp/reverseproxy/selectionpolicies.go + text: 'G404' # G404: Insecure random number source (rand) + linters: + - gosec + - path: modules/caddyhttp/reverseproxy/streaming.go + text: 'G404' # G404: Insecure random number source (rand) + linters: + - gosec + - path: modules/logging/filters.go + linters: + - dupl diff --git a/.golangci.yml b/.golangci.yml deleted file mode 100644 index 92a538e4..00000000 --- a/.golangci.yml +++ /dev/null @@ -1,4 +0,0 @@ -# golangci linter currently requires a config file to -# disable checking of the test files -run: - tests: false diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5b4649eb..00000000 --- a/.travis.yml +++ /dev/null @@ -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 diff --git a/README.md b/README.md index 7081026a..4c7fb8b1 100644 --- a/README.md +++ b/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. + + + + + + + + -[![Build Status](https://travis-ci.org/caddyserver/forwardproxy.svg?branch=master)](https://travis-ci.org/caddyserver/forwardproxy) -[![Join the chat at https://gitter.im/forwardproxy/Lobby](https://badges.gitter.im/forwardproxy/Lobby.svg)](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. @@ -14,31 +88,36 @@ For a complete list of features and their usage, see Caddyfile syntax: ## Caddyfile Syntax (Server Configuration) -The simplest way to enable the forward proxy without authentication just include the `forwardproxy` directive in your Caddyfile. However, this allows anyone to use your server as a proxy, which might not be desirable. +The simplest way to enable the forward proxy without authentication just include the `forward_proxy` directive in your Caddyfile. However, this allows anyone to use your server as a proxy, which might not be desirable. -Open a block for more control; here's an example of all properties in use (note that the syntax is subject to change): +The `forward_proxy` directive has no default order and must be used within a `route` directive to explicitly specify its order of evaluation. In the Caddyfile the addresses must start with `:443` for the `forward_proxy` to work for proxy requests of all origins. + +Here's an example of all properties in use (note that the syntax is subject to change): ``` -forwardproxy { - 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 - response_timeout 30 - 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 - allowfile /path/to/whitelist.txt - denyfile /path/to/blacklist.txt - allow all - deny all # unreachable rule, remaining requests are matched by `allow all` above - } +:443, example.com +route { + 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 } ``` @@ -46,16 +125,16 @@ forwardproxy { ##### Security -- **basicauth [user] [password]** -Sets basic HTTP auth credentials. This property may be repeated multiple times. Note that this is different from Caddy's built-in `basicauth` directive. BE SURE TO CHECK THE NAME OF THE SITE THAT IS REQUESTING CREDENTIALS BEFORE YOU ENTER THEM. +- **basic_auth [user] [password]** +Sets basic HTTP auth credentials. This property may be repeated multiple times. Note that this is different from Caddy's built-in `basic_auth` directive. BE SURE TO CHECK THE NAME OF THE SITE THAT IS REQUESTING CREDENTIALS BEFORE YOU ENTER THEM. _Default: no authentication required._ - **probe_resistance [secretlink.tld]** Attempts to hide the fact that the site is a forward proxy. Proxy will no longer respond with "407 Proxy Authentication Required" if credentials are incorrect or absent, and will attempt to mimic a generic Caddy web server as if the forward proxy is not enabled. -Probing resistance works (and makes sense) only if basicauth is set up. -To use your proxy with probe resistance, supply your basicauth credentials to your client configuration. +Probing resistance works (and makes sense) only if `basic_auth` is set up. +To use your proxy with probe resistance, supply your `basic_auth` credentials to your client configuration. If your proxy client(browser, operating system, browser extension, etc) allows you to preconfigure credentials, and sends credentials preemptively, you do not need secret link. If your proxy client does not preemptively send credentials, you will have to visit your secret link in your browser to trigger the authentication. @@ -90,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]...** - - **allowfile /path/to/whitelist.txt** - - **deny [ip or subnet or hostname] [ip or subnet or hostname]...** - - **denyfile /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 `allowfile`/`denyfile` 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. + - **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. _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 @@ -117,10 +196,6 @@ _Default deny rules intend to prohibit access to localhost and local networks an ##### Timeouts -- **response_timeout [integer]** -Sets timeout (in seconds) to get full response for HTTP requests made by proxy on behalf of users (does not affect `CONNECT`-method requests). -_Default: no timeout._ - - **dial_timeout [integer]** Sets timeout (in seconds) for establishing TCP connection to target website. Affects all requests. _Default: 20 seconds._ @@ -146,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 diff --git a/acl.go b/acl.go index 99ad596b..c3abf7f9 100644 --- a/acl.go +++ b/acl.go @@ -6,6 +6,12 @@ import ( "strings" ) +// ACLRule describes an ACL rule. +type ACLRule struct { + Subjects []string `json:"subjects,omitempty"` + Allow bool `json:"allow,omitempty"` +} + type aclDecision uint8 const ( @@ -31,7 +37,6 @@ func (a *aclIPRule) tryMatch(ip net.IP, domain string) aclDecision { return aclDecisionAllow } return aclDecisionDeny - } type aclDomainRule struct { @@ -41,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 { @@ -65,7 +69,7 @@ func (a *aclAllRule) tryMatch(ip net.IP, domain string) aclDecision { return aclDecisionDeny } -func newAclRule(ruleSubject string, allow bool) (aclRule, error) { +func newACLRule(ruleSubject string, allow bool) (aclRule, error) { if ruleSubject == "all" { return &aclAllRule{allow: allow}, nil } @@ -94,3 +98,25 @@ func newAclRule(ruleSubject string, allow bool) (aclRule, error) { } return &aclDomainRule{domain: ruleSubject, subdomainsAllowed: subdomainsAllowed, allow: allow}, nil } + +// isValidDomainLite shamelessly rejects non-LDH names. returns nil if domains seems valid +func isValidDomainLite(domain string) error { + for i := 0; i < len(domain); i++ { + c := domain[i] + if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || '0' <= c && c <= '9' || + c == '-' || c == '.' { + continue + } + return errors.New("character " + string(c) + " is not allowed") + } + sections := strings.Split(domain, ".") + for _, s := range sections { + if len(s) == 0 { + return errors.New("empty section between dots in domain name or trailing dot") + } + if len(s) > 63 { + return errors.New("domain name section is too long") + } + } + return nil +} diff --git a/acl_test.go b/acl_test.go index 693a6267..50e9d651 100644 --- a/acl_test.go +++ b/acl_test.go @@ -9,15 +9,14 @@ import ( test port blocking working test blacklist allowed test blacklist refused with correct status - */ func TestWhitelistAllowing(t *testing.T) { - useTls := true - for _, httpProxyVer := range testHttpProxyVersions { + const useTLS = true + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { response, err := getViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyWhiteListing.addr, httpProxyVer, - "", useTls) + "", useTLS) if err != nil { t.Fatal(err) } else if err = responseExpected(response, caddyTestTarget.contents[resource]); err != nil { @@ -28,11 +27,11 @@ func TestWhitelistAllowing(t *testing.T) { } func TestWhitelistBlocking(t *testing.T) { - useTls := true - for _, httpProxyVer := range testHttpProxyVersions { + const useTLS = true + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { response, err := getViaProxy(caddyHTTPTestTarget.addr, resource, caddyForwardProxyWhiteListing.addr, httpProxyVer, - "", useTls) + "", useTLS) if err != nil { t.Fatal(err) } else if response.StatusCode != http.StatusForbidden { @@ -41,10 +40,10 @@ func TestWhitelistBlocking(t *testing.T) { } } - for _, httpProxyVer := range testHttpProxyVersions { + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { response, err := getViaProxy("google.com:6451", resource, caddyForwardProxyWhiteListing.addr, httpProxyVer, - "", useTls) + "", useTLS) if err != nil { t.Fatal(err) } else if response.StatusCode != http.StatusForbidden { @@ -55,11 +54,11 @@ func TestWhitelistBlocking(t *testing.T) { } func TestLocalhostDefaultForbidden(t *testing.T) { - useTls := true - for _, httpProxyVer := range testHttpProxyVersions { + const useTLS = true + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { response, err := getViaProxy("localhost:6451", resource, caddyForwardProxyNoBlacklistOverride.addr, httpProxyVer, - "", useTls) + "", useTLS) if err != nil { t.Fatal(err) } else if response.StatusCode != http.StatusForbidden { @@ -68,10 +67,10 @@ func TestLocalhostDefaultForbidden(t *testing.T) { } } - for _, httpProxyVer := range testHttpProxyVersions { + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { response, err := getViaProxy("127.0.0.1:808", resource, caddyForwardProxyNoBlacklistOverride.addr, httpProxyVer, - "", useTls) + "", useTLS) if err != nil { t.Fatal(err) } else if response.StatusCode != http.StatusForbidden { @@ -80,10 +79,10 @@ func TestLocalhostDefaultForbidden(t *testing.T) { } } - for _, httpProxyVer := range testHttpProxyVersions { + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { response, err := getViaProxy("[::1]:8080", resource, caddyForwardProxyNoBlacklistOverride.addr, httpProxyVer, - "", useTls) + "", useTLS) if err != nil { t.Fatal(err) } else if response.StatusCode != http.StatusForbidden { @@ -94,11 +93,11 @@ func TestLocalhostDefaultForbidden(t *testing.T) { } func TestLocalNetworksDefaultForbidden(t *testing.T) { - useTls := true - for _, httpProxyVer := range testHttpProxyVersions { + const useTLS = true + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { response, err := getViaProxy("10.0.0.0:80", resource, caddyForwardProxyNoBlacklistOverride.addr, httpProxyVer, - "", useTls) + "", useTLS) if err != nil { t.Fatal(err) } else if response.StatusCode != http.StatusForbidden { @@ -107,10 +106,10 @@ func TestLocalNetworksDefaultForbidden(t *testing.T) { } } - for _, httpProxyVer := range testHttpProxyVersions { + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { response, err := getViaProxy("127.222.34.1:443", resource, caddyForwardProxyNoBlacklistOverride.addr, httpProxyVer, - "", useTls) + "", useTLS) if err != nil { t.Fatal(err) } else if response.StatusCode != http.StatusForbidden { @@ -119,10 +118,10 @@ func TestLocalNetworksDefaultForbidden(t *testing.T) { } } - for _, httpProxyVer := range testHttpProxyVersions { + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { response, err := getViaProxy("172.16.0.1:8080", resource, caddyForwardProxyNoBlacklistOverride.addr, httpProxyVer, - "", useTls) + "", useTLS) if err != nil { t.Fatal(err) } else if response.StatusCode != http.StatusForbidden { @@ -131,10 +130,10 @@ func TestLocalNetworksDefaultForbidden(t *testing.T) { } } - for _, httpProxyVer := range testHttpProxyVersions { + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { response, err := getViaProxy("192.168.192.168:888", resource, caddyForwardProxyNoBlacklistOverride.addr, httpProxyVer, - "", useTls) + "", useTLS) if err != nil { t.Fatal(err) } else if response.StatusCode != http.StatusForbidden { @@ -145,11 +144,11 @@ func TestLocalNetworksDefaultForbidden(t *testing.T) { } func TestBlacklistBlocking(t *testing.T) { - useTls := true - for _, httpProxyVer := range testHttpProxyVersions { + const useTLS = true + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { response, err := getViaProxy(blacklistedDomain, resource, caddyForwardProxyBlackListing.addr, httpProxyVer, - "", useTls) + "", useTLS) if err != nil { t.Fatal(err) } else if response.StatusCode != http.StatusForbidden { @@ -158,10 +157,10 @@ func TestBlacklistBlocking(t *testing.T) { } } - for _, httpProxyVer := range testHttpProxyVersions { + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { response, err := getViaProxy(blacklistedIPv4, resource, caddyForwardProxyBlackListing.addr, httpProxyVer, - "", useTls) + "", useTLS) if err != nil { t.Fatal(err) } else if response.StatusCode != http.StatusForbidden { @@ -170,10 +169,10 @@ func TestBlacklistBlocking(t *testing.T) { } } - for _, httpProxyVer := range testHttpProxyVersions { + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { response, err := getViaProxy("["+blacklistedIPv6+"]:80", resource, caddyForwardProxyBlackListing.addr, httpProxyVer, - "", useTls) + "", useTLS) if err != nil { t.Fatal(err) } else if response.StatusCode != http.StatusForbidden { @@ -184,11 +183,11 @@ func TestBlacklistBlocking(t *testing.T) { } func TestBlacklistAllowing(t *testing.T) { - useTls := true - for _, httpProxyVer := range testHttpProxyVersions { + const useTLS = true + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { response, err := getViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyBlackListing.addr, httpProxyVer, - "", useTls) + "", useTLS) if err != nil { t.Fatal(err) } else if err = responseExpected(response, caddyTestTarget.contents[resource]); err != nil { diff --git a/caddyfile.go b/caddyfile.go new file mode 100644 index 00000000..239b5c70 --- /dev/null +++ b/caddyfile.go @@ -0,0 +1,187 @@ +package forwardproxy + +import ( + "encoding/base64" + "log" + "strconv" + "strings" + + 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" +) + +func init() { + httpcaddyfile.RegisterHandlerDirective("forward_proxy", parseCaddyfile) +} + +func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { + var fp Handler + err := fp.UnmarshalCaddyfile(h.Dispenser) + return &fp, err +} + +// EncodeAuthCredentials base64-encode credentials +func EncodeAuthCredentials(user, pass string) (result []byte) { + raw := []byte(user + ":" + pass) + result = make([]byte, base64.StdEncoding.EncodedLen(len(raw))) + base64.StdEncoding.Encode(result, raw) + return +} + +// UnmarshalCaddyfile unmarshals Caddyfile tokens into h. +func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + if !d.Next() { + return d.ArgErr() + } + args := d.RemainingArgs() + if len(args) > 0 { + return d.ArgErr() + } + for nesting := d.Nesting(); d.NextBlock(nesting); { + subdirective := d.Val() + args := d.RemainingArgs() + switch subdirective { + case "basic_auth": + if len(args) != 2 { + return d.ArgErr() + } + if len(args[0]) == 0 { + return d.Err("empty usernames are not allowed") + } + // TODO: Evaluate policy of allowing empty passwords. + if strings.Contains(args[0], ":") { + return d.Err("character ':' in usernames is not allowed") + } + if h.AuthCredentials == nil { + h.AuthCredentials = [][]byte{} + } + h.AuthCredentials = append(h.AuthCredentials, EncodeAuthCredentials(args[0], args[1])) + case "hosts": + if len(args) == 0 { + return d.ArgErr() + } + if len(h.Hosts) != 0 { + return d.Err("hosts subdirective specified twice") + } + h.Hosts = caddyhttp.MatchHost(args) + case "ports": + if len(args) == 0 { + return d.ArgErr() + } + if len(h.AllowedPorts) != 0 { + return d.Err("ports subdirective specified twice") + } + h.AllowedPorts = make([]int, len(args)) + for i, p := range args { + intPort, err := strconv.Atoi(p) + if intPort <= 0 || intPort > 65535 || err != nil { + return d.Errf("ports are expected to be space-separated and in 0-65535 range, but got: %s", p) + } + h.AllowedPorts[i] = intPort + } + case "hide_ip": + if len(args) != 0 { + return d.ArgErr() + } + h.HideIP = true + case "hide_via": + if len(args) != 0 { + return d.ArgErr() + } + h.HideVia = true + case "probe_resistance": + if len(args) > 1 { + return d.ArgErr() + } + if len(args) == 1 { + lowercaseArg := strings.ToLower(args[0]) + if lowercaseArg != args[0] { + log.Println("[WARNING] Secret domain appears to have uppercase letters in it, which are not visitable") + } + h.ProbeResistance = &ProbeResistance{Domain: args[0]} + } else { + h.ProbeResistance = &ProbeResistance{} + } + case "serve_pac": + if len(args) > 1 { + return d.ArgErr() + } + if len(h.PACPath) != 0 { + return d.Err("serve_pac subdirective specified twice") + } + if len(args) == 1 { + h.PACPath = args[0] + if !strings.HasPrefix(h.PACPath, "/") { + h.PACPath = "/" + h.PACPath + } + } else { + h.PACPath = "/proxy.pac" + } + case "dial_timeout": + if len(args) != 1 { + return d.ArgErr() + } + timeout, err := caddy.ParseDuration(args[0]) + if err != nil { + return d.ArgErr() + } + if timeout < 0 { + return d.Err("dial_timeout cannot be negative.") + } + h.DialTimeout = caddy.Duration(timeout) + case "upstream": + if len(args) != 1 { + return d.ArgErr() + } + if h.Upstream != "" { + return d.Err("upstream directive specified more than once") + } + h.Upstream = args[0] + case "acl": + for nesting := d.Nesting(); d.NextBlock(nesting); { + aclDirective := d.Val() + args := d.RemainingArgs() + if len(args) == 0 { + return d.ArgErr() + } + var ruleSubjects []string + var err error + aclAllow := false + switch aclDirective { + case "allow": + ruleSubjects = args + aclAllow = true + case "allow_file": + if len(args) != 1 { + return d.Err("allowfile accepts a single filename argument") + } + ruleSubjects, err = readLinesFromFile(args[0]) + if err != nil { + return err + } + aclAllow = true + case "deny": + ruleSubjects = args + case "deny_file": + if len(args) != 1 { + return d.Err("denyfile accepts a single filename argument") + } + ruleSubjects, err = readLinesFromFile(args[0]) + if err != nil { + return err + } + default: + return d.Err("expected acl directive: allow/allowfile/deny/denyfile." + + "got: " + aclDirective) + } + ar := ACLRule{Subjects: ruleSubjects, Allow: aclAllow} + h.ACL = append(h.ACL, ar) + } + default: + return d.ArgErr() + } + } + return nil +} diff --git a/cmd/caddy/caddy.go b/cmd/caddy/caddy.go deleted file mode 100644 index 87c3197d..00000000 --- a/cmd/caddy/caddy.go +++ /dev/null @@ -1,12 +0,0 @@ -package main - -import ( - "github.com/caddyserver/caddy/caddy/caddymain" - - _ "github.com/caddyserver/forwardproxy" -) - -func main() { - caddymain.EnableTelemetry = false - caddymain.Run() -} diff --git a/common_test.go b/common_test.go index 631b064e..8f1341bb 100644 --- a/common_test.go +++ b/common_test.go @@ -1,34 +1,39 @@ package forwardproxy import ( + "context" "crypto/tls" - "encoding/hex" - "fmt" - "io" - "io/ioutil" + "encoding/json" + "net" "net/http" - "net/http/httputil" "os" - "strings" + "strconv" "testing" "time" - "github.com/caddyserver/caddy" + "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig" + "github.com/caddyserver/caddy/v2/modules/caddyhttp" + "github.com/caddyserver/caddy/v2/modules/caddyhttp/fileserver" + "github.com/caddyserver/caddy/v2/modules/caddypki" + "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} @@ -36,28 +41,32 @@ 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 { - *caddy.Instance - addr string // could be http or https - - HTTPRedirectPort string // used in probe-resist tests to simulate default Caddy's http->https redirect - root string // expected to have index.html and pic.png - directives []string - proxyEnabled bool - proxyDirectives []string - contents map[string][]byte + addr string + tls bool + + httpRedirPort string // used in probe-resist tests to simulate default Caddy's http->https redirect + + root string // expected to have index.html and pic.png + _ []string + proxyHandler *Handler + contents map[string][]byte } var ( @@ -78,137 +87,281 @@ var ( caddyHTTPTestTarget caddyTestServer // serves plain http on 6480 ) -func (c *caddyTestServer) marshal() []byte { - mainBlock := []string{c.addr + " {", - "root " + c.root} - mainBlock = append(mainBlock, c.directives...) - if c.proxyEnabled { - if len(c.proxyDirectives) == 0 { - mainBlock = append(mainBlock, "forwardproxy") - } else { - forwardProxyBlock := []string{"forwardproxy {"} - forwardProxyBlock = append(forwardProxyBlock, strings.Join(c.proxyDirectives, "\n")) - forwardProxyBlock = append(forwardProxyBlock, "}") - mainBlock = append(mainBlock, strings.Join(forwardProxyBlock, "\n")) +func (c *caddyTestServer) server() *caddyhttp.Server { + host, port, err := net.SplitHostPort(c.addr) + if err != nil { + panic(err) + } + + handlerJSON := func(h caddyhttp.MiddlewareHandler) json.RawMessage { + return caddyconfig.JSONModuleObject(h, "handler", h.(caddy.Module).CaddyModule().ID.Name(), nil) + } + + // create the routes + var routes caddyhttp.RouteList + if c.tls { + // cheap hack for our tests to get TLS certs for the hostnames that + // it needs TLS certs for: create an empty route with a single host + // matcher for that hostname, and auto HTTPS will do the rest + hostMatcherJSON, err := json.Marshal(caddyhttp.MatchHost{host}) + if err != nil { + panic(err) + } + matchersRaw := caddyhttp.RawMatcherSets{ + caddy.ModuleMap{"host": hostMatcherJSON}, } + routes = append(routes, caddyhttp.Route{MatcherSetsRaw: matchersRaw}) + } + if c.proxyHandler != nil { + if host != "" { + // tell the proxy which hostname to serve the proxy on; this must + // be distinct from the host matcher, since the proxy basically + // does its own host matching + c.proxyHandler.Hosts = caddyhttp.MatchHost{host} + } + routes = append(routes, caddyhttp.Route{ + HandlersRaw: []json.RawMessage{handlerJSON(c.proxyHandler)}, + }) + } + if c.root != "" { + routes = append(routes, caddyhttp.Route{ + HandlersRaw: []json.RawMessage{ + handlerJSON(&fileserver.FileServer{Root: c.root}), + }, + }) } - mainBlock = append(mainBlock, "}") - if len(c.HTTPRedirectPort) > 0 { - // TODO: this is not good enough, since `func redirPlaintextHost(cfg *SiteConfig) *SiteConfig` - // https://github.com/caddyserver/caddy/blob/master/caddyhttp/httpserver/https.go#L142 can change in future - // and we won't know. - redirectBlock := []string{"http://*:" + c.HTTPRedirectPort + " {", - "redir https://" + c.addr + "{uri}", - "header / Connection close", - "}"} - mainBlock = append(mainBlock, redirectBlock...) - } - return []byte(strings.Join(mainBlock, "\n")) -} -func (c *caddyTestServer) StartTestServer() { - var err error - c.Instance, err = caddy.Start(caddy.CaddyfileInput{Contents: c.marshal(), ServerTypeName: "http"}) - if err != nil { - panic(err) + srv := &caddyhttp.Server{ + Listen: []string{":" + port}, + Routes: routes, + } + if c.tls { + srv.TLSConnPolicies = caddytls.ConnectionPolicies{{}} + } else { + srv.AutoHTTPS = &caddyhttp.AutoHTTPSConfig{Disabled: true} } + 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) } + + return srv +} + +// 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}, + Routes: caddyhttp.RouteList{ + { + Handlers: []caddyhttp.MiddlewareHandler{ + caddyhttp.StaticResponse{ + StatusCode: caddyhttp.WeakString(strconv.Itoa(http.StatusPermanentRedirect)), + Headers: http.Header{ + "Location": []string{"https://" + c.addr + "/{http.request.uri}"}, + "Connection": []string{"close"}, + }, + Close: true, + }, + }, + }, + }, + } } func TestMain(m *testing.M) { - caddyForwardProxy = caddyTestServer{addr: "127.0.19.84:1984", root: "./test/forwardproxy", - directives: []string{"tls self_signed"}, - proxyEnabled: true, proxyDirectives: []string{"serve_pac", - "acl {\nallow all\n}"}} - caddyForwardProxy.StartTestServer() - - caddyForwardProxyAuth = caddyTestServer{addr: "127.0.0.1:4891", root: "./test/forwardproxy", - directives: []string{"tls self_signed"}, - proxyEnabled: true, proxyDirectives: []string{"basicauth test pass", - "acl {\nallow all\n}"}} - caddyForwardProxyAuth.StartTestServer() - - caddyHTTPForwardProxyAuth = caddyTestServer{addr: "127.0.69.73:6973", root: "./test/forwardproxy", - directives: []string{"tls off"}, - proxyEnabled: true, proxyDirectives: []string{"basicauth test pass", - "acl {\nallow all\n}"}} - caddyHTTPForwardProxyAuth.StartTestServer() - - caddyForwardProxyProbeResist = caddyTestServer{addr: "127.0.88.88:8888", root: "./test/forwardproxy", - directives: []string{"tls self_signed"}, HTTPRedirectPort: "8880", - proxyEnabled: true, proxyDirectives: []string{"basicauth test pass", - "probe_resistance test.localhost", - "serve_pac superhiddenfile.pac", - "acl {\nallow all\n}"}} - caddyForwardProxyProbeResist.StartTestServer() - - caddyDummyProbeResist = caddyTestServer{addr: "127.0.99.99:9999", root: "./test/forwardproxy", - directives: []string{"tls self_signed"}, HTTPRedirectPort: "9980", - proxyEnabled: false} - caddyDummyProbeResist.StartTestServer() - - // 127.0.0.1 and localhost are both used to avoid Caddy matching and routing proxy requests internally - caddyTestTarget = caddyTestServer{addr: "127.0.64.51:6451", root: "./test/index", - directives: []string{}, - proxyEnabled: false} - caddyTestTarget.StartTestServer() - - caddyHTTPTestTarget = caddyTestServer{addr: "localhost:6480", root: "./test/index", - directives: []string{"tls off"}, - proxyEnabled: false} - caddyHTTPTestTarget.StartTestServer() - - caddyAuthedUpstreamEnter = caddyTestServer{addr: "127.0.65.25:6585", root: "./test/upstreamingproxy", - directives: []string{"tls self_signed"}, - proxyEnabled: true, proxyDirectives: []string{"upstream https://test:pass@127.0.0.1:4891", - "basicauth upstreamtest upstreampass"}} - caddyAuthedUpstreamEnter.StartTestServer() - - caddyForwardProxyWhiteListing = caddyTestServer{addr: "127.0.87.76:8776", root: "./test/forwardproxy", - directives: []string{"tls self_signed"}, - proxyEnabled: true, proxyDirectives: []string{"acl {\nallow 127.0.64.51\n deny all\n}", - "ports 6451"}} - caddyForwardProxyWhiteListing.StartTestServer() - - caddyForwardProxyBlackListing = caddyTestServer{addr: "127.0.66.76:6676", root: "./test/forwardproxy", - directives: []string{"tls self_signed"}, - proxyEnabled: true, proxyDirectives: []string{"acl {\ndeny " + blacklistedIPv4 + "/30\n" + - "deny " + blacklistedIPv6 + "\nallow all\n}"}, - } - caddyForwardProxyBlackListing.StartTestServer() - - caddyForwardProxyNoBlacklistOverride = caddyTestServer{addr: "127.0.66.79:6679", root: "./test/forwardproxy", - directives: []string{"tls self_signed"}, - proxyEnabled: true, proxyDirectives: []string{}} - caddyForwardProxyNoBlacklistOverride.StartTestServer() + caddyForwardProxy = caddyTestServer{ + addr: "127.0.19.84:1984", + root: "./test/forwardproxy", + tls: true, + proxyHandler: &Handler{ + PACPath: defaultPACPath, + ACL: []ACLRule{{Allow: true, Subjects: []string{"all"}}}, + }, + } + + caddyForwardProxyAuth = caddyTestServer{ + addr: "127.0.0.1:4891", + root: "./test/forwardproxy", + tls: true, + proxyHandler: &Handler{ + PACPath: defaultPACPath, + ACL: []ACLRule{{Subjects: []string{"all"}, Allow: true}}, + AuthCredentials: [][]byte{EncodeAuthCredentials("test", "pass")}, + }, + } + + caddyHTTPForwardProxyAuth = caddyTestServer{ + addr: "127.0.69.73:6973", + root: "./test/forwardproxy", + proxyHandler: &Handler{ + PACPath: defaultPACPath, + ACL: []ACLRule{{Subjects: []string{"all"}, Allow: true}}, + AuthCredentials: [][]byte{EncodeAuthCredentials("test", "pass")}, + }, + } + + caddyForwardProxyProbeResist = caddyTestServer{ + addr: "127.0.88.88:8888", + root: "./test/forwardproxy", + tls: true, + proxyHandler: &Handler{ + PACPath: "/superhiddenfile.pac", + ACL: []ACLRule{{Subjects: []string{"all"}, Allow: true}}, + ProbeResistance: &ProbeResistance{Domain: "test.localhost"}, + AuthCredentials: [][]byte{EncodeAuthCredentials("test", "pass")}, + }, + httpRedirPort: "8880", + } + + caddyDummyProbeResist = caddyTestServer{ + addr: "127.0.99.99:9999", + root: "./test/forwardproxy", + tls: true, + httpRedirPort: "9980", + } + + caddyTestTarget = caddyTestServer{ + addr: "127.0.64.51:6451", + root: "./test/index", + } + + caddyHTTPTestTarget = caddyTestServer{ + addr: "localhost:6480", + root: "./test/index", + } + + caddyAuthedUpstreamEnter = caddyTestServer{ + addr: "127.0.65.25:6585", + root: "./test/upstreamingproxy", + tls: true, + proxyHandler: &Handler{ + Upstream: "https://test:pass@127.0.0.1:4891", + AuthCredentials: [][]byte{EncodeAuthCredentials("upstreamtest", "upstreampass")}, + }, + } + + caddyForwardProxyWhiteListing = caddyTestServer{ + addr: "127.0.87.76:8776", + root: "./test/forwardproxy", + tls: true, + proxyHandler: &Handler{ + ACL: []ACLRule{ + {Subjects: []string{"127.0.64.51"}, Allow: true}, + {Subjects: []string{"all"}, Allow: false}, + }, + AllowedPorts: []int{6451}, + }, + } + + caddyForwardProxyBlackListing = caddyTestServer{ + addr: "127.0.66.76:6676", + root: "./test/forwardproxy", + tls: true, + proxyHandler: &Handler{ + ACL: []ACLRule{ + {Subjects: []string{blacklistedIPv4 + "/30"}, Allow: false}, + {Subjects: []string{blacklistedIPv6}, Allow: false}, + {Subjects: []string{"all"}, Allow: true}, + }, + }, + } + + caddyForwardProxyNoBlacklistOverride = caddyTestServer{ + addr: "127.0.66.76:6679", + root: "./test/forwardproxy", + tls: true, + proxyHandler: &Handler{}, + } + + // done configuring all the servers; now build the HTTP app + httpApp := caddyhttp.App{ + HTTPPort: 1080, // use a high port to avoid permission issues + Servers: map[string]*caddyhttp.Server{ + "caddyForwardProxy": caddyForwardProxy.server(), + "caddyForwardProxyAuth": caddyForwardProxyAuth.server(), + "caddyHTTPForwardProxyAuth": caddyHTTPForwardProxyAuth.server(), + "caddyForwardProxyProbeResist": caddyForwardProxyProbeResist.server(), + "caddyDummyProbeResist": caddyDummyProbeResist.server(), + "caddyTestTarget": caddyTestTarget.server(), + "caddyHTTPTestTarget": caddyHTTPTestTarget.server(), + "caddyAuthedUpstreamEnter": caddyAuthedUpstreamEnter.server(), + "caddyForwardProxyWhiteListing": caddyForwardProxyWhiteListing.server(), + "caddyForwardProxyBlackListing": caddyForwardProxyBlackListing.server(), + "caddyForwardProxyNoBlacklistOverride": caddyForwardProxyNoBlacklistOverride.server(), + + // HTTP->HTTPS redirect simulation servers for those which have a redir port configured + "caddyForwardProxyProbeResist_redir": caddyForwardProxyProbeResist.redirServer(), + "caddyDummyProbeResist_redir": caddyDummyProbeResist.redirServer(), + }, + GracePeriod: caddy.Duration(1 * time.Second), // keep tests fast + } + httpAppJSON, err := json.Marshal(httpApp) + if err != nil { + panic(err) + } + + // ensure we always use internal issuer and not a public CA + tlsApp := caddytls.TLS{ + Automation: &caddytls.AutomationConfig{ + Policies: []*caddytls.AutomationPolicy{ + { + IssuersRaw: []json.RawMessage{json.RawMessage(`{"module": "internal"}`)}, + }, + }, + }, + } + tlsAppJSON, err := json.Marshal(tlsApp) + if err != nil { + panic(err) + } + + // configure the default CA so that we don't try to install trust, just for our tests + falseBool := false + pkiApp := caddypki.PKI{ + CAs: map[string]*caddypki.CA{ + "local": {InstallTrust: &falseBool}, + }, + } + pkiAppJSON, err := json.Marshal(pkiApp) + if err != nil { + panic(err) + } + + // build final config + cfg := &caddy.Config{ + Admin: &caddy.AdminConfig{Disabled: true}, + AppsRaw: caddy.ModuleMap{ + "http": httpAppJSON, + "tls": tlsAppJSON, + "pki": pkiAppJSON, + }, + } + + // start the engines + err = caddy.Run(cfg) + if err != nil { + panic(err) + } + + // wait server ready for tls dial + time.Sleep(500 * time.Millisecond) retCode := m.Run() - caddyForwardProxy.Stop() - caddyForwardProxyAuth.Stop() - caddyHTTPForwardProxyAuth.Stop() - caddyForwardProxyProbeResist.Stop() - caddyDummyProbeResist.Stop() - caddyTestTarget.Stop() - caddyHTTPTestTarget.Stop() - caddyAuthedUpstreamEnter.Stop() - caddyForwardProxyWhiteListing.Stop() - caddyForwardProxyBlackListing.Stop() - caddyForwardProxyNoBlacklistOverride.Stop() + caddy.Stop() // nolint:errcheck // ignore error on shutdown os.Exit(retCode) } @@ -216,11 +369,7 @@ func TestMain(m *testing.M) { // This is a sanity check confirming that target servers actually directly serve what they are expected to. // (And that they don't serve what they should not) func TestTheTest(t *testing.T) { - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - ResponseHeaderTimeout: 2 * time.Second, - } - client := &http.Client{Transport: tr, Timeout: 2 * time.Second} + client := &http.Client{Transport: testTransport, Timeout: 2 * time.Second} // Request index resp, err := client.Get("http://" + caddyTestTarget.addr) @@ -263,64 +412,20 @@ func TestTheTest(t *testing.T) { } } -func debugIoCopy(dst io.Writer, src io.Reader, prefix string) (written int64, err error) { - buf := make([]byte, 32*1024) - flusher, ok := dst.(http.Flusher) - for { - nr, er := src.Read(buf) - fmt.Printf("[%s] Read err %#v\n%s", prefix, er, hex.Dump(buf[0:nr])) - if nr > 0 { - nw, ew := dst.Write(buf[0:nr]) - if ok { - flusher.Flush() - } - fmt.Printf("[%s] Wrote %v %v\n", prefix, nw, ew) - if nw > 0 { - written += int64(nw) - } - if ew != nil { - err = ew - break - } - if nr != nw { - err = io.ErrShortWrite - break - } - } - if er != nil { - if er != io.EOF { - err = er - } - break - } - } - fmt.Printf("[%s] Returning with %#v %#v\n", prefix, written, err) - return -} - -func httpdump(r interface{}) string { - switch v := r.(type) { - case *http.Request: - if v == nil { - return "httpdump: nil" - } - b, err := httputil.DumpRequest(v, true) +var testTransport = &http.Transport{ + ResponseHeaderTimeout: 2 * time.Second, + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + // always dial localhost for testing purposes + return new(net.Dialer).DialContext(ctx, network, addr) + }, + DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + // always dial localhost for testing purposes + conn, err := new(net.Dialer).DialContext(ctx, network, addr) if err != nil { - return err.Error() - } else { - return string(b) + return nil, err } - case *http.Response: - if v == nil { - return "httpdump: nil" - } - b, err := httputil.DumpResponse(v, true) - if err != nil { - return err.Error() - } else { - return string(b) - } - default: - return "httpdump: wrong type" - } + return tls.Client(conn, &tls.Config{InsecureSkipVerify: true}), nil + }, } + +const defaultPACPath = "/proxy.pac" diff --git a/docker-build/Dockerfile b/docker-build/Dockerfile index 5637e073..ea1f3fd4 100644 --- a/docker-build/Dockerfile +++ b/docker-build/Dockerfile @@ -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" diff --git a/forwardproxy.go b/forwardproxy.go index c30dccb8..b3d73e5c 100644 --- a/forwardproxy.go +++ b/forwardproxy.go @@ -13,6 +13,7 @@ // limitations under the License. // // Caching is purposefully ignored. + package forwardproxy import ( @@ -20,437 +21,665 @@ import ( "bytes" "context" "crypto/subtle" + "crypto/tls" "encoding/base64" "errors" "fmt" "io" - "io/ioutil" "net" "net/http" "net/url" + "os" + "path/filepath" "strconv" "strings" "sync" "time" + "unicode/utf8" - "github.com/caddyserver/caddy/caddyhttp/httpserver" + 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" + "go.uber.org/zap" + "golang.org/x/net/proxy" ) -type ForwardProxy struct { - Next httpserver.Handler +func init() { + caddy.RegisterModule(Handler{}) +} + +// Handler implements a forward proxy. +// +// EXPERIMENTAL: This handler is still experimental and subject to breaking changes. +type Handler struct { + logger *zap.Logger + + // Filename of the PAC file to serve. + PACPath string `json:"pac_path,omitempty"` + + // If true, the Forwarded header will not be augmented with your IP address. + HideIP bool `json:"hide_ip,omitempty"` + + // If true, the Via header will not be added. + HideVia bool `json:"hide_via,omitempty"` - authRequired bool - authCredentials [][]byte // slice with base64-encoded credentials + // Host(s) (and ports) of the proxy. When you configure a client, + // you will give it the host (and port) of the proxy to use. + Hosts caddyhttp.MatchHost `json:"hosts,omitempty"` - hideIP bool - hideVia bool + // Optional probe resistance. (See documentation.) + ProbeResistance *ProbeResistance `json:"probe_resistance,omitempty"` - pacFilePath string + // How long to wait before timing out initial TCP connections. + DialTimeout caddy.Duration `json:"dial_timeout,omitempty"` - hostname string // do not intercept requests to the hostname (except for hidden link) - port string // port on which chain with forwardproxy is listening on + // Optionally configure an upstream proxy to use. + Upstream string `json:"upstream,omitempty"` - probeResistDomain string - probeResistEnabled bool + // Access control list. + ACL []ACLRule `json:"acl,omitempty"` - dialTimeout time.Duration // for initial tcp connection + // Ports to be allowed to connect to (if non-empty). + AllowedPorts []int `json:"allowed_ports,omitempty"` - httpTransport http.Transport + httpTransport *http.Transport - // overridden dialContext allow to redirect requests to upstream proxy + // overridden dialContext allows us to redirect requests to upstream proxy dialContext func(ctx context.Context, network, address string) (net.Conn, error) upstream *url.URL // address of upstream proxy - aclRules []aclRule - whitelistedPorts []int + aclRules []aclRule + + // TODO: temporary/deprecated - we should try to reuse existing authentication modules instead! + AuthCredentials [][]byte `json:"auth_credentials,omitempty"` // slice with base64-encoded credentials } -var bufferPool sync.Pool +// CaddyModule returns the Caddy module information. +func (Handler) CaddyModule() caddy.ModuleInfo { + return caddy.ModuleInfo{ + ID: "http.handlers.forward_proxy", + New: func() caddy.Module { return new(Handler) }, + } +} -func (fp *ForwardProxy) hostIsAllowed(hostname string, ip net.IP) bool { - for _, rule := range fp.aclRules { - switch rule.tryMatch(ip, hostname) { - case aclDecisionDeny: - return false - case aclDecisionAllow: - return true +// Provision ensures that h is set up properly before use. +func (h *Handler) Provision(ctx caddy.Context) error { + h.logger = ctx.Logger(h) + + if h.DialTimeout <= 0 { + h.DialTimeout = caddy.Duration(30 * time.Second) + } + + h.httpTransport = &http.Transport{ + Proxy: http.ProxyFromEnvironment, + MaxIdleConns: 50, + IdleConnTimeout: 60 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + } + + // access control lists + for _, rule := range h.ACL { + for _, subj := range rule.Subjects { + ar, err := newACLRule(subj, rule.Allow) + if err != nil { + return err + } + h.aclRules = append(h.aclRules, ar) } } - fmt.Println("ERROR: no acl match for ", hostname, ip) // shouldn't happen - return false + for _, ipDeny := range []string{ + "10.0.0.0/8", + "127.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16", + "::1/128", + "fe80::/10", + } { + ar, err := newACLRule(ipDeny, false) + if err != nil { + return err + } + h.aclRules = append(h.aclRules, ar) + } + h.aclRules = append(h.aclRules, &aclAllRule{allow: true}) + + if h.ProbeResistance != nil { + if h.AuthCredentials == nil { + return fmt.Errorf("probe resistance requires authentication") + } + if len(h.ProbeResistance.Domain) > 0 { + h.logger.Info("Secret domain used to connect to proxy: " + h.ProbeResistance.Domain) + } + } + + dialer := &net.Dialer{ + Timeout: time.Duration(h.DialTimeout), + KeepAlive: 30 * time.Second, + DualStack: true, + } + h.dialContext = dialer.DialContext + h.httpTransport.DialContext = func(ctx context.Context, network string, address string) (net.Conn, error) { + return h.dialContextCheckACL(ctx, network, address) + } + + if h.Upstream != "" { + upstreamURL, err := url.Parse(h.Upstream) + if err != nil { + return fmt.Errorf("bad upstream URL: %v", err) + } + h.upstream = upstreamURL + + if !isLocalhost(h.upstream.Hostname()) && h.upstream.Scheme != "https" { + return errors.New("insecure schemes are only allowed to localhost upstreams") + } + + registerHTTPDialer := func(u *url.URL, _ proxy.Dialer) (proxy.Dialer, error) { + // CONNECT request is proxied as-is, so we don't care about target url, but it could be + // useful in future to implement policies of choosing between multiple upstream servers. + // Given dialer is not used, since it's the same dialer provided by us. + d, err := httpclient.NewHTTPConnectDialer(h.upstream.String()) + if err != nil { + return nil, err + } + d.Dialer = *dialer + if isLocalhost(h.upstream.Hostname()) && h.upstream.Scheme == "https" { + // disabling verification helps with testing the package and setups + // 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}) // #nosec G402 + if err != nil { + return nil, "", err + } + return conn, conn.ConnectionState().NegotiatedProtocol, nil + } + } + return d, nil + } + proxy.RegisterDialerType("https", registerHTTPDialer) + proxy.RegisterDialerType("http", registerHTTPDialer) + + upstreamDialer, err := proxy.FromURL(h.upstream, dialer) + if err != nil { + return errors.New("failed to create proxy to upstream: " + err.Error()) + } + + if ctxDialer, ok := upstreamDialer.(dialContexter); ok { + // upstreamDialer has DialContext - use it + h.dialContext = ctxDialer.DialContext + } else { + // upstreamDialer does not have DialContext - ignore the context :( + h.dialContext = func(ctx context.Context, network string, address string) (net.Conn, error) { + return upstreamDialer.Dial(network, address) + } + } + } + + return nil } -func (fp *ForwardProxy) portIsAllowed(port string) bool { - portInt, err := strconv.Atoi(port) +func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { + // start by splitting the request host and port + reqHost, _, err := net.SplitHostPort(r.Host) if err != nil { - return false + reqHost = r.Host // OK; probably just didn't have a port } - if portInt <= 0 || portInt > 65535 { - return false + + var authErr error + if h.AuthCredentials != nil { + authErr = h.checkCredentials(r) } - if len(fp.whitelistedPorts) == 0 { - return true + if h.ProbeResistance != nil && len(h.ProbeResistance.Domain) > 0 && reqHost == h.ProbeResistance.Domain { + return serveHiddenPage(w, authErr) } - isAllowed := false - for _, p := range fp.whitelistedPorts { - if p == portInt { - isAllowed = true - break + if h.Hosts.Match(r) && (r.Method != http.MethodConnect || authErr != nil) { + // Always pass non-CONNECT requests to hostname + // Pass CONNECT requests only if probe resistance is enabled and not authenticated + if h.shouldServePACFile(r) { + return h.servePacFile(w, r) } + return next.ServeHTTP(w, r) + } + if authErr != nil { + if h.ProbeResistance != nil { + // probe resistance is requested and requested URI does not match secret domain; + // act like this proxy handler doesn't even exist (pass thru to next handler) + return next.ServeHTTP(w, r) + } + w.Header().Set("Proxy-Authenticate", "Basic realm=\"Caddy Secure Web Proxy\"") + return caddyhttp.Error(http.StatusProxyAuthRequired, authErr) } - return isAllowed -} -// Copies data target->clientReader and clientWriter->target, and flushes as needed -// Returns when clientWriter-> target stream is done. -// Caddy should finish writing target -> clientReader. -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) - buf = buf[0:cap(buf)] - _, _err := flushingIoCopy(w, r, buf) - if closeWriter, ok := w.(interface { - CloseWrite() error - }); ok { - closeWriter.CloseWrite() + if r.ProtoMajor != 1 && r.ProtoMajor != 2 && r.ProtoMajor != 3 { + return caddyhttp.Error(http.StatusHTTPVersionNotSupported, + fmt.Errorf("unsupported HTTP major version: %d", r.ProtoMajor)) + } + + ctx := context.Background() + if !h.HideIP { + ctxHeader := make(http.Header) + for k, v := range r.Header { + if kL := strings.ToLower(k); kL == "forwarded" || kL == "x-forwarded-for" { + ctxHeader[k] = v + } } - return _err + ctxHeader.Add("Forwarded", "for=\""+r.RemoteAddr+"\"") + ctx = context.WithValue(ctx, httpclient.ContextKeyHeader{}, ctxHeader) } - go stream(target, clientReader) - return stream(clientWriter, target) -} + if r.Method == http.MethodConnect { + if r.ProtoMajor == 2 || r.ProtoMajor == 3 { + if len(r.URL.Scheme) > 0 || len(r.URL.Path) > 0 { + return caddyhttp.Error(http.StatusBadRequest, + fmt.Errorf("CONNECT request has :scheme and/or :path pseudo-header fields")) + } + } -// Hijacks the connection from ResponseWriter, writes the response and proxies data between targetConn -// and hijacked connection. -func serveHijack(w http.ResponseWriter, targetConn net.Conn) (int, error) { - hijacker, ok := w.(http.Hijacker) - if !ok { - return http.StatusInternalServerError, errors.New("ResponseWriter does not implement Hijacker") + hostPort := r.URL.Host + if hostPort == "" { + hostPort = r.Host + } + targetConn, err := h.dialContextCheckACL(ctx, "tcp", hostPort) + if err != nil { + return err + } + if targetConn == nil { + // safest to check both error and targetConn afterwards, in case fp.dial (potentially unstable + // from x/net/proxy) misbehaves and returns both nil or both non-nil + return caddyhttp.Error(http.StatusForbidden, + fmt.Errorf("hostname %s is not allowed", r.URL.Hostname())) + } + defer targetConn.Close() + + switch r.ProtoMajor { + case 1: // http1: hijack the whole flow + return serveHijack(w, targetConn) + case 2: // http2: keep reading from "request" and writing into same response + fallthrough + case 3: + defer r.Body.Close() + w.WriteHeader(http.StatusOK) + err := http.NewResponseController(w).Flush() + if err != nil { + return caddyhttp.Error(http.StatusInternalServerError, + fmt.Errorf("ResponseWriter flush error: %v", err)) + } + return dualStream(targetConn, r.Body, w) + } + + panic("There was a check for http version, yet it's incorrect") } - clientConn, bufReader, err := hijacker.Hijack() - if err != nil { - return http.StatusInternalServerError, errors.New("failed to hijack: " + err.Error()) + + // Scheme has to be appended to avoid `unsupported protocol scheme ""` error. + // `http://` is used, since this initial request itself is always HTTP, regardless of what client and server + // may speak afterwards. + if r.URL.Scheme == "" { + r.URL.Scheme = "http" } - defer clientConn.Close() - // bufReader may contain unprocessed buffered data from the client. - if bufReader != nil { - // snippet borrowed from `proxy` plugin - if n := bufReader.Reader.Buffered(); n > 0 { - rbuf, err := bufReader.Reader.Peek(n) + if r.URL.Host == "" { + r.URL.Host = r.Host + } + r.Proto = "HTTP/1.1" + r.ProtoMajor = 1 + r.ProtoMinor = 1 + r.RequestURI = "" + + removeHopByHop(r.Header) + + if !h.HideIP { + r.Header.Add("Forwarded", "for=\""+r.RemoteAddr+"\"") + } + + // https://tools.ietf.org/html/rfc7230#section-5.7.1 + if !h.HideVia { + r.Header.Add("Via", strconv.Itoa(r.ProtoMajor)+"."+strconv.Itoa(r.ProtoMinor)+" caddy") + } + + var response *http.Response + if h.upstream == nil { + // non-upstream request uses httpTransport to reuse connections + if r.Body != nil && + (r.Method == "GET" || r.Method == "HEAD" || r.Method == "OPTIONS" || r.Method == "TRACE") { + // 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 := io.ReadAll(r.Body) if err != nil { - return http.StatusBadGateway, err + return caddyhttp.Error(http.StatusBadRequest, + fmt.Errorf("failed to read request body: %v", err)) + } + r.GetBody = func() (io.ReadCloser, error) { + return io.NopCloser(bytes.NewReader(rBodyBuf)), nil } - targetConn.Write(rbuf) + r.Body, _ = r.GetBody() + } + response, err = 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 + // useless connections. + // Just use dialContext, which will multiplex via single connection, if http/2 + if creds := h.upstream.User.String(); creds != "" { + // set upstream credentials for the request, if needed + r.Header.Set("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(creds))) + } + if r.URL.Port() == "" { + r.URL.Host = net.JoinHostPort(r.URL.Host, "80") + } + upsConn, err := h.dialContext(ctx, "tcp", r.URL.Host) + if err != nil { + return caddyhttp.Error(http.StatusBadGateway, + fmt.Errorf("failed to dial upstream: %v", err)) + } + err = r.Write(upsConn) + if err != nil { + return caddyhttp.Error(http.StatusBadGateway, + fmt.Errorf("failed to write upstream request: %v", err)) + } + response, err = http.ReadResponse(bufio.NewReader(upsConn), r) + if err != nil { + return caddyhttp.Error(http.StatusBadGateway, + fmt.Errorf("failed to read upstream response: %v", err)) } } - // 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, - Proto: "HTTP/1.1", - ProtoMajor: 1, - ProtoMinor: 1, - Header: make(http.Header), + if err := r.Body.Close(); err != nil { + return caddyhttp.Error(http.StatusBadGateway, + fmt.Errorf("failed to close response body: %v", err)) } - res.Header.Set("Server", "Caddy") - err = res.Write(clientConn) + if response != nil { + defer response.Body.Close() + } if err != nil { - return http.StatusInternalServerError, errors.New("failed to send response to client: " + err.Error()) + if _, ok := err.(caddyhttp.HandlerError); ok { + return err + } + return caddyhttp.Error(http.StatusBadGateway, + fmt.Errorf("failed to read response: %v", err)) } - return 0, dualStream(targetConn, clientConn, clientConn) + return forwardResponse(w, response) } -// Returns nil error on successful credentials check. -func (fp *ForwardProxy) checkCredentials(r *http.Request) error { +func (h Handler) checkCredentials(r *http.Request) error { pa := strings.Split(r.Header.Get("Proxy-Authorization"), " ") if len(pa) != 2 { return errors.New("Proxy-Authorization is required! Expected format: ") } 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 fp.authCredentials { + for _, creds := range h.AuthCredentials { if subtle.ConstantTimeCompare(creds, []byte(pa[1])) == 1 { + repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer) + buf := make([]byte, base64.StdEncoding.DecodedLen(len(creds))) + _, _ = base64.StdEncoding.Decode(buf, creds) // should not err ever since we are decoding a known good input + cred := string(buf) + repl.Set("http.auth.user.id", cred[:strings.IndexByte(cred, ':')]) // Please do not consider this to be timing-attack-safe code. Simple equality is almost // mindlessly substituted with constant time algo and there ARE known issues with this code, // e.g. size of smallest credentials is guessable. TODO: protect from all the attacks! Hash? return nil } } - return errors.New("Invalid credentials") -} - -// borrowed from `proxy` plugin -func stripPort(address string) string { - // Keep in mind that the address might be a IPv6 address - // and thus contain a colon, but not have a port. - portIdx := strings.LastIndex(address, ":") - ipv6Idx := strings.LastIndex(address, "]") - if portIdx > ipv6Idx { - address = address[:portIdx] - } - return address -} - -func serveHiddenPage(w http.ResponseWriter, authErr error) (int, error) { - const hiddenPage = ` - - Hidden Proxy Page - - -

Hidden Proxy Page!

-%s
- -` - const AuthFail = "Please authenticate yourself to the proxy." - const AuthOk = "Congratulations, you are successfully authenticated to the proxy! Go browse all the things!" - - w.Header().Set("Content-Type", "text/html") - 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))) - return 0, authErr - } - w.Write([]byte(fmt.Sprintf(hiddenPage, AuthOk))) - return 0, nil -} - -func (fp *ForwardProxy) shouldServePacFile(r *http.Request) bool { - if len(fp.pacFilePath) > 0 && r.URL.Path == fp.pacFilePath { - return true + repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer) + buf := make([]byte, base64.StdEncoding.DecodedLen(len([]byte(pa[1])))) + n, err := base64.StdEncoding.Decode(buf, []byte(pa[1])) + if err != nil { + repl.Set("http.auth.user.id", "invalidbase64:"+err.Error()) + return err + } + if utf8.Valid(buf[:n]) { + cred := string(buf[:n]) + i := strings.IndexByte(cred, ':') + if i >= 0 { + repl.Set("http.auth.user.id", "invalid:"+cred[:i]) + } else { + repl.Set("http.auth.user.id", "invalidformat:"+cred) + } + } else { + repl.Set("http.auth.user.id", "invalid::") } - return false + return errors.New("invalid credentials") } -const pacFile = ` -function FindProxyForURL(url, host) { - if (host === "127.0.0.1" || host === "::1" || host === "localhost") - return "DIRECT"; - return "HTTPS %s:%s"; +func (h Handler) shouldServePACFile(r *http.Request) bool { + return len(h.PACPath) > 0 && r.URL.Path == h.PACPath } -` -func (fp *ForwardProxy) servePacFile(w http.ResponseWriter) (int, error) { - fmt.Fprintf(w, pacFile, fp.hostname, fp.port) - return 0, nil +func (h Handler) servePacFile(w http.ResponseWriter, r *http.Request) error { + fmt.Fprintf(w, pacFile, r.Host) + // fmt.Fprintf(w, pacFile, h.hostname, h.port) + return nil } // dialContextCheckACL enforces Access Control List and calls fp.DialContext -func (fp *ForwardProxy) dialContextCheckACL(ctx context.Context, network, hostPort string) (net.Conn, *ProxyError) { +func (h Handler) dialContextCheckACL(ctx context.Context, network, hostPort string) (net.Conn, error) { var conn net.Conn if network != "tcp" && network != "tcp4" && network != "tcp6" { - return nil, &ProxyError{S: "Network " + network + " is not supported", Code: http.StatusBadRequest} + // return nil, &proxyError{S: "Network " + network + " is not supported", Code: http.StatusBadRequest} + return nil, caddyhttp.Error(http.StatusBadRequest, + fmt.Errorf("network %s is not supported", network)) } host, port, err := net.SplitHostPort(hostPort) if err != nil { - return nil, &ProxyError{S: err.Error(), Code: http.StatusBadRequest} + // return nil, &proxyError{S: err.Error(), Code: http.StatusBadRequest} + return nil, caddyhttp.Error(http.StatusBadRequest, err) } - if fp.upstream != nil { + if h.upstream != nil { // if upstreaming -- do not resolve locally nor check acl - conn, err = fp.dialContext(ctx, network, hostPort) + conn, err = h.dialContext(ctx, network, hostPort) if err != nil { - return conn, &ProxyError{S: err.Error(), Code: http.StatusBadGateway} + // return conn, &proxyError{S: err.Error(), Code: http.StatusBadGateway} + return conn, caddyhttp.Error(http.StatusBadGateway, err) } return conn, nil } - if !fp.portIsAllowed(port) { - return nil, &ProxyError{S: "port " + port + " is not allowed", Code: http.StatusForbidden} + if !h.portIsAllowed(port) { + // return nil, &proxyError{S: "port " + port + " is not allowed", Code: http.StatusForbidden} + return nil, caddyhttp.Error(http.StatusForbidden, + fmt.Errorf("port %s is not allowed", port)) } // in case IP was provided, net.LookupIP will simply return it IPs, err := net.LookupIP(host) if err != nil { - return nil, &ProxyError{S: fmt.Sprintf("Lookup of %s failed: %v", host, err), - Code: http.StatusBadGateway} + // return nil, &proxyError{S: fmt.Sprintf("Lookup of %s failed: %v", host, err), + // Code: http.StatusBadGateway} + return nil, caddyhttp.Error(http.StatusBadGateway, + fmt.Errorf("lookup of %s failed: %v", host, err)) } // This is net.Dial's default behavior: if the host resolves to multiple IP addresses, // Dial will try each IP address in order until one succeeds for _, ip := range IPs { - if !fp.hostIsAllowed(host, ip) { + if !h.hostIsAllowed(host, ip) { continue } - conn, err = fp.dialContext(ctx, network, net.JoinHostPort(ip.String(), port)) + conn, err = h.dialContext(ctx, network, net.JoinHostPort(ip.String(), port)) if err == nil { return conn, nil } } - return nil, &ProxyError{S: "No allowed IP addresses for " + host, Code: http.StatusForbidden} + + return nil, caddyhttp.Error(http.StatusForbidden, fmt.Errorf("no allowed IP addresses for %s", host)) } -func (fp *ForwardProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - var authErr error - if fp.authRequired { - authErr = fp.checkCredentials(r) +func (h Handler) hostIsAllowed(hostname string, ip net.IP) bool { + for _, rule := range h.aclRules { + switch rule.tryMatch(ip, hostname) { + case aclDecisionDeny: + return false + case aclDecisionAllow: + return true + } } - if fp.probeResistEnabled && len(fp.probeResistDomain) > 0 && stripPort(r.Host) == fp.probeResistDomain { - return serveHiddenPage(w, authErr) + // TODO: convert this to log entry + // fmt.Println("ERROR: no acl match for ", hostname, ip) // shouldn't happen + return false +} + +func (h Handler) portIsAllowed(port string) bool { + portInt, err := strconv.Atoi(port) + if err != nil { + return false } - if stripPort(r.Host) == fp.hostname && (r.Method != http.MethodConnect || authErr != nil) { - // Always pass non-CONNECT requests to hostname - // Pass CONNECT requests only if probe resistance is enabled and not authenticated - if fp.shouldServePacFile(r) { - return fp.servePacFile(w) - } - return fp.Next.ServeHTTP(w, r) + if portInt <= 0 || portInt > 65535 { + return false } - if authErr != nil { - if fp.probeResistEnabled { - // probe resistance is requested and requested URI does not match secret domain - httpserver.WriteSiteNotFound(w, r) - return 0, authErr // current Caddy behavior without forwardproxy - } else { - w.Header().Set("Proxy-Authenticate", "Basic realm=\"Caddy Secure Web Proxy\"") - return http.StatusProxyAuthRequired, authErr + if len(h.AllowedPorts) == 0 { + return true + } + isAllowed := false + for _, p := range h.AllowedPorts { + if p == portInt { + isAllowed = true + break } } + return isAllowed +} + +func serveHiddenPage(w http.ResponseWriter, authErr error) error { + const hiddenPage = ` + + Hidden Proxy Page + + +

Hidden Proxy Page!

+%s
+ +` + const AuthFail = "Please authenticate yourself to the proxy." + const AuthOk = "Congratulations, you are successfully authenticated to the proxy! Go browse all the things!" - if r.ProtoMajor != 1 && r.ProtoMajor != 2 { - return http.StatusHTTPVersionNotSupported, errors.New("Unsupported HTTP major version: " + strconv.Itoa(r.ProtoMajor)) + w.Header().Set("Content-Type", "text/html") + 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))) + return authErr } + _, _ = w.Write([]byte(fmt.Sprintf(hiddenPage, AuthOk))) + return nil +} - ctx := context.Background() - if !fp.hideIP { - ctxHeader := make(http.Header) - for k, v := range r.Header { - if kL := strings.ToLower(k); kL == "forwarded" || kL == "x-forwarded-for" { - ctxHeader[k] = v +// Hijacks the connection from ResponseWriter, writes the response and proxies data between targetConn +// and hijacked connection. +func serveHijack(w http.ResponseWriter, targetConn net.Conn) error { + clientConn, bufReader, err := http.NewResponseController(w).Hijack() + if err != nil { + return caddyhttp.Error(http.StatusInternalServerError, + fmt.Errorf("hijack failed: %v", err)) + } + defer clientConn.Close() + // bufReader may contain unprocessed buffered data from the client. + if bufReader != nil { + // snippet borrowed from `proxy` plugin + if n := bufReader.Reader.Buffered(); n > 0 { + rbuf, err := bufReader.Reader.Peek(n) + if err != nil { + return caddyhttp.Error(http.StatusBadGateway, err) } + _, _ = targetConn.Write(rbuf) + } - ctxHeader.Add("Forwarded", "for=\""+r.RemoteAddr+"\"") - ctx = context.WithValue(ctx, httpclient.ContextKeyHeader{}, ctxHeader) } + // 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, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: make(http.Header), + } + res.Header.Set("Server", "Caddy") - if r.Method == http.MethodConnect { - if r.ProtoMajor == 2 { - if len(r.URL.Scheme) > 0 || len(r.URL.Path) > 0 { - return http.StatusBadRequest, errors.New("CONNECT request has :scheme or/and :path pseudo-header fields") - } - } + 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)) + } - hostPort := r.URL.Host - if hostPort == "" { - hostPort = r.Host - } - targetConn, err := fp.dialContextCheckACL(ctx, "tcp", hostPort) - if err != nil { - return err.SplitCodeError() - } - if targetConn == nil { - // safest to check both error and targetConn afterwards, in case fp.dial (potentially unstable - // from x/net/proxy) misbehaves and returns both nil or both non-nil - return http.StatusForbidden, errors.New("hostname " + r.URL.Hostname() + " is not allowed") - } - defer targetConn.Close() + return dualStream(targetConn, clientConn, clientConn) +} - switch r.ProtoMajor { - case 1: // http1: hijack the whole flow - return serveHijack(w, targetConn) - case 2: // http2: keep reading from "request" and writing into same response - defer r.Body.Close() - wFlusher, ok := w.(http.Flusher) - if !ok { - return http.StatusInternalServerError, errors.New("ResponseWriter doesn't implement Flusher()") - } - w.WriteHeader(http.StatusOK) - wFlusher.Flush() - return 0, dualStream(targetConn, r.Body, w) - default: - panic("There was a check for http version, yet it's incorrect") +// Copies data target->clientReader and clientWriter->target, and flushes as needed +// Returns when clientWriter-> target stream is done. +// Caddy should finish writing target -> clientReader. +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 + bufPtr := bufferPool.Get().(*[]byte) + buf := *bufPtr + buf = buf[0:cap(buf)] + _, _err := flushingIoCopy(w, r, buf) + bufferPool.Put(bufPtr) + + if cw, ok := w.(closeWriter); ok { + _ = cw.CloseWrite() } - } else { - // Scheme has to be appended to avoid `unsupported protocol scheme ""` error. - // `http://` is used, since this initial request itself is always HTTP, regardless of what client and server - // may speak afterwards. - if r.URL.Scheme == "" { - r.URL.Scheme = "http" - } - if r.URL.Host == "" { - r.URL.Host = r.Host - } - r.Proto = "HTTP/1.1" - r.ProtoMajor = 1 - r.ProtoMinor = 1 - r.RequestURI = "" - - removeHopByHop(r.Header) - - if !fp.hideIP { - r.Header.Add("Forwarded", "for=\""+r.RemoteAddr+"\"") - } - - // https://tools.ietf.org/html/rfc7230#section-5.7.1 - if !fp.hideVia { - r.Header.Add("Via", strconv.Itoa(r.ProtoMajor)+"."+strconv.Itoa(r.ProtoMinor)+" caddy") - } - - var err error - var response *http.Response - if fp.upstream == nil { - // non-upstream request uses httpTransport to reuse connections - if r.Body != nil && - (r.Method == "GET" || r.Method == "HEAD" || r.Method == "OPTIONS" || r.Method == "TRACE") { - // 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) - if err != nil { - return http.StatusBadRequest, errors.New("failed to read request Body: " + err.Error()) - } - r.GetBody = func() (io.ReadCloser, error) { - return ioutil.NopCloser(bytes.NewReader(rBodyBuf)), nil - } - r.Body, _ = r.GetBody() - } - response, err = fp.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 - // useless connections. - // Just use dialContext, which will multiplex via single connection, if http/2 - if creds := fp.upstream.User.String(); creds != "" { - // set upstream credentials for the request, if needed - r.Header.Set("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(creds))) - } - if r.URL.Port() == "" { - r.URL.Host = net.JoinHostPort(r.URL.Host, "80") + return _err + } + go stream(target, clientReader) //nolint: errcheck + return stream(clientWriter, target) +} + +type closeWriter interface { + CloseWrite() error +} + +// flushingIoCopy is analogous to buffering io.Copy(), but also attempts to flush on each iteration. +// If dst does not implement http.Flusher(e.g. net.TCPConn), it will do a simple io.CopyBuffer(). +// Reasoning: http2ResponseWriter will not flush on its own, so we have to do it manually. +func flushingIoCopy(dst io.Writer, src io.Reader, buf []byte) (written int64, err error) { + rw, ok := dst.(http.ResponseWriter) + if !ok { + return io.CopyBuffer(dst, src, buf) + } + rc := http.NewResponseController(rw) + for { + nr, er := src.Read(buf) + if nr > 0 { + nw, ew := dst.Write(buf[0:nr]) + if nw > 0 { + written += int64(nw) } - upsConn, err := fp.dialContext(ctx, "tcp", r.URL.Host) - if err != nil { - return http.StatusBadGateway, errors.New("failed to dial upstream: " + err.Error()) + if ew != nil { + err = ew + break } - err = r.Write(upsConn) - if err != nil { - return http.StatusBadGateway, errors.New("failed to write http request: " + err.Error()) + ef := rc.Flush() + if ef != nil { + err = ef + break } - response, err = http.ReadResponse(bufio.NewReader(upsConn), r) - if err != nil { - return http.StatusBadGateway, errors.New("failed to read http response: " + err.Error()) + if nr != nw { + err = io.ErrShortWrite + break } } - r.Body.Close() - if response != nil { - defer response.Body.Close() - } - if err != nil { - if p, ok := err.(*ProxyError); ok { - return p.SplitCodeError() + if er != nil { + if er != io.EOF { + err = er } - return http.StatusBadGateway, errors.New("failed to read http response: " + err.Error()) + break } - - return 0, forwardResponse(w, response) } + return } // Removes hop-by-hop headers, and writes response into ResponseWriter. @@ -465,12 +694,24 @@ 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(bufPtr) return err } +func removeHopByHop(header http.Header) { + connectionHeaders := header.Get("Connection") + for _, h := range strings.Split(connectionHeaders, ",") { + header.Del(strings.TrimSpace(h)) + } + for _, h := range hopByHopHeaders { + header.Del(h) + } +} + var hopByHopHeaders = []string{ "Keep-Alive", "Proxy-Authenticate", @@ -483,47 +724,58 @@ var hopByHopHeaders = []string{ "Transfer-Encoding", } -func removeHopByHop(header http.Header) { - connectionHeaders := header.Get("Connection") - for _, h := range strings.Split(connectionHeaders, ",") { - header.Del(strings.TrimSpace(h)) - } - for _, h := range hopByHopHeaders { - header.Del(h) - } +const pacFile = ` +function FindProxyForURL(url, host) { + if (host === "127.0.0.1" || host === "::1" || host === "localhost") + return "DIRECT"; + return "HTTPS %s"; } +` -// flushingIoCopy is analogous to buffering io.Copy(), but also attempts to flush on each iteration. -// If dst does not implement http.Flusher(e.g. net.TCPConn), it will do a simple io.CopyBuffer(). -// Reasoning: http2ResponseWriter will not flush on its own, so we have to do it manually. -func flushingIoCopy(dst io.Writer, src io.Reader, buf []byte) (written int64, err error) { - flusher, ok := dst.(http.Flusher) - if !ok { - return io.CopyBuffer(dst, src, buf) +var bufferPool = sync.Pool{ + New: func() interface{} { + buffer := make([]byte, 0, 32*1024) + return &buffer + }, +} + +////// used during provision only + +func isLocalhost(hostname string) bool { + return hostname == "localhost" || + hostname == "127.0.0.1" || + hostname == "::1" +} + +type dialContexter interface { + DialContext(ctx context.Context, network, address string) (net.Conn, error) +} + +// ProbeResistance configures probe resistance. +type ProbeResistance struct { + Domain string `json:"domain,omitempty"` +} + +func readLinesFromFile(filename string) ([]string, error) { + cleanFilename := filepath.Clean(filename) + file, err := os.Open(cleanFilename) + if err != nil { + return nil, err } - for { - nr, er := src.Read(buf) - if nr > 0 { - nw, ew := dst.Write(buf[0:nr]) - flusher.Flush() - if nw > 0 { - written += int64(nw) - } - if ew != nil { - err = ew - break - } - if nr != nw { - err = io.ErrShortWrite - break - } - } - if er != nil { - if er != io.EOF { - err = er - } - break - } + defer file.Close() + + var hostnames []string + scanner := bufio.NewScanner(file) + for scanner.Scan() { + hostnames = append(hostnames, scanner.Text()) } - return + + return hostnames, scanner.Err() } + +// Interface guards +var ( + _ caddy.Provisioner = (*Handler)(nil) + _ caddyhttp.MiddlewareHandler = (*Handler)(nil) + _ caddyfile.Unmarshaler = (*Handler)(nil) +) diff --git a/forwardproxy_test.go b/forwardproxy_test.go index 3ba90ee1..beb1158a 100644 --- a/forwardproxy_test.go +++ b/forwardproxy_test.go @@ -17,36 +17,31 @@ package forwardproxy import ( "bufio" "crypto/tls" - "errors" "fmt" "io" - "io/ioutil" "net" "net/http" "net/url" - "strings" "testing" "time" - _ "github.com/caddyserver/caddy/caddyhttp/header" - _ "github.com/caddyserver/caddy/caddyhttp/httpserver" - _ "github.com/caddyserver/caddy/caddyhttp/redirect" - _ "github.com/caddyserver/caddy/caddyhttp/root" "github.com/caddyserver/forwardproxy/httpclient" "golang.org/x/net/http2" ) -func dial(proxyAddr, httpProxyVer string, useTls bool) (net.Conn, error) { - if useTls { - return tls.Dial("tcp", proxyAddr, &tls.Config{InsecureSkipVerify: true, - NextProtos: []string{httpVersionToAlpn[httpProxyVer]}}) - } else { - return net.Dial("tcp", proxyAddr) +func dial(proxyAddr, httpProxyVer string, useTLS bool) (net.Conn, error) { + // always dial localhost for testing purposes + if useTLS { + return tls.Dial("tcp", proxyAddr, &tls.Config{ + InsecureSkipVerify: true, + NextProtos: []string{httpVersionToALPN[httpProxyVer]}, + }) } + return net.Dial("tcp", proxyAddr) } -func getViaProxy(targetHost, resource, proxyAddr, httpProxyVer, proxyCredentials string, useTls bool) (*http.Response, error) { - proxyConn, err := dial(proxyAddr, httpProxyVer, useTls) +func getViaProxy(targetHost, resource, proxyAddr, httpProxyVer, proxyCredentials string, useTLS bool) (*http.Response, error) { + proxyConn, err := dial(proxyAddr, httpProxyVer, useTLS) if err != nil { return nil, err } @@ -54,18 +49,18 @@ func getViaProxy(targetHost, resource, proxyAddr, httpProxyVer, proxyCredentials } // if connect is not successful - that response is returned, otherwise the requested resource -func connectAndGetViaProxy(targetHost, resource, proxyAddr, httpTargetVer, proxyCredentials, httpProxyVer string, useTls bool) (*http.Response, error) { - proxyConn, err := dial(proxyAddr, httpProxyVer, useTls) +func connectAndGetViaProxy(targetHost, resource, proxyAddr, httpTargetVer, proxyCredentials, httpProxyVer string, useTLS bool) (*http.Response, error) { + proxyConn, err := dial(proxyAddr, httpProxyVer, useTLS) if err != nil { return nil, err } - req := http.Request{Header: make(http.Header)} + req := &http.Request{Header: make(http.Header)} if len(proxyCredentials) > 0 { req.Header.Set("Proxy-Authorization", proxyCredentials) } req.Host = targetHost - req.URL, err = url.Parse("https://" + req.Host) + req.URL, err = url.Parse("https://" + req.Host + "/") // TODO: appending "/" causes file server to NOT issue redirect... if err != nil { return nil, err } @@ -79,13 +74,13 @@ 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 { return nil, err } - resp, err = clientConn.RoundTrip(&req) + resp, err = clientConn.RoundTrip(req) if err != nil { return resp, err } @@ -93,8 +88,8 @@ func connectAndGetViaProxy(targetHost, resource, proxyAddr, httpTargetVer, proxy case "HTTP/1.1": req.ProtoMajor = 1 req.ProtoMinor = 1 - req.Write(proxyConn) - resp, err = http.ReadResponse(bufio.NewReader(proxyConn), &req) + req.Write(proxyConn) // nolint:errcheck // we don't care about the error here + resp, err = http.ReadResponse(bufio.NewReader(proxyConn), req) if err != nil { return resp, err } @@ -115,12 +110,12 @@ func connectAndGetViaProxy(targetHost, resource, proxyAddr, httpTargetVer, proxy func getResourceViaProxyConn(proxyConn net.Conn, targetHost, resource, httpTargetVer, proxyCredentials string) (*http.Response, error) { var err error - req := http.Request{Header: make(http.Header)} + req := &http.Request{Header: make(http.Header)} if len(proxyCredentials) > 0 { req.Header.Set("Proxy-Authorization", proxyCredentials) } req.Host = targetHost - req.URL, err = url.Parse("http://" + req.Host + resource) + req.URL, err = url.Parse("http://" + targetHost + resource) if err != nil { return nil, err } @@ -137,14 +132,14 @@ func getResourceViaProxyConn(proxyConn net.Conn, targetHost, resource, httpTarge if err != nil { return nil, err } - return clientConn.RoundTrip(&req) + return clientConn.RoundTrip(req) case "HTTP/1.1": req.ProtoMajor = 1 req.ProtoMinor = 1 t := http.Transport{Dial: func(network, addr string) (net.Conn, error) { return proxyConn, nil }} - return t.RoundTrip(&req) + return t.RoundTrip(req) default: panic("proxy ver: " + httpTargetVer) } @@ -165,30 +160,26 @@ func responseExpected(res *http.Response, expectedResponse []byte) error { panic(err) } if nTotal == responseLen { - return errors.New(fmt.Sprintf("nTotal == responseLen, but haven't seen io.EOF. Expected response: %s\nGot: %s\n", - expectedResponse, response)) + return fmt.Errorf("nTotal == responseLen, but haven't seen io.EOF. Expected response: %s\nGot: %s", + expectedResponse, response) } } response = response[:nTotal] if len(expectedResponse) != len(response) { - return errors.New(fmt.Sprintf("Expected length: %d. Got thus far: %d. Expected response: %s\nGot: %s\n", - len(expectedResponse), len(response), expectedResponse, response)) + return fmt.Errorf("expected length: %d. Got thus far: %d. Expected response: %s\nGot: %s", + len(expectedResponse), len(response), expectedResponse, response) } for i := range response { if response[i] != expectedResponse[i] { - return errors.New(fmt.Sprintf("Response mismatch at character #%d. Expected response: %s\nGot: %s\n", - i, expectedResponse, response)) + return fmt.Errorf("response mismatch at character #%d. Expected response: %s\nGot: %s", + i, expectedResponse, response) } } return nil } func TestPassthrough(t *testing.T) { - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - ResponseHeaderTimeout: 2 * time.Second, - } - client := &http.Client{Transport: tr, Timeout: 2 * time.Second} + client := &http.Client{Transport: testTransport, Timeout: 2 * time.Second} resp, err := client.Get("https://" + caddyForwardProxy.addr) if err != nil { t.Fatal(err) @@ -212,10 +203,10 @@ func TestPassthrough(t *testing.T) { } func TestGETNoAuth(t *testing.T) { - useTls := true - for _, httpProxyVer := range testHttpProxyVersions { + const useTLS = true + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { - response, err := getViaProxy(caddyHTTPTestTarget.addr, resource, caddyForwardProxy.addr, httpProxyVer, credentialsEmpty, useTls) + response, err := getViaProxy(caddyHTTPTestTarget.addr, resource, caddyForwardProxy.addr, httpProxyVer, credentialsEmpty, useTLS) if err != nil { t.Fatal(err) } else if err = responseExpected(response, caddyHTTPTestTarget.contents[resource]); err != nil { @@ -226,10 +217,10 @@ func TestGETNoAuth(t *testing.T) { } func TestGETAuthCorrect(t *testing.T) { - useTls := true - for _, httpProxyVer := range testHttpProxyVersions { + const useTLS = true + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { - response, err := getViaProxy(caddyHTTPTestTarget.addr, resource, caddyForwardProxyAuth.addr, httpProxyVer, credentialsCorrect, useTls) + response, err := getViaProxy(caddyHTTPTestTarget.addr, resource, caddyForwardProxyAuth.addr, httpProxyVer, credentialsCorrect, useTLS) if err != nil { t.Fatal(err) } else if err = responseExpected(response, caddyHTTPTestTarget.contents[resource]); err != nil { @@ -240,11 +231,11 @@ func TestGETAuthCorrect(t *testing.T) { } func TestGETAuthWrong(t *testing.T) { - useTls := true + const useTLS = true for _, wrongCreds := range credentialsWrong { - for _, httpProxyVer := range testHttpProxyVersions { + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { - response, err := getViaProxy(caddyHTTPTestTarget.addr, resource, caddyForwardProxyAuth.addr, httpProxyVer, wrongCreds, useTls) + response, err := getViaProxy(caddyHTTPTestTarget.addr, resource, caddyForwardProxyAuth.addr, httpProxyVer, wrongCreds, useTLS) if err != nil { t.Fatal(err) } @@ -258,11 +249,11 @@ func TestGETAuthWrong(t *testing.T) { } func TestProxySelfGet(t *testing.T) { - useTls := true + const useTLS = true // GETNoAuth to self - for _, httpTargetVer := range testHttpTargetVersions { + for _, httpTargetVer := range testHTTPTargetVersions { for _, resource := range testResources { - response, err := getViaProxy(caddyForwardProxy.addr, resource, caddyForwardProxy.addr, httpTargetVer, credentialsEmpty, useTls) + response, err := getViaProxy(caddyForwardProxy.addr, resource, caddyForwardProxy.addr, httpTargetVer, credentialsEmpty, useTLS) if err != nil { t.Fatal(err) } else if err = responseExpected(response, caddyForwardProxy.contents[resource]); err != nil { @@ -272,9 +263,9 @@ func TestProxySelfGet(t *testing.T) { } // GETAuthCorrect to self - for _, httpTargetVer := range testHttpTargetVersions { + for _, httpTargetVer := range testHTTPTargetVersions { for _, resource := range testResources { - response, err := getViaProxy(caddyForwardProxyAuth.addr, resource, caddyForwardProxyAuth.addr, httpTargetVer, credentialsCorrect, useTls) + response, err := getViaProxy(caddyForwardProxyAuth.addr, resource, caddyForwardProxyAuth.addr, httpTargetVer, credentialsCorrect, useTLS) if err != nil { t.Fatal(err) } else if err = responseExpected(response, caddyForwardProxyAuth.contents[resource]); err != nil { @@ -289,11 +280,11 @@ func TestProxySelfGet(t *testing.T) { // Low priority since this is a functionality issue, not security, and it would be easily caught in the wild. func TestConnectNoAuth(t *testing.T) { - useTls := true - for _, httpProxyVer := range testHttpProxyVersions { - for _, httpTargetVer := range testHttpTargetVersions { + const useTLS = true + for _, httpProxyVer := range testHTTPProxyVersions { + for _, httpTargetVer := range testHTTPTargetVersions { for _, resource := range testResources { - response, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, caddyForwardProxy.addr, httpTargetVer, credentialsEmpty, httpProxyVer, useTls) + response, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, caddyForwardProxy.addr, httpTargetVer, credentialsEmpty, httpProxyVer, useTLS) if err != nil { t.Fatal(err) } else if err = responseExpected(response, caddyTestTarget.contents[resource]); err != nil { @@ -305,11 +296,11 @@ func TestConnectNoAuth(t *testing.T) { } func TestConnectAuthCorrect(t *testing.T) { - useTls := true - for _, httpProxyVer := range testHttpProxyVersions { - for _, httpTargetVer := range testHttpTargetVersions { + const useTLS = true + for _, httpProxyVer := range testHTTPProxyVersions { + for _, httpTargetVer := range testHTTPTargetVersions { for _, resource := range testResources { - response, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyAuth.addr, httpTargetVer, credentialsCorrect, httpProxyVer, useTls) + response, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyAuth.addr, httpTargetVer, credentialsCorrect, httpProxyVer, useTLS) if err != nil { t.Fatal(httpProxyVer, httpTargetVer, err) } else if err = responseExpected(response, caddyTestTarget.contents[resource]); err != nil { @@ -321,18 +312,18 @@ func TestConnectAuthCorrect(t *testing.T) { } func TestConnectAuthWrong(t *testing.T) { - useTls := true + const useTLS = true for _, wrongCreds := range credentialsWrong { - for _, httpProxyVer := range testHttpProxyVersions { - for _, httpTargetVer := range testHttpTargetVersions { + for _, httpProxyVer := range testHTTPProxyVersions { + for _, httpTargetVer := range testHTTPTargetVersions { for _, resource := range testResources { - response, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyAuth.addr, httpTargetVer, wrongCreds, httpProxyVer, useTls) + response, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyAuth.addr, httpTargetVer, wrongCreds, httpProxyVer, useTLS) if err != nil { t.Fatal(err) } if response.StatusCode != http.StatusProxyAuthRequired { - t.Fatalf("Expected response: 407 StatusProxyAuthRequired, Got: %d %s\n", - response.StatusCode, response.Status) + t.Fatalf("Expected response: 407 StatusProxyAuthRequired, Got: %d %s (wrongCreds=%s httpProxyVer=%s httpTargetVer=%s resource=%s)", + response.StatusCode, response.Status, wrongCreds, httpProxyVer, httpTargetVer, resource) } } } @@ -341,17 +332,12 @@ func TestConnectAuthWrong(t *testing.T) { } func TestPAC(t *testing.T) { - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - ResponseHeaderTimeout: 2 * time.Second, - } - client := &http.Client{Transport: tr, Timeout: 2 * time.Second} + client := &http.Client{Transport: testTransport, Timeout: 2 * time.Second} resp, err := client.Get("https://" + caddyForwardProxy.addr + "/proxy.pac") if err != nil { t.Fatal(err) } - splitAddr := strings.Split(caddyForwardProxy.addr, ":") - if err = responseExpected(resp, []byte(fmt.Sprintf(pacFile, splitAddr[0], splitAddr[1]))); err != nil { + if err = responseExpected(resp, []byte(fmt.Sprintf(pacFile, caddyForwardProxy.addr))); err != nil { t.Fatal(err) } @@ -359,20 +345,19 @@ func TestPAC(t *testing.T) { if err != nil { t.Fatal(err) } - splitAddr = strings.Split(caddyForwardProxyProbeResist.addr, ":") - if err = responseExpected(resp, []byte(fmt.Sprintf(pacFile, splitAddr[0], splitAddr[1]))); err != nil { + if err = responseExpected(resp, []byte(fmt.Sprintf(pacFile, caddyForwardProxyProbeResist.addr))); err != nil { t.Fatal(err) } } func TestCONNECTViaUpstream(t *testing.T) { - useTls := true + const useTLS = true for range make([]byte, 5) { // do several times to test http2 connection reuse - for _, httpProxyVer := range testHttpProxyVersions { - for _, httpTargetVer := range testHttpTargetVersions { + for _, httpProxyVer := range testHTTPProxyVersions { + for _, httpTargetVer := range testHTTPTargetVersions { for _, resource := range testResources { response, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, caddyAuthedUpstreamEnter.addr, - httpTargetVer, credentialsUpstreamCorrect, httpProxyVer, useTls) + httpTargetVer, credentialsUpstreamCorrect, httpProxyVer, useTLS) if err != nil { t.Fatal(err) } else if err = responseExpected(response, caddyTestTarget.contents[resource]); err != nil { @@ -385,12 +370,12 @@ func TestCONNECTViaUpstream(t *testing.T) { } func TestGETViaUpstream(t *testing.T) { - useTls := true + const useTLS = true for range make([]byte, 5) { // do several times to test http2 connection reuse - for _, httpProxyVer := range testHttpProxyVersions { + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { response, err := getViaProxy(caddyHTTPTestTarget.addr, resource, caddyAuthedUpstreamEnter.addr, httpProxyVer, - credentialsUpstreamCorrect, useTls) + credentialsUpstreamCorrect, useTLS) if err != nil { t.Fatal(err) } else if err = responseExpected(response, caddyHTTPTestTarget.contents[resource]); err != nil { @@ -403,11 +388,7 @@ func TestGETViaUpstream(t *testing.T) { func TestUpstreamPassthrough(t *testing.T) { // Usptreaming proxy still hosts things as expected - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - ResponseHeaderTimeout: 2 * time.Second, - } - client := &http.Client{Transport: tr, Timeout: 2 * time.Second} + client := &http.Client{Transport: testTransport, Timeout: 2 * time.Second} resp, err := client.Get("https://" + caddyAuthedUpstreamEnter.addr) if err != nil { t.Fatal(err) diff --git a/go.mod b/go.mod index c1a5e6b4..54ec2d53 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,114 @@ module github.com/caddyserver/forwardproxy -go 1.12 +go 1.21 require ( - github.com/caddyserver/caddy v1.0.5 - github.com/golang/protobuf v1.4.3 // indirect - github.com/lucas-clemente/quic-go v0.19.3 // indirect - github.com/mholt/caddy v1.0.0 - golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c // indirect - golang.org/x/net v0.0.0-20200707034311-ab3426394381 - golang.org/x/sys v0.0.0-20201204225414-ed752295db88 // indirect - google.golang.org/protobuf v1.25.0 // indirect + github.com/caddyserver/caddy/v2 v2.7.6 + go.uber.org/zap v1.26.0 + golang.org/x/net v0.21.0 +) + +require ( + filippo.io/edwards25519 v1.0.0 // indirect + github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect + github.com/BurntSushi/toml v1.3.2 // 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.9.1 // indirect + github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect + github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/caddyserver/certmagic v0.20.0 // indirect + github.com/cespare/xxhash v1.1.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chzyer/readline v1.5.1 // 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.10.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.7.1 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/golang/glog v1.1.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/cel-go v0.15.1 // indirect + github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/huandu/xstrings v1.3.3 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgconn v1.14.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.2 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgtype v1.14.0 // indirect + github.com/jackc/pgx/v4 v4.18.0 // indirect + github.com/klauspost/compress v1.17.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // 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.16 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect + github.com/mholt/acmez v1.2.0 // indirect + github.com/micromdm/scep/v2 v2.1.0 // indirect + github.com/miekg/dns v1.1.56 // 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.12.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/quic-go/qpack v0.4.0 // indirect + github.com/quic-go/quic-go v0.41.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.25.0 // indirect + github.com/smallstep/nosql v0.6.0 // indirect + github.com/smallstep/truststore v0.12.1 // indirect + github.com/spf13/cast v1.4.1 // indirect + github.com/spf13/cobra v1.7.0 // 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-20230806124524-28a91b69a046 // indirect + github.com/urfave/cli v1.22.14 // indirect + github.com/yuin/goldmark v1.5.6 // indirect + github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc // indirect + github.com/zeebo/blake3 v0.2.3 // indirect + go.etcd.io/bbolt v1.3.7 // indirect + go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect + go.step.sm/cli-utils v0.8.0 // indirect + go.step.sm/crypto v0.35.1 // indirect + go.step.sm/linkedca v0.20.1 // indirect + go.uber.org/mock v0.3.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.19.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/term v0.17.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.13.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect + google.golang.org/grpc v1.59.0 // indirect + google.golang.org/protobuf v1.31.0 // 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 ) diff --git a/go.sum b/go.sum index 85366a00..5b0fe529 100644 --- a/go.sum +++ b/go.sum @@ -1,545 +1,836 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA= -dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= -dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= -dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg= -github.com/Azure/go-autorest/autorest v0.5.0/go.mod h1:9HLKlQjVBH6U3oDfsXOeVc56THsLPw1L03yban4xThw= -github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= -github.com/Azure/go-autorest/autorest/adal v0.2.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= -github.com/Azure/go-autorest/autorest/azure/auth v0.1.0/go.mod h1:Gf7/i2FUpyb/sGBLIFxTBzrNzBo7aPXXE3ZVeDRwdpM= -github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= -github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88= +cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o= +cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/iam v1.1.2 h1:gacbrBdWcoVmGLozRuStX45YKvJtzIjJdAolzUs1sm4= +cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= +cloud.google.com/go/kms v1.15.2 h1:lh6qra6oC4AyWe5fUUUBe/S27k12OHAleOOOw6KakdE= +cloud.google.com/go/kms v1.15.2/go.mod h1:3hopT4+7ooWRCjc2DxgnpESFxhIraaI2IpAVUEhbT/w= +filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= +filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= +github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= +github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k= -github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75/go.mod h1:uAXEEpARkRhCZfEvy/y0Jcc888f9tHCc1W7/UeEtreE= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= +github.com/alecthomas/chroma/v2 v2.9.1 h1:0O3lTQh9FxazJ4BYE/MOi/vDGuHn7B+6Bu902N2UZvU= +github.com/alecthomas/chroma/v2 v2.9.1/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw= +github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= -github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= +github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw= +github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.45.12 h1:+bKbbesGNPp+TeGrcqfrWuZoqcIEhjwKyBMHQPp80Jo= +github.com/aws/aws-sdk-go v1.45.12/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115 h1:fUjoj2bT6dG8LoEe+uNsKk8J+sLkDbQkJnB6Z1F02Bc= -github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/caddyserver/caddy v1.0.5 h1:5B1Hs0UF2x2tggr2X9jL2qOZtDXbIWQb9YLbmlxHSuM= -github.com/caddyserver/caddy v1.0.5/go.mod h1:AnFHB+/MrgRC+mJAvuAgQ38ePzw+wKeW0wzENpdQQKY= -github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= -github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= -github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= -github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/caddyserver/caddy/v2 v2.7.6 h1:w0NymbG2m9PcvKWsrXO6EEkY9Ru4FJK8uQbYcev1p3A= +github.com/caddyserver/caddy/v2 v2.7.6/go.mod h1:JCiwFMnRWjk8lOa7po0wM/75kwd38ccJPMSrXvQCMQ0= +github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc= +github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9 h1:a1zrFsLFac2xoM6zG1u72DWJwZG3ayttYLfmLbxVETk= -github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= -github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decker502/dnspod-go v0.2.0/go.mod h1:qsurYu1FgxcDwfSwXJdLt4kRsBLZeosEb9uq4Sy+08g= +github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= +github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= +github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= +github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= +github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= +github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= -github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/dnsimple/dnsimple-go v0.30.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= +github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-acme/lego v2.5.0+incompatible h1:5fNN9yRQfv8ymH3DSsxla+4aYeQt2IgfZqHKVnK8f0s= -github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= -github.com/go-acme/lego/v3 v3.1.0/go.mod h1:074uqt+JS6plx+c9Xaiz6+L+GBb+7itGtzfcDM2AhEE= -github.com/go-acme/lego/v3 v3.2.0 h1:z0zvNlL1niv/1qA06V5X1BRC5PeLoGKAlVaWthXQz9c= -github.com/go-acme/lego/v3 v3.2.0/go.mod h1:074uqt+JS6plx+c9Xaiz6+L+GBb+7itGtzfcDM2AhEE= -github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/cel-go v0.15.1 h1:iTgVZor2x9okXtmTrqO8cg4uvqIeaBcWhXtruaWFMYQ= +github.com/google/cel-go v0.15.1/go.mod h1:YzWEoI07MC/a/wj9in8GeVatqfypkldgBlwXh9bCwqY= +github.com/google/certificate-transparency-go v1.1.6 h1:SW5K3sr7ptST/pIvNkSVWMiJqemRmkjJPPT0jzXdOOY= +github.com/google/certificate-transparency-go v1.1.6/go.mod h1:0OJjOsOk+wj6aYQgP7FU0ioQ0AJUmnWPFMqTjQeazPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk= +github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= +github.com/google/go-tpm-tools v0.4.1 h1:gYU6iwRo0tY3V6NDnS6m+XYog+b3g6YFhHQl3sYaUL4= +github.com/google/go-tpm-tools v0.4.1/go.mod h1:w03m0jynhTo7puXTYoyfpNOMqyQ9SB7sixnKWsS/1L0= +github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus= +github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8 h1:gpptm606MZYGaMHMsB4Srmb6EbW/IVHnt04rcMXnkBQ= +github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= +github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.4.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 h1:UnszMmmmm5vLwWzDjTFVIkfhvWF1NdrmChl8L2NUDCw= -github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= -github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8= +github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q= +github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0= +github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= +github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.0 h1:Ltaa1ePvc7msFGALnCrqKJVEByu/qYh5jJBYcDtAno4= +github.com/jackc/pgx/v4 v4.18.0/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= -github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= -github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= -github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= -github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA= -github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ= -github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f h1:sSeNEkJrs+0F9TUau0CgWTTNEwF23HST3Eq0A+QIx+A= -github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f/go.mod h1:JpH9J1c9oX6otFSgdUHwUBUizmKlrMjxWnIAjff4m04= -github.com/lucas-clemente/quic-clients v0.1.0/go.mod h1:y5xVIEoObKqULIKivu+gD/LU90pL73bTdtQjPBvtCBk= -github.com/lucas-clemente/quic-go v0.10.2 h1:iQtTSZVbd44k94Lu0U16lLBIG3lrnjDvQongjPd4B/s= -github.com/lucas-clemente/quic-go v0.10.2/go.mod h1:hvaRS9IHjFLMq76puFJeWNfmn+H70QZ/CXoxqw9bzao= -github.com/lucas-clemente/quic-go v0.13.1 h1:CxtJTXQIh2aboCPk0M6vf530XOov6DZjVBiSE3nSj8s= -github.com/lucas-clemente/quic-go v0.13.1/go.mod h1:Vn3/Fb0/77b02SGhQk36KzOUmXgVpFfizUfW5WMaqyU= -github.com/lucas-clemente/quic-go v0.19.3 h1:eCDQqvGBB+kCTkA0XrAFtNe81FMa0/fn4QSoeAbmiF4= -github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8= -github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced h1:zqEC1GJZFbGZA0tRyNZqRjep92K5fujFtFsu5ZW7Aug= -github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced/go.mod h1:NCcRLrOTZbzhZvixZLlERbJtDtYsmMw8Jc4vS8Z0g58= -github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/marten-seemann/chacha20 v0.2.0 h1:f40vqzzx+3GdOmzQoItkLX5WLvHgPgyYqFFIO5Gh4hQ= -github.com/marten-seemann/chacha20 v0.2.0/go.mod h1:HSdjFau7GzYRj+ahFNwsO3ouVJr1HFkWoEwNDb4TMtE= -github.com/marten-seemann/qpack v0.1.0 h1:/0M7lkda/6mus9B8u34Asqm8ZhHAAt9Ho0vniNuVSVg= -github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI= -github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs= -github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= -github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= -github.com/marten-seemann/qtls v0.4.1 h1:YlT8QP3WCCvvok7MGEZkMldXbyqgr8oFg5/n8Gtbkks= -github.com/marten-seemann/qtls v0.4.1/go.mod h1:pxVXcHHw1pNIt8Qo0pwSYQEoZ8yYOOPXTCZLQQunvRc= -github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc= -github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= -github.com/marten-seemann/qtls-go1-15 v0.1.1 h1:LIH6K34bPVttyXnUWixk0bzH6/N07VxbSabxn5A5gZQ= -github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis= +github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= +github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mholt/caddy v1.0.0 h1:KI6RPGih2GFzWRPG8s9clKK28Ns4ZlVMKR/v7mxq6+c= -github.com/mholt/caddy v1.0.0/go.mod h1:PzUpQ3yGCTuEuy0KSxEeB4TZOi3zBZ8BR/zY0RBP414= -github.com/mholt/certmagic v0.5.0 h1:lYXxsLUFya/I3BgDCrfuwcMQOB+4auzI8CCzpK41tjc= -github.com/mholt/certmagic v0.5.0/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY= -github.com/mholt/certmagic v0.8.3 h1:JOUiX9IAZbbgyjNP2GY6v/6lorH+9GkZsc7ktMpGCSo= -github.com/mholt/certmagic v0.8.3/go.mod h1:91uJzK5K8IWtYQqTi5R2tsxV1pCde+wdGfaRaOZi6aQ= -github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/miekg/dns v1.1.3 h1:1g0r1IvskvgL8rR+AcHzUA+oFmGcQlaIm4IqakufeMM= -github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.15 h1:CSSIDtllwGLMoA6zjdKnaE6Tx6eVUxQ29LUgGetiDCI= -github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= +github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= +github.com/micromdm/scep/v2 v2.1.0 h1:2fS9Rla7qRR266hvUoEauBJ7J6FhgssEiq2OkSKXmaU= +github.com/micromdm/scep/v2 v2.1.0/go.mod h1:BkF7TkPPhmgJAMtHfP+sFTKXmgzNJgLQlvvGoOExBcc= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= +github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= +github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= +github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= -github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= -github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= -github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/HzqidhOhjw= -github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ= -github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA= +github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= -github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU= +github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 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= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= +github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k= +github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4 h1:S9YlS71UNJIyS61OqGAmLXv3w5zclSidN+qwr80XxKs= -github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= -github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= -github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= -github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= -github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= -github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= -github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= -github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= -github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= -github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= -github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= -github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= -github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= -github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E= +github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= -github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/slackhq/nebula v1.6.1 h1:/OCTR3abj0Sbf2nGoLUrdDXImrCv0ZVFpVPP5qa0DsM= +github.com/slackhq/nebula v1.6.1/go.mod h1:UmkqnXe4O53QwToSl/gG7sM4BroQwAB7dd4hUaT6MlI= +github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY= +github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= +github.com/smallstep/certificates v0.25.0 h1:WWihtjQ7SprnRxDV44mBp8t5SMsNO5EWsQaEwy1rgFg= +github.com/smallstep/certificates v0.25.0/go.mod h1:thJmekMKUplKYip+la99Lk4IwQej/oVH/zS9PVMagEE= +github.com/smallstep/go-attestation v0.4.4-0.20230627102604-cf579e53cbd2 h1:UIAS8DTWkeclraEGH2aiJPyNPu16VbT41w4JoBlyFfU= +github.com/smallstep/go-attestation v0.4.4-0.20230627102604-cf579e53cbd2/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4= +github.com/smallstep/nosql v0.6.0 h1:ur7ysI8s9st0cMXnTvB8tA3+x5Eifmkb6hl4uqNV5jc= +github.com/smallstep/nosql v0.6.0/go.mod h1:jOXwLtockXORUPPZ2MCUcIkGR6w0cN1QGZniY9DITQA= +github.com/smallstep/truststore v0.12.1 h1:guLUKkc1UlsXeS3t6BuVMa4leOOpdiv02PCRTiy1WdY= +github.com/smallstep/truststore v0.12.1/go.mod h1:M4mebeNy28KusGX3lJxpLARIktLcyqBOrj3ZiZ46pqw= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= +github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY= -github.com/transip/gotransip v0.0.0-20190812104329-6d8d9179b66f/go.mod h1:i0f4R4o2HM0m3DZYQWsj6/MEowD57VzoH0v3d7igeFY= -github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046 h1:8rUlviSVOEe7TMk7W0gIPrW8MqEzYfZHpsNWSf8s2vg= +github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046/go.mod h1:kNGUQ3VESx3VZwRwA9MSCUegIl6+saPL8Noq82ozCaU= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= -github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= +github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.5.6 h1:COmQAWTCcGetChm3Ig7G/t8AFAN00t+o8Mt4cf7JpwA= +github.com/yuin/goldmark v1.5.6/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ= +github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I= +github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= +github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= +github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= +github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= +github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= +go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mozilla.org/pkcs7 v0.0.0-20210730143726-725912489c62/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak= +go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.step.sm/cli-utils v0.8.0 h1:b/Tc1/m3YuQq+u3ghTFP7Dz5zUekZj6GUmd5pCvkEXQ= +go.step.sm/cli-utils v0.8.0/go.mod h1:S77aISrC0pKuflqiDfxxJlUbiXcAanyJ4POOnzFSxD4= +go.step.sm/crypto v0.35.1 h1:QAZZ7Q8xaM4TdungGSAYw/zxpyH4fMYTkfaXVV9H7pY= +go.step.sm/crypto v0.35.1/go.mod h1:vn8Vkx/Mbqgoe7AG8btC0qZ995Udm3e+JySuDS1LCJA= +go.step.sm/linkedca v0.20.1 h1:bHDn1+UG1NgRrERkWbbCiAIvv4lD5NOFaswPDTyO5vU= +go.step.sm/linkedca v0.20.1/go.mod h1:Vaq4+Umtjh7DLFI1KuIxeo598vfBzgSYZUjgVJ7Syxw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= -golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= -golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= +go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c h1:9HhBz5L/UjnK9XLtiZhYAdue5BVKep3PMmS2LuPDt8k= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20170726083632-f5079bd7f6f7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6 h1:FP8hkuE6yUEaJnK7O2eTuejKWwW+Rhfj80dQ2JcKxCU= -golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a h1:Yu34BogBivvmu7SAzHHaB9nZWH5D1C+z3F1jyIaYZSQ= -golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e h1:ZytStCyV048ZqDsWHiYDdoI2Vd4msMcrDECFxS+tL9c= -golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201204225414-ed752295db88 h1:KmZPnMocC93w341XZp26yTJg8Za7lhb2KhkYmixoeso= -golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.142.0 h1:mf+7EJ94fi5ZcnpPy+m0Yv2dkz8bKm+UL0snTCuwXlY= +google.golang.org/api v0.142.0/go.mod h1:zJAN5o6HRqR7O+9qJUFOWrZkYE66RH+efPBdTLA4xBA= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a h1:fwgW9j3vHirt4ObdHoYNwuO24BEZjSzbh+zPaNWoiY8= +google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:EMfReVxb80Dq1hhioy0sOsY9jCE46YDgHlJ7fWVUWRE= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/mcuadros/go-syslog.v2 v2.2.1/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw= -gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= +howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/httpclient/httpclient.go b/httpclient/httpclient.go index 5a6ee484..ad77f880 100644 --- a/httpclient/httpclient.go +++ b/httpclient/httpclient.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// httpclient is used by the upstreaming forwardproxy to establish connections to http(s) upstreams. +// Package httpclient is used by the upstreaming forwardproxy to establish connections to http(s) upstreams. // it implements x/net/proxy.Dialer interface package httpclient @@ -33,7 +33,7 @@ import ( // HTTPConnectDialer allows to configure one-time use HTTP CONNECT client type HTTPConnectDialer struct { - ProxyUrl url.URL + ProxyURL url.URL DefaultHeader http.Header // TODO: If spkiFp is set, use it as SPKI fingerprint to confirm identity of the @@ -52,47 +52,47 @@ type HTTPConnectDialer struct { cachedH2RawConn net.Conn } -// NewHTTPClient creates a client to issue CONNECT requests and tunnel traffic via HTTPS proxy. -// proxyUrlStr must provide Scheme and Host, may provide credentials and port. +// NewHTTPConnectDialer creates a client to issue CONNECT requests and tunnel traffic via HTTPS proxy. +// proxyURLStr must provide Scheme and Host, may provide credentials and port. // Example: https://username:password@golang.org:443 -func NewHTTPConnectDialer(proxyUrlStr string) (*HTTPConnectDialer, error) { - proxyUrl, err := url.Parse(proxyUrlStr) +func NewHTTPConnectDialer(proxyURLStr string) (*HTTPConnectDialer, error) { + proxyURL, err := url.Parse(proxyURLStr) if err != nil { return nil, err } - if proxyUrl.Host == "" { - return nil, errors.New("misparsed `url=" + proxyUrlStr + + if proxyURL.Host == "" { + return nil, errors.New("misparsed `url=" + proxyURLStr + "`, make sure to specify full url like https://username:password@hostname.com:443/") } - switch proxyUrl.Scheme { + switch proxyURL.Scheme { case "http": - if proxyUrl.Port() == "" { - proxyUrl.Host = net.JoinHostPort(proxyUrl.Host, "80") + if proxyURL.Port() == "" { + proxyURL.Host = net.JoinHostPort(proxyURL.Host, "80") } case "https": - if proxyUrl.Port() == "" { - proxyUrl.Host = net.JoinHostPort(proxyUrl.Host, "443") + if proxyURL.Port() == "" { + proxyURL.Host = net.JoinHostPort(proxyURL.Host, "443") } case "": return nil, errors.New("specify scheme explicitly (https://)") default: - return nil, errors.New("scheme " + proxyUrl.Scheme + " is not supported") + return nil, errors.New("scheme " + proxyURL.Scheme + " is not supported") } client := &HTTPConnectDialer{ - ProxyUrl: *proxyUrl, + ProxyURL: *proxyURL, DefaultHeader: make(http.Header), SpkiFP: nil, EnableH2ConnReuse: true, } - if proxyUrl.User != nil { - if proxyUrl.User.Username() != "" { - password, _ := proxyUrl.User.Password() + if proxyURL.User != nil { + if proxyURL.User.Username() != "" { + password, _ := proxyURL.User.Password() client.DefaultHeader.Set("Proxy-Authorization", "Basic "+ - base64.StdEncoding.EncodeToString([]byte(proxyUrl.User.Username()+":"+password))) + base64.StdEncoding.EncodeToString([]byte(proxyURL.User.Username()+":"+password))) } } return client, nil @@ -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 @@ -191,24 +191,25 @@ func (c *HTTPConnectDialer) DialContext(ctx context.Context, network, address st var err error var rawConn net.Conn negotiatedProtocol := "" - switch c.ProxyUrl.Scheme { + switch c.ProxyURL.Scheme { case "http": - rawConn, err = c.Dialer.DialContext(ctx, network, c.ProxyUrl.Host) + rawConn, err = c.Dialer.DialContext(ctx, network, c.ProxyURL.Host) if err != nil { return nil, err } case "https": if c.DialTLS != nil { - rawConn, negotiatedProtocol, err = c.DialTLS(network, c.ProxyUrl.Host) + rawConn, negotiatedProtocol, err = c.DialTLS(network, c.ProxyURL.Host) if err != nil { return nil, err } } else { tlsConf := tls.Config{ 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 { return nil, err } @@ -220,7 +221,7 @@ func (c *HTTPConnectDialer) DialContext(ctx context.Context, network, address st rawConn = tlsConn } default: - return nil, errors.New("scheme " + c.ProxyUrl.Scheme + " is not supported") + return nil, errors.New("scheme " + c.ProxyURL.Scheme + " is not supported") } switch negotiatedProtocol { @@ -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 { diff --git a/httpclient_test.go b/httpclient_test.go index c4cd920b..700a5a1b 100644 --- a/httpclient_test.go +++ b/httpclient_test.go @@ -3,6 +3,7 @@ package forwardproxy import ( "crypto/tls" + "fmt" "net" "sync" "testing" @@ -12,22 +13,30 @@ import ( ) func TestHttpClient(t *testing.T) { - _test := func(proxyUrl string) { - for _, httpProxyVer := range testHttpProxyVersions { - for _, httpTargetVer := range testHttpTargetVersions { + _test := func(urlSchemeAndCreds, urlAddress string) { + for _, httpProxyVer := range testHTTPProxyVersions { + for _, httpTargetVer := range testHTTPTargetVersions { for _, resource := range testResources { - dialer, err := httpclient.NewHTTPConnectDialer(proxyUrl) + // always dial localhost for testing purposes + proxyURL := fmt.Sprintf("%s@%s", urlSchemeAndCreds, urlAddress) + + dialer, err := httpclient.NewHTTPConnectDialer(proxyURL) if err != nil { t.Fatal(err) } dialer.DialTLS = func(network string, address string) (net.Conn, string, error) { - conn, err := tls.Dial(network, address, &tls.Config{InsecureSkipVerify: true, - NextProtos: []string{httpVersionToAlpn[httpProxyVer]}}) + // always dial localhost for testing purposes + conn, err := tls.Dial(network, address, &tls.Config{ + InsecureSkipVerify: true, + NextProtos: []string{httpVersionToALPN[httpProxyVer]}, + }) if err != nil { return nil, "", err } return conn, conn.ConnectionState().NegotiatedProtocol, nil } + + // always dial localhost for testing purposes conn, err := dialer.Dial("tcp", caddyTestTarget.addr) if err != nil { t.Fatal(err) @@ -43,8 +52,8 @@ func TestHttpClient(t *testing.T) { } } - _test("https://" + credentialsCorrectPlain + "@" + caddyForwardProxyAuth.addr) - _test("http://" + credentialsCorrectPlain + "@" + caddyHTTPForwardProxyAuth.addr) + _test("https://"+credentialsCorrectPlain, caddyForwardProxyAuth.addr) + _test("http://"+credentialsCorrectPlain, caddyHTTPForwardProxyAuth.addr) } func TestHttpClientH2Multiplexing(t *testing.T) { @@ -58,8 +67,11 @@ func TestHttpClientH2Multiplexing(t *testing.T) { t.Fatal(err) } dialer.DialTLS = func(network string, address string) (net.Conn, string, error) { - conn, err := tls.Dial(network, address, &tls.Config{InsecureSkipVerify: true, - NextProtos: []string{httpVersionToAlpn[httpProxyVer]}}) + // always dial localhost for testing purposes + conn, err := tls.Dial(network, address, &tls.Config{ + InsecureSkipVerify: true, + NextProtos: []string{httpVersionToALPN[httpProxyVer]}, + }) if err != nil { return nil, "", err } @@ -74,6 +86,7 @@ func TestHttpClientH2Multiplexing(t *testing.T) { _test := func() { defer wg.Done() for _, resource := range testResources { + // always dial localhost for testing purposes conn, err := dialer.Dial("tcp", caddyTestTarget.addr) if err != nil { t.Fatal(err) @@ -90,6 +103,7 @@ func TestHttpClientH2Multiplexing(t *testing.T) { _test() // do serially at least once for i := 0; i < retries; i++ { + // nolint:govet // this is a test go _test() time.Sleep(sleepInterval) } diff --git a/probe_resist_test.go b/probe_resist_test.go index e74adf97..4214c31a 100644 --- a/probe_resist_test.go +++ b/probe_resist_test.go @@ -4,17 +4,18 @@ import ( "bytes" "errors" "fmt" - "io/ioutil" + "io" + "net" "net/http" "strings" "testing" ) func TestGETAuthCorrectProbeResist(t *testing.T) { - useTls := true - for _, httpProxyVer := range testHttpProxyVersions { + const useTLS = true + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { - response, err := getViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyProbeResist.addr, httpProxyVer, credentialsCorrect, useTls) + response, err := getViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyProbeResist.addr, httpProxyVer, credentialsCorrect, useTLS) if err != nil { t.Fatal(err) } else if err = responseExpected(response, caddyTestTarget.contents[resource]); err != nil { @@ -25,47 +26,56 @@ func TestGETAuthCorrectProbeResist(t *testing.T) { } func TestGETAuthWrongProbeResist(t *testing.T) { - useTls := true + const useTLS = true for _, wrongCreds := range credentialsWrong { - for _, httpProxyVer := range testHttpProxyVersions { + for _, httpProxyVer := range testHTTPProxyVersions { for _, resource := range testResources { - responseProbeResist, err := getViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyProbeResist.addr, httpProxyVer, wrongCreds, useTls) + responseProbeResist, err := getViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyProbeResist.addr, httpProxyVer, wrongCreds, useTLS) if err != nil { t.Fatal(err) } // get response from reference server without forwardproxy and compare them - responseReference, err := getViaProxy(caddyTestTarget.addr, resource, caddyDummyProbeResist.addr, httpProxyVer, wrongCreds, useTls) + responseReference, err := getViaProxy(caddyTestTarget.addr, resource, caddyDummyProbeResist.addr, httpProxyVer, wrongCreds, useTLS) if err != nil { t.Fatal(err) } // as a sanity check, get 407 from simple authenticated forwardproxy - responseForwardProxy, err := getViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyAuth.addr, httpProxyVer, wrongCreds, useTls) + responseForwardProxy, err := getViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyAuth.addr, httpProxyVer, wrongCreds, useTLS) if err != nil { t.Fatal(err) } + if responseProbeResist.StatusCode != responseReference.StatusCode { t.Fatalf("Expected response: %d, Got: %d\n", responseReference.StatusCode, responseProbeResist.StatusCode) } if err = responsesAreEqual(responseProbeResist, responseReference); err != nil { - t.Fatal(err) + var e errorHeaderAlternativeServiceNotEqual + if !errors.As(err, &e) { + t.Fatal(err) + } + if err = e.CheckAlternativeServiceError(caddyForwardProxyProbeResist.addr, caddyDummyProbeResist.addr); err != nil { + t.Fatal(err) + } } if err = responsesAreEqual(responseProbeResist, responseForwardProxy); err == nil { - t.Fatal("Responses from servers with and without forwardproxy are expected to be different.") + t.Fatalf("Responses from servers with and without Probe Resistance are expected to be different."+ + "\nResponse from Caddy with ProbeResist: %v\nResponse from Caddy without ProbeResist: %v\n", + responseProbeResist, responseForwardProxy) } } for _, resource := range testResources { - responseProbeResist, err := getViaProxy(caddyForwardProxyProbeResist.addr, resource, caddyForwardProxyProbeResist.addr, httpProxyVer, wrongCreds, useTls) + responseProbeResist, err := getViaProxy(caddyForwardProxyProbeResist.addr, resource, caddyForwardProxyProbeResist.addr, httpProxyVer, wrongCreds, useTLS) if err != nil { t.Fatal(err) } // get response from reference server without forwardproxy and compare them - responseReference, err := getViaProxy(caddyDummyProbeResist.addr, resource, caddyDummyProbeResist.addr, httpProxyVer, wrongCreds, useTls) + responseReference, err := getViaProxy(caddyDummyProbeResist.addr, resource, caddyDummyProbeResist.addr, httpProxyVer, wrongCreds, useTLS) if err != nil { t.Fatal(err) } // as a sanity check, get 407 from simple authenticated forwardproxy - responseForwardProxy, err := getViaProxy(caddyForwardProxyAuth.addr, resource, caddyForwardProxyAuth.addr, httpProxyVer, wrongCreds, useTls) + responseForwardProxy, err := getViaProxy(caddyForwardProxyAuth.addr, resource, caddyForwardProxyAuth.addr, httpProxyVer, wrongCreds, useTLS) if err != nil { t.Fatal(err) } @@ -74,10 +84,18 @@ func TestGETAuthWrongProbeResist(t *testing.T) { responseProbeResist.StatusCode) } if err = responsesAreEqual(responseProbeResist, responseReference); err != nil { - t.Fatal(err) + var e errorHeaderAlternativeServiceNotEqual + if !errors.As(err, &e) { + t.Fatal(err) + } + if err = e.CheckAlternativeServiceError(caddyForwardProxyProbeResist.addr, caddyDummyProbeResist.addr); err != nil { + t.Fatal(err) + } } if err = responsesAreEqual(responseProbeResist, responseForwardProxy); err == nil { - t.Fatal("Responses from servers with and without forwardproxy are expected to be different.") + t.Fatalf("Responses from servers with and without Probe Resistance are expected to be different."+ + "\nResponse from Caddy with ProbeResist: %v\nResponse from Caddy without ProbeResist: %v\n", + responseProbeResist, responseForwardProxy) } } } @@ -86,16 +104,14 @@ func TestGETAuthWrongProbeResist(t *testing.T) { // test that responses on http redirect port are same func TestGETAuthWrongProbeResistRedir(t *testing.T) { - useTls := false + const useTLS = false httpProxyVer := "HTTP/1.1" for _, wrongCreds := range credentialsWrong { // request test target for _, resource := range testResources { - responseProbeResist, rPRerr := getViaProxy(caddyTestTarget.addr, resource, stripPort(caddyForwardProxyProbeResist.addr)+":"+caddyForwardProxyProbeResist.HTTPRedirectPort, - httpProxyVer, wrongCreds, useTls) + responseProbeResist, rPRerr := getViaProxy(caddyTestTarget.addr, resource, changePort(caddyForwardProxyProbeResist.addr, caddyForwardProxyProbeResist.httpRedirPort), httpProxyVer, wrongCreds, useTLS) // get response from reference server without forwardproxy and compare them - responseReference, rRerr := getViaProxy(caddyTestTarget.addr, resource, stripPort(caddyDummyProbeResist.addr)+":"+caddyDummyProbeResist.HTTPRedirectPort, - httpProxyVer, wrongCreds, useTls) + responseReference, rRerr := getViaProxy(caddyTestTarget.addr, resource, changePort(caddyDummyProbeResist.addr, caddyDummyProbeResist.httpRedirPort), httpProxyVer, wrongCreds, useTLS) if (rPRerr == nil && rRerr != nil) || (rPRerr != nil && rRerr == nil) { t.Fatalf("Reference error: %s. Probe resist error: %s", rRerr, rPRerr) } @@ -109,14 +125,12 @@ func TestGETAuthWrongProbeResistRedir(t *testing.T) { } // request self for _, resource := range testResources { - responseProbeResist, err := getViaProxy(caddyForwardProxyProbeResist.addr, resource, stripPort(caddyForwardProxyProbeResist.addr)+":"+caddyForwardProxyProbeResist.HTTPRedirectPort, - httpProxyVer, wrongCreds, useTls) + responseProbeResist, err := getViaProxy(caddyForwardProxyProbeResist.addr, resource, changePort(caddyForwardProxyProbeResist.addr, caddyForwardProxyProbeResist.httpRedirPort), httpProxyVer, wrongCreds, useTLS) if err != nil { t.Fatal(err) } // get response from reference server without forwardproxy and compare them - responseReference, err := getViaProxy(caddyDummyProbeResist.addr, resource, stripPort(caddyDummyProbeResist.addr)+":"+caddyDummyProbeResist.HTTPRedirectPort, - httpProxyVer, wrongCreds, useTls) + responseReference, err := getViaProxy(caddyDummyProbeResist.addr, resource, changePort(caddyDummyProbeResist.addr, caddyDummyProbeResist.httpRedirPort), httpProxyVer, wrongCreds, useTLS) if err != nil { t.Fatal(err) } @@ -132,11 +146,11 @@ func TestGETAuthWrongProbeResistRedir(t *testing.T) { } func TestConnectAuthCorrectProbeResist(t *testing.T) { - useTls := true - for _, httpProxyVer := range testHttpProxyVersions { - for _, httpTargetVer := range testHttpTargetVersions { + const useTLS = true + for _, httpProxyVer := range testHTTPProxyVersions { + for _, httpTargetVer := range testHTTPTargetVersions { for _, resource := range testResources { - response, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyProbeResist.addr, httpTargetVer, credentialsCorrect, httpProxyVer, useTls) + response, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyProbeResist.addr, httpTargetVer, credentialsCorrect, httpProxyVer, useTLS) if err != nil { t.Fatal(err) } else if err = responseExpected(response, caddyTestTarget.contents[resource]); err != nil { @@ -148,22 +162,22 @@ func TestConnectAuthCorrectProbeResist(t *testing.T) { } func TestConnectAuthWrongProbeResist(t *testing.T) { - useTls := true + const useTLS = true for _, wrongCreds := range credentialsWrong { - for _, httpProxyVer := range testHttpProxyVersions { - for _, httpTargetVer := range testHttpTargetVersions { + for _, httpProxyVer := range testHTTPProxyVersions { + for _, httpTargetVer := range testHTTPTargetVersions { for _, resource := range testResources { - responseProbeResist, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyProbeResist.addr, httpTargetVer, wrongCreds, httpProxyVer, useTls) + responseProbeResist, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyProbeResist.addr, httpTargetVer, wrongCreds, httpProxyVer, useTLS) if err != nil { t.Fatal(err) } // get response from reference server without forwardproxy and compare them - responseReference, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, caddyDummyProbeResist.addr, httpTargetVer, wrongCreds, httpProxyVer, useTls) + responseReference, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, caddyDummyProbeResist.addr, httpTargetVer, wrongCreds, httpProxyVer, useTLS) if err != nil { t.Fatal(err) } // as a sanity check, get 407 from simple authenticated forwardproxy - responseForwardProxy, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyAuth.addr, httpTargetVer, wrongCreds, httpProxyVer, useTls) + responseForwardProxy, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, caddyForwardProxyAuth.addr, httpTargetVer, wrongCreds, httpProxyVer, useTLS) if err != nil { t.Fatal(err) } @@ -172,10 +186,18 @@ func TestConnectAuthWrongProbeResist(t *testing.T) { responseReference.StatusCode, responseProbeResist.StatusCode) } if err = responsesAreEqual(responseProbeResist, responseReference); err != nil { - t.Fatal(err) + var e errorHeaderAlternativeServiceNotEqual + if !errors.As(err, &e) { + t.Fatal(err) + } + if err = e.CheckAlternativeServiceError(caddyForwardProxyProbeResist.addr, caddyDummyProbeResist.addr); err != nil { + t.Fatal(err) + } } if err = responsesAreEqual(responseProbeResist, responseForwardProxy); err == nil { - t.Fatal("Responses from servers with and without forwardproxy are expected to be different.") + t.Fatalf("Responses from servers with and without Probe Resistance are expected to be different."+ + "\nResponse from Caddy with ProbeResist: %v\nResponse from Caddy without ProbeResist: %v\n", + responseProbeResist, responseForwardProxy) } } // request self @@ -183,25 +205,33 @@ func TestConnectAuthWrongProbeResist(t *testing.T) { if httpTargetVer != httpProxyVer { continue } - responseProbeResist, err := connectAndGetViaProxy(caddyForwardProxyProbeResist.addr, resource, caddyForwardProxyProbeResist.addr, httpTargetVer, wrongCreds, httpProxyVer, useTls) + responseProbeResist, err := connectAndGetViaProxy(caddyForwardProxyProbeResist.addr, resource, caddyForwardProxyProbeResist.addr, httpTargetVer, wrongCreds, httpProxyVer, useTLS) if err != nil { t.Fatal(err) } // get response from reference server without forwardproxy and compare them - responseReference, err := connectAndGetViaProxy(caddyDummyProbeResist.addr, resource, caddyDummyProbeResist.addr, httpTargetVer, wrongCreds, httpProxyVer, useTls) + responseReference, err := connectAndGetViaProxy(caddyDummyProbeResist.addr, resource, caddyDummyProbeResist.addr, httpTargetVer, wrongCreds, httpProxyVer, useTLS) if err != nil { t.Fatal(err) } // as a sanity check, get 407 from simple authenticated forwardproxy - responseForwardProxy, err := connectAndGetViaProxy(caddyForwardProxyAuth.addr, resource, caddyForwardProxyAuth.addr, httpTargetVer, wrongCreds, httpProxyVer, useTls) + responseForwardProxy, err := connectAndGetViaProxy(caddyForwardProxyAuth.addr, resource, caddyForwardProxyAuth.addr, httpTargetVer, wrongCreds, httpProxyVer, useTLS) if err != nil { t.Fatal(err) } if err = responsesAreEqual(responseProbeResist, responseReference); err != nil { - t.Fatal(err) + var e errorHeaderAlternativeServiceNotEqual + if !errors.As(err, &e) { + t.Fatal(err) + } + if err = e.CheckAlternativeServiceError(caddyForwardProxyProbeResist.addr, caddyDummyProbeResist.addr); err != nil { + t.Fatal(err) + } } if err = responsesAreEqual(responseProbeResist, responseForwardProxy); err == nil { - t.Fatal("Responses from servers with and without forwardproxy are expected to be different.") + t.Fatalf("Responses from servers with and without Probe Resistance are expected to be different."+ + "\nResponse from Caddy with ProbeResist: %v\nResponse from Caddy without ProbeResist: %v\n", + responseProbeResist, responseForwardProxy) } } } @@ -211,20 +241,18 @@ func TestConnectAuthWrongProbeResist(t *testing.T) { // test that responses on http redirect port are same func TestConnectAuthWrongProbeResistRedir(t *testing.T) { - useTls := false + const useTLS = false httpProxyVer := "HTTP/1.1" for _, wrongCreds := range credentialsWrong { - for _, httpTargetVer := range testHttpTargetVersions { + for _, httpTargetVer := range testHTTPTargetVersions { // request test target for _, resource := range testResources { - responseProbeResist, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, stripPort(caddyForwardProxyProbeResist.addr)+":"+caddyForwardProxyProbeResist.HTTPRedirectPort, - httpTargetVer, wrongCreds, httpProxyVer, useTls) + responseProbeResist, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, changePort(caddyForwardProxyProbeResist.addr, caddyForwardProxyProbeResist.httpRedirPort), httpTargetVer, wrongCreds, httpProxyVer, useTLS) if err != nil { t.Fatal(err) } // get response from reference server without forwardproxy and compare them - responseReference, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, stripPort(caddyDummyProbeResist.addr)+":"+caddyDummyProbeResist.HTTPRedirectPort, - httpTargetVer, wrongCreds, httpProxyVer, useTls) + responseReference, err := connectAndGetViaProxy(caddyTestTarget.addr, resource, changePort(caddyDummyProbeResist.addr, caddyDummyProbeResist.httpRedirPort), httpTargetVer, wrongCreds, httpProxyVer, useTLS) if err != nil { t.Fatal(err) } @@ -238,14 +266,12 @@ func TestConnectAuthWrongProbeResistRedir(t *testing.T) { } // request self for _, resource := range testResources { - responseProbeResist, err := connectAndGetViaProxy(caddyForwardProxyProbeResist.addr, resource, stripPort(caddyForwardProxyProbeResist.addr)+":"+caddyForwardProxyProbeResist.HTTPRedirectPort, - httpTargetVer, wrongCreds, httpProxyVer, useTls) + responseProbeResist, err := connectAndGetViaProxy(caddyForwardProxyProbeResist.addr, resource, changePort(caddyForwardProxyProbeResist.addr, caddyForwardProxyProbeResist.httpRedirPort), httpTargetVer, wrongCreds, httpProxyVer, useTLS) if err != nil { t.Fatal(err) } // get response from reference server without forwardproxy and compare them - responseReference, err := connectAndGetViaProxy(caddyDummyProbeResist.addr, resource, stripPort(caddyDummyProbeResist.addr)+":"+caddyDummyProbeResist.HTTPRedirectPort, - httpTargetVer, wrongCreds, httpProxyVer, useTls) + responseReference, err := connectAndGetViaProxy(caddyDummyProbeResist.addr, resource, changePort(caddyDummyProbeResist.addr, caddyDummyProbeResist.httpRedirPort), httpTargetVer, wrongCreds, httpProxyVer, useTLS) if err != nil { t.Fatal(err) } @@ -261,6 +287,37 @@ func TestConnectAuthWrongProbeResistRedir(t *testing.T) { } } +type errorHeaderAlternativeServiceNotEqual struct { + ValueA []string + ValueB []string +} + +func (e errorHeaderAlternativeServiceNotEqual) Error() string { + return fmt.Sprintf("header 'Alt-Svc' not equal: %v, %v\n", e.ValueA, e.ValueB) +} + +func (e errorHeaderAlternativeServiceNotEqual) CheckAlternativeServiceError(serverAddrA, serverAddrB string) error { + if len(e.ValueA) == 0 || len(e.ValueB) == 0 { + return fmt.Errorf("header 'Alt-Svc' is empty: %w", e) + } + _, port, err := net.SplitHostPort(serverAddrA) + if err != nil { + return fmt.Errorf("failed to split server address :%w", err) + } + if !strings.Contains(e.ValueA[0], port) { + return fmt.Errorf("Alt-Svc address :%s does not contain the server port: %s", e.ValueA[0], port) + } + _, port, err = net.SplitHostPort(serverAddrB) + if err != nil { + return fmt.Errorf("failed to split server address :%w", err) + } + if !strings.Contains(e.ValueB[0], port) { + return fmt.Errorf("Alt-Svc address :%s does not contain the server port: %s", e.ValueB[0], port) + } + + return nil +} + // returns nil if are equal func responsesAreEqual(res1, res2 *http.Response) error { if res1 == nil { @@ -270,36 +327,31 @@ func responsesAreEqual(res1, res2 *http.Response) error { return errors.New("res2 is nil") } if res1.Status != res2.Status { - return errors.New("Status is different") + return fmt.Errorf("status is different; %s != %s", res1.Status, res2.Status) } if res1.StatusCode != res2.StatusCode { - return errors.New("StatusCode is different") + return fmt.Errorf("status code is different; %d != %d", res1.StatusCode, res2.StatusCode) } - if res1.ProtoMajor != res2.ProtoMajor { - return errors.New("ProtoMajor is different") + return fmt.Errorf("proto major is different; %d != %d", res1.ProtoMajor, res2.ProtoMajor) } - - if res1.Close != res2.Close { - return errors.New("Close is different") - } - if res1.ProtoMinor != res2.ProtoMinor { - return errors.New("ProtoMinor is different") + return fmt.Errorf("proto minor is different; %d != %d", res1.ProtoMinor, res2.ProtoMinor) + } + if res1.Close != res2.Close { + return fmt.Errorf("close is different; %t != %t", res1.Close, res2.Close) } - if res1.ContentLength != res2.ContentLength { - return errors.New("ContentLength is different") + return fmt.Errorf("content length is different; %d != %d", res1.ContentLength, res2.ContentLength) } - if res1.Uncompressed != res2.Uncompressed { - return errors.New("Uncompressed is different") + return fmt.Errorf("uncompressed is different; %t != %t", res1.Uncompressed, res2.Uncompressed) } if res1.Proto != res2.Proto { - return errors.New("Proto is different") + return fmt.Errorf("proto is different; %s != %s", res1.Proto, res2.Proto) } if len(res1.TransferEncoding) != len(res2.TransferEncoding) { - return errors.New("TransferEncodings have different length") + return fmt.Errorf("transfer encodings have different lenght; %d != %d", len(res1.TransferEncoding), len(res2.TransferEncoding)) } // returns "" if equal @@ -318,6 +370,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]) @@ -334,6 +387,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" { @@ -341,28 +395,23 @@ func responsesAreEqual(res1, res2 *http.Response) error { } v2, ok := res2.Header[k1] if !ok { - return errors.New(fmt.Sprintf("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) - } + return fmt.Errorf("header \"%s: %s\" is absent in res2", k1, v1) } if errStr = stringSlicesAreEqual(v1, v2); errStr != "" { - return errors.New(fmt.Sprintf("Header \"%s\" is different: %s", k1, errStr)) + if k1 == "Alt-Svc" { + return errorHeaderAlternativeServiceNotEqual{v1, v2} + } + 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 { - return errors.New(fmt.Sprintf("Bodies are different: %s. n1 = %d, n2 = %d. err1 = %v, err2 = %v. buf1 = %s, buf2 = %s", - s, n1, n2, err1, err2, buf1[:n1], buf2[:n2])) + return fmt.Errorf("bodies are different: %s. n1 = %d, n2 = %d. err1 = %v, err2 = %v. buf1 = %s, buf2 = %s", + s, n1, n2, err1, err2, buf1[:n1], buf2[:n2]) } if n2 != n1 { return makeBodyError("Body sizes are different") @@ -383,13 +432,17 @@ 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 { + panic(err) + } + return net.JoinHostPort(host, toPort) } diff --git a/setup.go b/setup.go deleted file mode 100644 index 1c0bd43d..00000000 --- a/setup.go +++ /dev/null @@ -1,409 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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 forwardproxy - -import ( - "bufio" - "context" - "crypto/tls" - "encoding/base64" - "errors" - "fmt" - "log" - "net" - "net/http" - "net/url" - "os" - "strconv" - "strings" - "sync" - "time" - - "github.com/caddyserver/caddy" - "github.com/caddyserver/caddy/caddyhttp/httpserver" - "github.com/caddyserver/forwardproxy/httpclient" - "golang.org/x/net/proxy" -) - -func setup(c *caddy.Controller) error { - httpserver.GetConfig(c).FallbackSite = true - fp := &ForwardProxy{ - dialTimeout: time.Second * 20, - hostname: httpserver.GetConfig(c).Host(), port: httpserver.GetConfig(c).Port(), - httpTransport: http.Transport{ - Proxy: http.ProxyFromEnvironment, - MaxIdleConns: 50, - IdleConnTimeout: 60 * time.Second, - TLSHandshakeTimeout: 10 * time.Second, - }, - } - - c.Next() // skip the directive name - - args := c.RemainingArgs() - if len(args) > 0 { - return c.ArgErr() - } - - for c.NextBlock() { - subdirective := c.Val() - args := c.RemainingArgs() - switch subdirective { - case "basicauth": - if len(args) != 2 { - return c.ArgErr() - } - if len(args[0]) == 0 { - return c.Err("empty usernames are not allowed") - } - // TODO: Evaluate policy of allowing empty passwords. - if strings.Contains(args[0], ":") { - return c.Err("character ':' in usernames is not allowed") - } - if fp.authCredentials == nil { - fp.authCredentials = [][]byte{} - } - // base64-encode credentials - buf := make([]byte, base64.StdEncoding.EncodedLen(len(args[0])+1+len(args[1]))) - base64.StdEncoding.Encode(buf, []byte(args[0]+":"+args[1])) - fp.authCredentials = append(fp.authCredentials, buf) - fp.authRequired = true - case "ports": - if len(args) == 0 { - return c.ArgErr() - } - if len(fp.whitelistedPorts) != 0 { - return c.Err("ports subdirective specified twice") - } - fp.whitelistedPorts = make([]int, len(args)) - for i, p := range args { - intPort, err := strconv.Atoi(p) - if intPort <= 0 || intPort > 65535 || err != nil { - return c.Err("ports are expected to be space-separated" + - " and in 0-65535 range. Got: " + p) - } - fp.whitelistedPorts[i] = intPort - } - case "hide_ip": - if len(args) != 0 { - return c.ArgErr() - } - fp.hideIP = true - case "hide_via": - if len(args) != 0 { - return c.ArgErr() - } - fp.hideVia = true - case "probe_resistance": - if len(args) > 1 { - return c.ArgErr() - } - fp.probeResistEnabled = true - if len(args) == 1 { - lowercaseArg := strings.ToLower(args[0]) - if lowercaseArg != args[0] { - log.Println("WARNING: secret domain appears to have uppercase letters in it, which are not visitable") - } - fp.probeResistDomain = args[0] - } - case "serve_pac": - if len(args) > 1 { - return c.ArgErr() - } - if len(fp.pacFilePath) != 0 { - return c.Err("serve_pac subdirective specified twice") - } - if len(args) == 1 { - fp.pacFilePath = args[0] - if !strings.HasPrefix(fp.pacFilePath, "/") { - fp.pacFilePath = "/" + fp.pacFilePath - } - } else { - fp.pacFilePath = "/proxy.pac" - } - log.Printf("Proxy Auto-Config will be served at %s%s\n", fp.hostname, fp.pacFilePath) - case "response_timeout": - if len(args) != 1 { - return c.ArgErr() - } - timeout, err := strconv.Atoi(args[0]) - if err != nil { - return c.ArgErr() - } - if timeout < 0 { - return c.Err("response_timeout cannot be negative.") - } - fp.httpTransport.ResponseHeaderTimeout = time.Duration(timeout) * time.Second - case "dial_timeout": - if len(args) != 1 { - return c.ArgErr() - } - timeout, err := strconv.Atoi(args[0]) - if err != nil { - return c.ArgErr() - } - if timeout < 0 { - return c.Err("dial_timeout cannot be negative.") - } - fp.dialTimeout = time.Second * time.Duration(timeout) - case "upstream": - if len(args) != 1 { - return c.ArgErr() - } - if fp.upstream != nil { - return c.Err("upstream directive specified more than once") - } - var err error - fp.upstream, err = url.Parse(args[0]) - if err != nil { - return c.Err("failed to parse upstream address: " + err.Error()) - } - case "acl": - if len(args) != 0 { - return c.Err("acl should be only subdirective on the line") - } - args := c.RemainingArgs() - if len(args) > 0 { - return c.ArgErr() - } - c.Next() - if c.Val() != "{" { - return c.Err("acl directive must be followed by opening curly braces \"{\"") - } - for { - if !c.Next() { - return c.Err("acl blockmust be ended by closing curly braces \"}\"") - } - aclDirective := c.Val() - args := c.RemainingArgs() - if aclDirective == "}" { - break - } - if len(args) == 0 { - return c.ArgErr() - } - var ruleSubjects []string - var err error - aclAllow := false - switch aclDirective { - case "allow": - ruleSubjects = args[:] - aclAllow = true - case "allowfile": - if len(args) != 1 { - return c.Err("allowfile accepts a single filename argument") - } - ruleSubjects, err = readLinesFromFile(args[0]) - if err != nil { - return err - } - aclAllow = true - case "deny": - ruleSubjects = args[:] - case "denyfile": - if len(args) != 1 { - return c.Err("denyfile accepts a single filename argument") - } - ruleSubjects, err = readLinesFromFile(args[0]) - if err != nil { - return err - } - default: - return c.Err("expected acl directive: allow/allowfile/deny/denyfile." + - "got: " + aclDirective) - } - for _, rs := range ruleSubjects { - ar, err := newAclRule(rs, aclAllow) - if err != nil { - return err - } - fp.aclRules = append(fp.aclRules, ar) - } - } - default: - return c.ArgErr() - } - } - - if fp.upstream != nil && (fp.aclRules != nil || len(fp.whitelistedPorts) != 0) { - return c.Err("upstream subdirective is incompatible with acl/ports subdirectives") - } - - for _, ipDeny := range []string{ - "10.0.0.0/8", - "127.0.0.0/8", - "172.16.0.0/12", - "192.168.0.0/16", - "::1/128", - "fe80::/10", - } { - ar, err := newAclRule(ipDeny, false) - if err != nil { - panic(err) - } - fp.aclRules = append(fp.aclRules, ar) - } - fp.aclRules = append(fp.aclRules, &aclAllRule{allow: true}) - - if fp.probeResistEnabled { - if !fp.authRequired { - return c.Err("probing resistance requires authentication: " + - "add `basicauth username password` to forwardproxy") - } - if len(fp.probeResistDomain) > 0 { - log.Printf("Secret domain used to connect to proxy: %s\n", fp.probeResistDomain) - } - } - - dialer := &net.Dialer{ - Timeout: fp.dialTimeout, - KeepAlive: 30 * time.Second, - DualStack: true, - } - fp.dialContext = dialer.DialContext - fp.httpTransport.DialContext = func(ctx context.Context, network string, address string) (net.Conn, error) { - conn, err := fp.dialContextCheckACL(ctx, network, address) - if err != nil { - return conn, err - } - return conn, nil - } - - if fp.upstream != nil { - if !isLocalhost(fp.upstream.Hostname()) && fp.upstream.Scheme != "https" { - return errors.New("insecure schemes are only allowed to localhost upstreams") - } - - registerHTTPDialer := func(u *url.URL, _ proxy.Dialer) (proxy.Dialer, error) { - // CONNECT request is proxied as-is, so we don't care about target url, but it could be - // useful in future to implement policies of choosing between multiple upstream servers. - // Given dialer is not used, since it's the same dialer provided by us. - d, err := httpclient.NewHTTPConnectDialer(fp.upstream.String()) - if err != nil { - return nil, err - } - d.Dialer = *dialer - if isLocalhost(fp.upstream.Hostname()) && fp.upstream.Scheme == "https" { - // disabling verification helps with testing the package and setups - // either way, it's impossible to have a legit TLS certificate for "127.0.0.1" - log.Println("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}) - if err != nil { - return nil, "", err - } - return conn, conn.ConnectionState().NegotiatedProtocol, nil - } - } - return d, nil - } - proxy.RegisterDialerType("https", registerHTTPDialer) - proxy.RegisterDialerType("http", registerHTTPDialer) - - upstreamDialer, err := proxy.FromURL(fp.upstream, dialer) - if err != nil { - return errors.New("failed to create proxy to upstream: " + err.Error()) - } - - if ctxDialer, ok := upstreamDialer.(interface { - DialContext(ctx context.Context, network, address string) (net.Conn, error) - }); ok { - // upstreamDialer has DialContext - use it - fp.dialContext = ctxDialer.DialContext - } else { - // upstreamDialer does not have DialContext - ignore the context :( - fp.dialContext = func(ctx context.Context, network string, address string) (net.Conn, error) { - return upstreamDialer.Dial(network, address) - } - } - } - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - fp.Next = next - return fp - }) - - makeBuffer := func() interface{} { return make([]byte, 0, 32*1024) } - bufferPool = sync.Pool{New: makeBuffer} - return nil -} - -func init() { - caddy.RegisterPlugin("forwardproxy", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -func isLocalhost(hostname string) bool { - if hostname == "localhost" || hostname == "127.0.0.1" || hostname == "::1" { - return true - } - return false -} - -func readLinesFromFile(filename string) ([]string, error) { - file, err := os.Open(filename) - if err != nil { - return nil, err - } - defer file.Close() - - var hostnames []string - scanner := bufio.NewScanner(file) - for scanner.Scan() { - hostnames = append(hostnames, scanner.Text()) - } - - return hostnames, scanner.Err() -} - -// isValidDomainLite shamelessly rejects non-LDH names. returns nil if domains seems valid -func isValidDomainLite(domain string) error { - for i := 0; i < len(domain); i++ { - c := domain[i] - if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || '0' <= c && c <= '9' || - c == '-' || c == '.' { - continue - } - return errors.New("character " + string(c) + " is not allowed") - } - sections := strings.Split(domain, ".") - for _, s := range sections { - if len(s) == 0 { - return errors.New("empty section between dots in domain name or trailing dot") - } - if len(s) > 63 { - return errors.New("domain name section is too long") - } - } - return nil -} - -type ProxyError struct { - S string - Code int -} - -func (e *ProxyError) Error() string { - return fmt.Sprintf("[%v] %s", e.Code, e.S) -} - -func (e *ProxyError) SplitCodeError() (int, error) { - if e == nil { - return 200, nil - } - return e.Code, errors.New(e.S) -} diff --git a/setup_test.go b/setup_test.go deleted file mode 100644 index 2497f0a1..00000000 --- a/setup_test.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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 forwardproxy - -import ( - "testing" - - "github.com/caddyserver/caddy" -) - -func TestSetup(t *testing.T) { - c := caddy.NewTestController("http", "forwardproxy string") - err := setup(c) - if err == nil { - t.Fatal("Expected: failure. Got: success. Input: forwardproxy string") - } - - testParsing := func(subdirectives []string, shouldSucceed bool) { - input := "forwardproxy" - if len(subdirectives) > 0 { - input += " {\n" - for _, s := range subdirectives { - input += s + "\n" - } - input += "}" - } - c := caddy.NewTestController("http", input) - err := setup(c) - if shouldSucceed && err != nil { - t.Fatalf("Expected: success. Got: %v. Input:\n%s\n", err, input) - } - if !shouldSucceed && err == nil { - t.Fatalf("Expected: failure. Got: success. Input:\n%s\n", input) - } - } - testParsing(nil, true) - testParsing([]string{}, true) - testParsing([]string{"qweqwe"}, false) - testParsing([]string{"0"}, false) - - testParsing([]string{"basicauth john"}, false) - testParsing([]string{"basicauth john \"\""}, true) - testParsing([]string{"basicauth john", "basicauth john \"\""}, false) - testParsing([]string{"basicauth john doe"}, true) - testParsing([]string{"basicauth john doe foo"}, false) - testParsing([]string{"basicauth john doe foo bar"}, false) - testParsing([]string{"basicauth \"\" doe"}, false) - testParsing([]string{"basicauth \"\" \"\""}, false) - testParsing([]string{"basicauth 0"}, false) - testParsing([]string{"basicauth 0 0"}, true) - testParsing([]string{"basicauth 0 0 0"}, false) - testParsing([]string{"basicauth 秘密"}, false) - testParsing([]string{"basicauth 秘密 秘密"}, true) - testParsing([]string{"basicauth 秘密 秘密 秘密"}, false) - testParsing([]string{"basicauth cyrillic пароль"}, true) - testParsing([]string{"basicauth john \"\"", "basicauth john doe", "basicauth 0 0", "basicauth 秘密 秘密", "basicauth cyrillic пароль"}, true) - - testParsing([]string{"ports"}, false) - testParsing([]string{"ports 0"}, false) - testParsing([]string{"ports 0 1"}, false) - testParsing([]string{"ports -1"}, false) - testParsing([]string{"ports hi!"}, false) - testParsing([]string{"ports 11, 122, 33"}, false) - testParsing([]string{"ports 11, 122, 33"}, false) - testParsing([]string{"ports 11111 99999"}, false) - testParsing([]string{"ports 11 12"}, true) - testParsing([]string{"ports 1"}, true) - testParsing([]string{"ports 1 11 111 332 324 6546 33333"}, true) - testParsing([]string{"ports 1 11 111 332 324 6546 33333", "ports 1 11 111 332 324 6546 33333"}, false) - testParsing([]string{"ports 1", "ports 2"}, false) - - testParsing([]string{"hide_ip"}, true) - testParsing([]string{"hide_ip 0"}, false) - testParsing([]string{"hide_ip 0 1"}, false) - - testParsing([]string{"hide_via"}, true) - testParsing([]string{"hide_via 0"}, false) - testParsing([]string{"hide_via 0 1"}, false) - - testParsing([]string{"probe_resistance"}, false) - testParsing([]string{"probe_resistance local.host"}, false) - testParsing([]string{"probe_resistance local.host very.local.host"}, false) - testParsing([]string{"probe_resistance", "basicauth john doe"}, true) - testParsing([]string{"probe_resistance local.host", "basicauth john doe"}, true) - testParsing([]string{"probe_resistance local.host very.local.host", "basicauth john doe"}, false) - - testParsing([]string{"serve_pac"}, true) - testParsing([]string{"serve_pac \"\""}, true) - testParsing([]string{"serve_pac proxyautoconfig.pac"}, true) - testParsing([]string{"serve_pac 1.pac 2.pac"}, false) - - testParsing([]string{"response_timeout"}, false) - testParsing([]string{"response_timeout -1"}, false) - testParsing([]string{"response_timeout 1 2"}, false) - testParsing([]string{"response_timeout seven"}, false) - testParsing([]string{"response_timeout 2"}, true) - - testParsing([]string{"dial_timeout"}, false) - testParsing([]string{"dial_timeout -1"}, false) - testParsing([]string{"dial_timeout 1 2"}, false) - testParsing([]string{"dial_timeout seven"}, false) - testParsing([]string{"dial_timeout 2"}, true) - - testParsing([]string{"upstream proxy.site"}, false) - testParsing([]string{"upstream https://proxy.site https://proxy.site"}, false) - testParsing([]string{"upstream http://localhost:1230"}, true) - testParsing([]string{"upstream socks5://127.0.0.1:999"}, true) - testParsing([]string{"upstream http://proxy.site"}, false) - testParsing([]string{"upstream https://proxy.site https://proxy.site"}, false) - testParsing([]string{"upstream https://proxy.site"}, true) - testParsing([]string{"upstream https://caddyserver.com", "acl {\nallow all\n}"}, false) - testParsing([]string{"upstream https://caddyserver.com", "ports 123"}, false) - testParsing([]string{"upstream https://username:password@caddyserver.com", "ports 123"}, false) - testParsing([]string{"upstream https://username:password@caddyserver.com:90", "ports 123"}, false) - - testParsing([]string{"acl {\nallow all\n}"}, true) - testParsing([]string{"acl {\nallow localhost 128.32.22.1/32 1.1.1.1 caddyserver.com\n deny all\n}"}, true) - testParsing([]string{"acl {\nallowfile test/parseable_acl.txt\n}"}, true) - testParsing([]string{"acl {\ndenyfile test/parseable_acl.txt\n}"}, true) - testParsing([]string{"acl {\nallowfile test/unparseable_acl.txt\n}"}, false) - testParsing([]string{"acl {\ndenyfile test/unparseable_acl.txt\n}"}, false) - //testParsing([]string{"acl {\nallow all\n"}, false) // doesn't fail, but should: caddy itself doesn't demand curly brace to be closed - testParsing([]string{"acl {\nallow all\n", "serve_pac"}, false) // this does fail - testParsing([]string{"acl \nallow all\n}"}, false) - testParsing([]string{"acl {allow all\n}"}, false) - //testParsing([]string{"acl {\nallow all}"}, false) // '}' is not on the next line, "all}" parses as regexp -}