Compare commits

...

120 Commits

Author SHA1 Message Date
Francis Lavoie 6d010189a5 Implement success ratio in health checks 2023-04-15 11:34:09 -04:00
Francis Lavoie 2c61b50b5f Add min_successes 2023-04-15 11:34:09 -04:00
Francis Lavoie c8b8c3a7b2 Add min_success_ratio WIP 2023-04-15 11:34:09 -04:00
Francis Lavoie c4b934f232 Add caddyhttp.Ratio type 2023-04-15 11:34:09 -04:00
Francis Lavoie cf69cd7b27 Add success_duration 2023-04-15 11:34:09 -04:00
Francis Lavoie 998c6e06a7 chore: Adjustments to CI caching (#5495) 2023-04-14 21:38:33 -04:00
Francis Lavoie 4636109ce1 reverseproxy: Remove deprecated lookup_srv (#5396) 2023-04-10 20:08:40 +00:00
Matt Holt 205b142614 cmd: Support ' quotes in envfile parsing (#5437) 2023-04-10 13:55:45 -06:00
Matt Holt ff35ba9ec3 Update contributing guidelines (#5466)
* Update contributing guidelines

* Request disclosure as a courtesy
2023-04-10 13:08:32 -06:00
WeidiDeng d8d87a378f caddyhttp: Serve http2 when listener wrapper doesn't return *tls.Conn (#4929)
* Serve http2 when listener wrapper doesn't return *tls.Conn

* close conn when h2server serveConn returns

* merge from upstream

* rebase from latest

* run New and Closed ConnState hook for h2 conns

* go fmt

* fix lint

* Add comments

* reorder import
2023-04-10 17:05:02 +00:00
Francis Lavoie f8b59e77f8 reverseproxy: Add query and client_ip_hash lb policies (#5468) 2023-04-04 03:31:47 +00:00
Matthew Holt 508cf2aa22 cmd: Create pidfile before config load (close #5477) 2023-04-03 11:57:16 -06:00
Kid f9bd2d3e92 fileserver: Add color-scheme meta tag (#5475) 2023-04-02 22:44:21 -04:00
dependabot[bot] b1366c7e46 build(deps): bump actions/setup-go from 3 to 4 (#5474)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-02 00:36:16 -04:00
Corin Langosch b6fe5d4b41 proxyprotocol: Add PROXY protocol support to reverse_proxy, add HTTP listener wrapper (#5424)
Co-authored-by: WeidiDeng <weidi_deng@icloud.com>
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2023-03-31 17:44:53 -04:00
Francis Lavoie 66e571e687 reverseproxy: Add mention of which half a copyBuffer err comes from (#5472)
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2023-03-31 15:46:29 -04:00
Francis Lavoie 2b3046de36 caddyhttp: Log request body bytes read (#5461) 2023-03-27 22:40:15 +00:00
Mohammed Al Sahaf 1aef807c71 log: Make sink logs encodable (#5441)
* log: make `sink` encodable

* deduplicate logger fields

* extract common fields into `BaseLog` and embed it into `SinkLog`

* amend godoc on `BaseLog` and `SinkLog`

* minor style change

---------

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2023-03-27 21:41:24 +00:00
Francis Lavoie e16a886814 caddytls: Eval replacer on automation policy subjects (#5459)
Also renamed the field to SubjectsRaw, which can be considered a breaking change but I don't expect this to affect much.
2023-03-27 21:16:22 +00:00
黑墨水鱼 dd86171d67 headers: Support deleting all headers as first op (#5464)
* Delete all existing fields when fieldName is `*`

* Rearrange deletion before addition in headers

* Revert "Rearrange deletion before addition in headers"

This reverts commit 1b50eeeccc92ccd660c7896d8283c7d9e5d1fcb0.

* Treat deleting all headers as a special case

* Apply suggestions from code review

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2023-03-27 21:05:18 +00:00
Francis Lavoie f5a13a4ab4 replacer: Add HTTP time format (#5458) 2023-03-27 20:51:13 +00:00
Francis Lavoie 10b265d252 reverseproxy: Header up/down support for CLI command (#5460) 2023-03-27 20:35:31 +00:00
Francis Lavoie 05e9974570 caddyhttp: Determine real client IP if trusted proxies configured (#5104)
* caddyhttp: Determine real client IP if trusted proxies configured

* Support customizing client IP header

* Implement client_ip matcher, deprecate remote_ip's forwarded option
2023-03-27 20:22:59 +00:00
Francis Lavoie 330be2d8c7 httpcaddyfile: Adjust path matcher sorting to solve for specificity (#5462) 2023-03-27 15:43:44 -04:00
Matt Holt 0cc49c053f caddytls: Zero out throttle window first (#5443)
* caddytls: Zero out throttle window first

* Don't error for on-demand 

Fixes https://github.com/caddyserver/caddy/commit/b97c76fb4789b8da0b80f5a2c1c1c5bebba163b5

---------

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2023-03-20 12:06:00 -06:00
Mohammed Al Sahaf a7db0cfe55 ci: add --yes to cosign arguments (#5440) 2023-03-17 10:36:59 -06:00
Trea Hauet 2182270a2c reverseproxy: Reset Content-Length to prevent FastCGI from hanging (#5435)
Fixes: https://github.com/caddyserver/caddy/issues/5420
2023-03-16 11:42:16 -06:00
Matthew Holt a7af7c486e caddytls: Allow on-demand w/o ask for internal-only 2023-03-14 10:29:27 -06:00
Matthew Holt b97c76fb47 caddytls: Require 'ask' endpoint for on-demand TLS 2023-03-14 10:02:44 -06:00
Matt Holt 6cc3cbbc69 fileserver: New file browse template (#5427)
* fileserver: New file browse template

* Redo extension/icon logic; minor color tweaks

* Fine-tune image display
2023-03-10 18:19:31 +00:00
Matthew Holt 9e943319b4 go.mod: Upgrade dependencies 2023-03-09 10:33:25 -07:00
Chris Reeves b420561737 tracing: Support autoprop from OTEL_PROPAGATORS (#5147)
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2023-03-09 09:02:35 -07:00
Marten Seemann c05e3898b9 caddyhttp: Enable 0-RTT QUIC (#5425) 2023-03-09 08:58:31 -07:00
WeidiDeng b3f0cea2c3 encode: flush status code when hijacked. (#5419) 2023-03-06 09:13:48 -07:00
esell 94d41a9d86 fileserver: Remove trailing slash on fs filenames (#5417) 2023-03-03 14:45:17 -07:00
Matt Holt 99d47050e9 core: Eliminate unnecessary shutdown delay on Unix (#5413)
* core: Eliminate unnecessary shutdown delay on Unix

Fix #5393, alternate to #5405

* Comments, cleanup, adjust logs

* Fix build constraint
2023-03-03 04:00:18 +00:00
Francis Lavoie 85375861f6 caddyhttp: Fix vars_regexp matcher with placeholders (#5408)
Changed to match the `vars` matcher's logic for handling placeholders
2023-03-02 09:01:54 -07:00
Francis Lavoie f6bab8ba85 context: Rename func to AppIfConfigured (#5397) 2023-02-27 18:58:27 +00:00
Emily Lange 941eae5f61 reverseproxy: allow specifying ip version for dynamic a upstream (#5401)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2023-02-27 17:23:09 +00:00
Mohammed Al Sahaf 096971e313 ci/cd: ship tarballs with vendored deps (#5403) 2023-02-26 22:06:15 +00:00
Francis Lavoie f3379f650a caddyfile: Fix heredoc fuzz crasher, drop trailing newline (#5404)
Co-authored-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2023-02-26 16:56:48 -05:00
Francis Lavoie 960150bb03 caddyfile: Implement heredoc support (#5385) 2023-02-26 00:34:27 +00:00
Francis Lavoie 9e6919550b cmd: Expand cobra support, add short flags (#5379)
* cmd: Expand cobra support

* Convert commands to cobra, add short flags

* Fix version command typo

Co-authored-by: Emily Lange <git@indeednotjames.com>

* Apply suggestions from code review

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>

---------

Co-authored-by: Emily Lange <git@indeednotjames.com>
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2023-02-24 16:09:12 -07:00
Matthew Holt 167981d258 ci: Update minimum Go version to 1.19 2023-02-24 13:45:44 -07:00
Matthew Holt 8cb1bb4af3 go.mod: Upgrade quic-go to v0.33.0 (Go 1.19 min) 2023-02-24 13:35:56 -07:00
Mohammed Al Sahaf e3909cc385 reverseproxy: refactor HTTP transport layer (#5369)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
Co-authored-by: Weidi Deng <weidi_deng@icloud.com>
2023-02-24 19:54:04 +00:00
Francis Lavoie be53e432fc caddytls: Relax the warning for on-demand (#5384) 2023-02-22 11:41:01 -07:00
Francis Lavoie 79de6df93d cmd: Strict unmarshal for validate (#5383) 2023-02-22 11:39:40 -07:00
WeidiDeng 8bc05e598d caddyfile: Implement variadics for import args placeholders (#5249)
* implement variadic placeholders
imported snippets reflect actual lines in file

* add import directive line number for imported snippets
add tests for parsing

* add realfile field to help debug import cycle detection.

* use file field to reflect import chain

* Switch syntax, deprecate old syntax, refactoring

- Moved the import args handling to a separate file
- Using {args[0:1]} syntax now
- Deprecate {args.*} syntax
- Use a replacer map for better control over the parsing
- Add plenty of warnings when invalid placeholders are detected
- Renaming variables, cleanup comments for readability
- More tests to cover edgecases I could think of
- Minor cleanup to snippet tracking in tokens, drop a redundant boolean field in tokens

---------

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2023-02-16 17:08:36 -07:00
Emily Lange bf54892a73 cmd: make caddy fmt hints more clear (#5378) 2023-02-16 16:34:12 -07:00
Francis Lavoie 5ded580444 cmd: Adjust documentation for commands (#5377) 2023-02-16 09:14:07 -07:00
Matthew Holt 0db29e2ce9 go.mod: Upgrade acmez and x/net
x/net 0.7.0 contains a security patch apparently.
2023-02-14 12:08:31 -07:00
Matt Holt 4b119a475f reverseproxy: Don't buffer chunked requests (fix #5366) (#5367)
* reverseproxy: Don't buffer chunked requests (fix #5366)

Mostly reverts 845bc4d50b (#5289)

Adds warning for unsafe config.

Deprecates unsafe properties in favor of simpler, safer designed ones.

* Update modules/caddyhttp/reverseproxy/caddyfile.go

Co-authored-by: Y.Horie <u5.horie@gmail.com>

* Update modules/caddyhttp/reverseproxy/reverseproxy.go

Co-authored-by: Y.Horie <u5.horie@gmail.com>

* Update modules/caddyhttp/reverseproxy/reverseproxy.go

Co-authored-by: Y.Horie <u5.horie@gmail.com>

* Remove unused code

---------

Co-authored-by: Y.Horie <u5.horie@gmail.com>
2023-02-11 17:25:29 -07:00
Francis Lavoie 90798f3eea go.mod: Upgrade various dependencies (#5362)
* chore: Upgrade various dependencies

* Support CEL file matcher with no args

* Document `http.request.orig_uri.path.*`, reorder placeholders in docs

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2023-02-08 17:49:17 +00:00
Steffen Brüheim 536c28d4dc core: Support Windows absolute paths for UDS proxy upstreams (#5114)
* added some tests for parseUpstreamDialAddress

Test 4 fails because it produces "[[::1]]:80" instead of "[::1]:80"

* support absolute windows path in unix reverse proxy address

* make IsUnixNetwork public, support +h2c and reuse it
* add new tests
2023-02-08 10:05:09 -07:00
WeidiDeng c77a6bea66 reverseproxy: Log status code and byte count for websockets (#5140)
* log response size for websocket request

* record size when using hijack bufio.Writer
2023-02-06 16:14:59 -07:00
Francis Lavoie 12bcbe2c49 caddyhttp: Pluggable trusted proxy IP range sources (#5328)
* caddyhttp: Pluggable trusted proxy IP range sources

* Add request to the IPRangeSource interface
2023-02-06 12:44:11 -07:00
Matthew Holt f6f1d8fc89 Run go.mod tidy 2023-02-06 12:24:01 -07:00
Y.Horie 8d3a1b8bcb caddyauth: Use singleflight for basic auth (#5344)
* caddyauth: Add singleflight for basic auth

* Fixes #5338
* it occurred the thunder herd problem like this https://medium.com/@mhrlife/avoid-duplicate-requests-while-filling-cache-98c687879f59

* Update modules/caddyhttp/caddyauth/basicauth.go

Fix comment

Co-authored-by: Francis Lavoie <lavofr@gmail.com>

---------

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2023-02-06 18:29:03 +00:00
Francis Lavoie ac83b7e218 admin: Add CADDY_ADMIN env var to override the default (#5332) 2023-02-06 17:55:16 +00:00
Francis Lavoie e62b5fb586 chore: Build with Go 1.20, keep minimum at 1.18 for now (#5353) 2023-02-06 11:29:20 -05:00
Amis Shokoohi 94b8d56096 cmd: Add --envfile flag to validate command (#5350)
Fixes https://github.com/caddyserver/caddy/issues/5346
2023-01-31 16:27:35 -05:00
Amis Shokoohi 8c0b49bf03 cmd: fmt exit successfully after overwriting config file (#5351)
Fixes https://github.com/caddyserver/caddy/issues/5349
2023-01-31 11:24:44 -05:00
Francis Lavoie 201b9b41f9 chore: Fix warning "range variable captured by func literal" (#5348) 2023-01-31 03:07:57 -05:00
Matthew Holt 0a3efd1641 caddytls: Debug log for ask endpoint 2023-01-30 09:30:53 -07:00
Y.Horie d73660f7c3 httpcaddyfile: Add persist_config global option (#5339)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2023-01-27 23:31:37 -05:00
Francis Lavoie 7f2a93e6c3 caddyfile: Allow overriding server names (#5323) 2023-01-27 14:56:39 -05:00
Y.Horie e9d95ab29f reverseproxy: Add flag to short command to disable redirects (#5330)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
Fixes undefined
2023-01-25 09:40:08 -05:00
David Frickert 962310204f tracing: Support placeholders in span name (#5329)
Fixes https://github.com/caddyserver/caddy/issues/5171
2023-01-25 02:26:44 -05:00
Brad Fitzpatrick 98867ac346 go.mod: bump tscert package to fix Tailscale 1.34+ on Windows (#5331)
As of Tailscale 1.34.0 on Windows, Tailscale now uses a named pipe to
connect to the local tailscale service.

This pulls in tailscale/tscert#5 as reported in tailscale/tscert#4.

(Sorry, we should've noticed this earlier!)

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2023-01-24 20:01:54 -05:00
Y.Horie 5805b3ca11 cmd: caddy fmt return code is 1 if not formatted (#5297)
* cmd: Fix caddy fmt if input isn't formatted

* Fixes #5294
* return exit 1 with an error message

* cmd: Use formattingDifference for caddy fmt

* #5294
* expose caddyfile.formattingDifference
2023-01-21 21:28:37 -07:00
Y.Horie d6d7511699 httpcaddyfile: Warn on importing empty file; skip dotfiles (#5320)
* httpcaddyfile: Change the parse rules when empty file or dotfile with a glob.

* Fixes #5295
* Empty file should just log a warning, and result in no tokens.
* The last segment of the path is '*', it should skip any dotfiles.
* The last segment of the path is '.*', it should read all dotfiles in a dir.

* httpcaddyfile: Regard empty files as import files which include only white space.
2023-01-21 10:22:36 -07:00
Y.Horie 8d6870fd06 chore: Fix typo, coral -> cobra (#5325) 2023-01-21 10:27:58 -05:00
WeidiDeng c38a040e85 httpcaddyfile: Fix handle grouping inside route (#5315)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2023-01-18 16:04:41 -05:00
Alexandre Vicenzi e8ad9b32c9 go.mod: Update golang.org/x/net to v0.5.0 (#5314) 2023-01-17 07:07:07 -05:00
Y.Horie 62e8b21724 chore: Fix caddyfile.replaceEnvVars return (#5311) 2023-01-17 06:57:42 -05:00
Francis Lavoie 223cbe3d0b caddyhttp: Add server-level trusted_proxies config (#5103) 2023-01-10 00:08:23 -05:00
Yannick Ihmels 66ce0c5c63 caddytls: Add test cases for Caddyfile tls options (#5293) 2023-01-09 15:18:12 -05:00
Y.Horie 845bc4d50b reverseproxy: Fix hanging for Transfer-Encoding: chunked (#5289)
* Fixes #5236
* enable request body buffering in reverse proxy
  when the request header has Transfer-Encoding: chunked
2023-01-09 00:13:34 -07:00
Emily Lange e450a7377b reverseproxy: Don't enable auto-https when --from flag is http (#5269) 2023-01-06 15:42:07 -05:00
Matt Holt d74f6fd967 reverseproxy: Set origreq in active health check (#5284)
* reverseproxy: Set origreq in active health check

Fix #5281

* Oops; dereference Request
2023-01-06 15:06:38 -05:00
Yannick Ihmels 55035d327a caddytls: Add dns_ttl config, improve Caddyfile tls options (#5287) 2023-01-06 14:44:00 -05:00
Matthew Holt 4e9ad50f65 fileserver: Add a couple test cases
With placeholders
2023-01-04 11:07:27 -07:00
Matt Holt 05a4637489 Update README.md
Attempt to fix logo that was appearing black in some browsers (perhaps due to CSP?).

Thanks to @IndeedNotJames for investigating! Hopefully this works.
2023-01-01 16:27:06 -07:00
Matt Holt bd74f94496 Update README.md
Update logo and fix test result badge
2022-12-31 10:10:32 -07:00
Francis Lavoie b40548ff61 ci: Fix goreleaser deprecation (#5270) 2022-12-28 13:11:39 -05:00
TAKAHASHI Shuuji 4e54e48409 ci: Update GitHub Actions to avoid set-output deprecation (#5271) 2022-12-28 12:05:42 -05:00
Mohammed Al Sahaf b166b90083 ci: exclude dependbot from running tests on s390x machine (#5266) 2022-12-22 14:13:47 -05:00
darkweak dac7cacd4d encode: Respect Cache-Control no-transform (#5257)
* encode: respect Cache-Control HTTP header no-transform

* encode: switch to strings.Contains
2022-12-20 13:26:53 -07:00
dependabot[bot] af93517c2d build(deps): bump goreleaser/goreleaser-action from 2 to 4 (#5264)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-19 19:47:33 -05:00
dependabot[bot] 3b724a2082 build(deps): bump actions/upload-artifact from 1 to 3 (#5262)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-19 19:14:43 -05:00
dependabot[bot] 329af5ced9 build(deps): bump actions/cache from 2 to 3 (#5263)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-19 18:56:52 -05:00
dependabot[bot] cd49847edb build(deps): bump peter-evans/repository-dispatch from 1 to 2 (#5261)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-19 18:44:54 -05:00
John Losito d3d76d6ac2 ci: Check for github action updates monthly (#5258) 2022-12-19 14:57:56 -07:00
Lukas Vogel c3b5b1811c cmd: Avoid panic when printing version without build info (#5210)
* version: don't panic if read build info doesn't work

If `debug.ReadBuildInfo()` doesn't return the build information we
should not try to access it. Especially if users only want to build with
the `CustomVersion` we should not assume access to
`debug.ReadBuildInfo()`.

The build environment where this isn't available for me is when building
with bazel.

* exit early
2022-12-19 14:23:45 -07:00
Emily Lange 4fe5e64e46 readme: white ZeroSSL text color in dark mode (#5259)
* readme: white ZeroSSL text color in dark mode

* fix: keep `valign` for GitHub mobile app
2022-12-19 13:01:30 -07:00
IndeedNotJames e10ed7b00d readme: darker variants of logos in dark mode (#5248) 2022-12-12 10:18:30 -07:00
Matthew Holt fac35db9dc go.mod: Update quic-go to v0.31.0
And fix a comment typo
2022-12-08 08:55:04 -07:00
Kyle McCullough bfaf2a8201 acme_server: Configurable default lifetime for issued certificates (#5232)
* acme_server: add certificate lifetime configuration option

Signed-off-by: Kyle McCullough <kylemcc@gmail.com>

* pki: allow intermediate cert lifetime to be configured

Signed-off-by: Kyle McCullough <kylemcc@gmail.com>

Signed-off-by: Kyle McCullough <kylemcc@gmail.com>
2022-12-06 00:12:26 -07:00
Mohammed Al Sahaf fef9cb3e05 caddytest: internalize init config into '.go' file (#5230) 2022-12-05 18:49:41 +00:00
Alban Lecocq d4a7d89f56 reverseproxy: Improve hostByHashing distribution (#5229)
* If upstreams are all using same host but with different ports
ie:
foobar:4001
foobar:4002
foobar:4003
...
Because fnv-1a has not a good enough avalanche effect
Then the hostByHashing result is not well balanced over
all upstreams

As last byte FNV input tend to affect few bits, the idea is to change
the concatenation order between the key and the upstream strings
So the upstream last byte have more impact on hash diffusion
2022-12-05 11:28:12 -07:00
Matthew Holt ae77a56ac8 Clarify some docs 2022-11-30 16:03:31 -07:00
bit 762b02789a admin: set certmagic cache logger (#5173)
same way it is set in modules/caddytls/tls.go
2022-11-23 20:49:22 -07:00
Mariano Cano 6f8fe01da1 caddypki: Use go.step.sm/crypto to generate the PKI (#5217)
This commit replaces the use of github.com/smallstep/cli to generate the
root and intermediate certificates and uses go.step.sm/crypto instead.

It also upgrades the version of github.com/smallstep/certificates to the
latest version.
2022-11-23 20:47:42 -07:00
bit ac96455a9a admin: fix certificate renewal for admin (#5169)
certmagic.New takes a template and returns pointer to the new config.
GetConfigForCert later must return a pointer to the new config not the
template.

fixes #5162
2022-11-23 11:48:37 -07:00
Francis Lavoie ee7c92ec9b reverseproxy: Mask the WS close message when we're the client (#5199)
* reverseproxy: Mask the WS close message when we're the client

* weakrand

* Bump golangci-lint version so path ignores work on Windows

* gofmt

* ugh, gofmt everything, I guess
2022-11-14 09:38:02 -07:00
Jonathan Garcia 33fdea8f26 caddypki: Prefer user-configured root instead of generating new one (#5189)
instead of generating a new root certificate at the default location
load the certificate from the configuration.
fixes: #5181
2022-11-08 12:13:46 -07:00
Ashish Kurmi 6efd1b3bb1 ci: set least privilged token for github actions for lint workflow (#5179)
* ci: set least privilged token for github actions

Signed-off-by: Ashish Kurmi <akurmi@stepsecurity.io>

* ci:reverting github actions permissions for all but lint workflow

Signed-off-by: Ashish Kurmi <akurmi@stepsecurity.io>
2022-11-06 08:01:36 +00:00
Alexander Graf 087f126cf4 caddyhttp: Canonicalize header field names (#5176) 2022-10-29 16:35:44 -04:00
Benjamin Chalmers 1fa4cb7ba1 caddytest: Increased sleep between retries to reduce flakey tests in CI (#5160)
* Incresed sleep between retries to reduce flakey tests in CI

* Also changed wait time for admin

* Modified time to make it more reliable

Co-authored-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2022-10-27 22:12:30 +00:00
Mohammed Al Sahaf f20a8e7aa0 cmd: replace deprecate func use (#5170) 2022-10-25 17:56:38 +03:00
Matthew Holt 798c4a3ba4 go.mod: Upgrade some dependencies
Quic-go 0.30 should be faster
2022-10-24 12:20:39 -06:00
Matthew Holt 817470dd66 httploader: Close resp body on bad status code
Related to #5158
2022-10-24 12:18:32 -06:00
Chris Lahaye bbe3663167 caddyconfig: Fix httploader leak from unused responses (#5159)
fixes #5158

Signed-off-by: Chris Lahaye <mail@chrislahaye.com>

Signed-off-by: Chris Lahaye <mail@chrislahaye.com>
2022-10-24 11:58:30 -06:00
XYenon ed503118dd caddyhttp: add placeholder {http.request.orig_uri.path.*} (#5161) 2022-10-24 11:57:50 -06:00
Matt Holt a3ae146cbd fileserver: Reject non-GET/HEAD requests (close #5166) (#5167)
* fileserver: Reject non-GET/HEAD requests (close #5166)

* Set Allow header according to RFC 9110 10.2.1
2022-10-24 10:23:57 -06:00
Matt Holt 4bf6cb4199 fileserver: Reject ADS and short name paths; trim trailing dots and spaces on Windows (#5148)
* fileserver: Reject ADS and short name paths

* caddyhttp: Trim trailing space and dot on Windows

Windows ignores trailing dots and spaces in filenames.

* Fix test

* Adjust path filters

* Revert Windows test

* Actually revert the test

* Just check for colons
2022-10-18 21:55:25 -06:00
Scott Mebberson 72e7edda1f map: Clarified how destination values should be formatted (#5156) 2022-10-18 18:14:53 -06:00
BakaFT a999b70727 cmd: Add missing \n to HelpTemplate (#5151) 2022-10-17 11:51:41 +03:00
Francis Lavoie 1cd594963e docs: Fix templates documentation, stray newline breaks godoc (#5149) 2022-10-16 12:25:44 -04:00
146 changed files with 6473 additions and 2075 deletions
+16 -6
View File
@@ -1,7 +1,7 @@
Contributing to Caddy
=====================
Welcome! Thank you for choosing to be a part of our community. Caddy wouldn't be great without your involvement!
Welcome! Thank you for choosing to be a part of our community. Caddy wouldn't be nearly as excellent without your involvement!
For starters, we invite you to join [the Caddy forum](https://caddy.community) where you can hang out with other Caddy users and developers.
@@ -35,19 +35,29 @@ Here are some of the expectations we have of contributors:
- **Keep related commits together in a PR.** We do want pull requests to be small, but you should also keep multiple related commits in the same PR if they rely on each other.
- **Write tests.** Tests are essential! Written properly, they ensure your change works, and that other changes in the future won't break your change. CI checks should pass.
- **Write tests.** Good, automated tests are very valuable! Written properly, they ensure your change works, and that other changes in the future won't break your change. CI checks should pass.
- **Benchmarks should be included for optimizations.** Optimizations sometimes make code harder to read or have changes that are less than obvious. They should be proven with benchmarks or profiling.
- **Benchmarks should be included for optimizations.** Optimizations sometimes make code harder to read or have changes that are less than obvious. They should be proven with benchmarks and profiling.
- **[Squash](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) insignificant commits.** Every commit should be significant. Commits which merely rewrite a comment or fix a typo can be combined into another commit that has more substance. Interactive rebase can do this, or a simpler way is `git reset --soft <diverging-commit>` then `git commit -s`.
- **Own your contributions.** Caddy is a growing project, and it's much better when individual contributors help maintain their change after it is merged.
- **Be responsible for and maintain your contributions.** Caddy is a growing project, and it's much better when individual contributors help maintain their change after it is merged.
- **Use comments properly.** We expect good godoc comments for package-level functions, types, and values. Comments are also useful whenever the purpose for a line of code is not obvious.
- **Pull requests may still get closed.** The longer a PR stays open and idle, the more likely it is to be closed. If we haven't reviewed it in a while, it probably means the change is not a priority. Please don't take this personally, we're trying to balance a lot of tasks! If nobody else has commented or reacted to the PR, it likely means your change is useful only to you. The reality is this happens quite a bit. We don't tend to accept PRs that aren't generally helpful. For these reasons or others, the PR may get closed even after a review. We are not obligated to accept all proposed changes, even if the best justification we can give is something vague like, "It doesn't sit right." Sometimes PRs are just the wrong thing or the wrong time. Because it is open source, you can always build your own modified version of Caddy with a change you need, even if we reject it in the official repo.
- **Pull requests may still get closed.** The longer a PR stays open and idle, the more likely it is to be closed. If we haven't reviewed it in a while, it probably means the change is not a priority. Please don't take this personally, we're trying to balance a lot of tasks! If nobody else has commented or reacted to the PR, it likely means your change is useful only to you. The reality is this happens quite a lot. We don't tend to accept PRs that aren't generally helpful. For these reasons or others, the PR may get closed even after a review. We are not obligated to accept all proposed changes, even if the best justification we can give is something vague like, "It doesn't sit right." Sometimes PRs are just the wrong thing or the wrong time. Because it is open source, you can always build your own modified version of Caddy with a change you need, even if we reject it in the official repo. Plus, because Caddy is extensible, it's possible your feature could make a great plugin instead!
We often grant [collaborator status](#collaborator-instructions) to contributors who author one or more significant, high-quality PRs that are merged into the code base!
- **You certify that you wrote and comprehend the code you submit.** The Caddy project welcomes original contributions that comply with [our CLA](https://cla-assistant.io/caddyserver/caddy), meaning that authors must be able to certify that they created or have rights to the code they are contributing. In addition, we require that code is not simply copy-pasted from Q/A sites or AI language models without full comprehension and rigorous testing. In other words: contributors are allowed to refer to communities for assistance and use AI tools such as language models for inspiration, but code which originates from or is assisted by these resources MUST be:
- Licensed for you to freely share
- Fully comprehended by you (be able to explain every line of code)
- Verified by automated tests when feasible, or thorough manual tests otherwise
We have found that current language models (LLMs, like ChatGPT) may understand code syntax and even problem spaces to an extent, but often fail in subtle ways to convey true knowledge and produce correct algorithms. Integrated tools such as GitHub Copilot and Sourcegraph Cody may be used for inspiration, but code generated by these tools still needs to meet our criteria for licensing, human comprehension, and testing. These tools may be used to help write code comments and tests as long as you can certify they are accurate and correct. Note that it is often more trouble than it's worth to certify that Copilot (for example) is not giving you code that is possibly plagiarised, unlicensed, or licensed with incompatible terms -- as the Caddy project cannot accept such contributions. If that's too difficult for you (or impossible), then we recommend using these resources only for inspiration and write your own code. Ultimately, you (the contributor) are responsible for the code you're submitting.
As a courtesy to reviewers, we kindly ask that you disclose when contributing code that was generated by an AI tool or copied from another website so we can be aware of what to look for in code review.
We often grant [collaborator status](#collaborator-instructions) to contributors who author one or more significant, high-quality PRs that are merged into the code base.
#### HOW TO MAKE A PULL REQUEST TO CADDY
+7
View File
@@ -0,0 +1,7 @@
---
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
+17 -34
View File
@@ -19,16 +19,16 @@ jobs:
fail-fast: false
matrix:
os: [ ubuntu-latest, macos-latest, windows-latest ]
go: [ '1.18', '1.19' ]
go: [ '1.19', '1.20' ]
include:
# Set the minimum Go patch version for the given Go minor
# Usable via ${{ matrix.GO_SEMVER }}
- go: '1.18'
GO_SEMVER: '~1.18.4'
- go: '1.19'
GO_SEMVER: '~1.19.0'
GO_SEMVER: '~1.19.6'
- go: '1.20'
GO_SEMVER: '~1.20.1'
# Set some variables per OS, usable via ${{ matrix.VAR }}
# CADDY_BIN_PATH: the path to the compiled Caddy binary, for artifact publishing
@@ -48,15 +48,15 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.GO_SEMVER }}
check-latest: true
- name: Checkout code
uses: actions/checkout@v3
# These tools would be useful if we later decide to reinvestigate
# publishing test/coverage reports to some tool for easier consumption
# - name: Install test and coverage analysis tools
@@ -64,7 +64,7 @@ jobs:
# go get github.com/axw/gocov/gocov
# go get github.com/AlekSi/gocov-xml
# go get -u github.com/jstemmer/go-junit-report
# echo "::add-path::$(go env GOPATH)/bin"
# echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
- name: Print Go version and environment
id: vars
@@ -77,24 +77,7 @@ jobs:
env
printf "Git version: $(git version)\n\n"
# Calculate the short SHA1 hash of the git commit
echo "::set-output name=short_sha::$(git rev-parse --short HEAD)"
- name: Cache the build cache
uses: actions/cache@v2
with:
# In order:
# * Module download cache
# * Build cache (Linux)
# * Build cache (Mac)
# * Build cache (Windows)
path: |
~/go/pkg/mod
~/.cache/go-build
~/Library/Caches/go-build
~\AppData\Local\go-build
key: ${{ runner.os }}-${{ matrix.go }}-go-ci-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-${{ matrix.go }}-go-ci
echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Get dependencies
run: |
@@ -109,7 +92,7 @@ jobs:
go build -trimpath -ldflags="-w -s" -v
- name: Publish Build Artifact
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v3
with:
name: caddy_${{ runner.os }}_go${{ matrix.go }}_${{ steps.vars.outputs.short_sha }}
path: ${{ matrix.CADDY_BIN_PATH }}
@@ -123,7 +106,7 @@ jobs:
run: |
# (go test -v -coverprofile=cover-profile.out -race ./... 2>&1) > test-results/test-result.out
go test -v -coverprofile="cover-profile.out" -short -race ./...
# echo "::set-output name=status::$?"
# echo "status=$?" >> $GITHUB_OUTPUT
# Relevant step if we reinvestigate publishing test/coverage reports
# - name: Prepare coverage reports
@@ -143,10 +126,10 @@ jobs:
s390x-test:
name: test (s390x on IBM Z)
runs-on: ubuntu-latest
if: github.event.pull_request.head.repo.full_name == github.repository
if: github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]'
continue-on-error: true # August 2020: s390x VM is down due to weather and power issues
steps:
- name: Checkout code into the Go module directory
- name: Checkout code
uses: actions/checkout@v3
- name: Run Tests
run: |
@@ -172,10 +155,10 @@ jobs:
goreleaser-check:
runs-on: ubuntu-latest
steps:
- name: checkout
- name: Checkout code
uses: actions/checkout@v3
- uses: goreleaser/goreleaser-action@v2
- uses: goreleaser/goreleaser-action@v4
with:
version: latest
args: check
+7 -20
View File
@@ -16,19 +16,22 @@ jobs:
fail-fast: false
matrix:
goos: ['android', 'linux', 'solaris', 'illumos', 'dragonfly', 'freebsd', 'openbsd', 'plan9', 'windows', 'darwin', 'netbsd']
go: [ '1.19' ]
go: [ '1.20' ]
include:
# Set the minimum Go patch version for the given Go minor
# Usable via ${{ matrix.GO_SEMVER }}
- go: '1.19'
GO_SEMVER: '~1.19.0'
- go: '1.20'
GO_SEMVER: '~1.20.1'
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.GO_SEMVER }}
check-latest: true
@@ -43,22 +46,6 @@ jobs:
printf "\n\nSystem environment:\n\n"
env
- name: Cache the build cache
uses: actions/cache@v2
with:
# In order:
# * Module download cache
# * Build cache (Linux)
path: |
~/go/pkg/mod
~/.cache/go-build
key: cross-build-go${{ matrix.go }}-${{ matrix.goos }}-${{ hashFiles('**/go.sum') }}
restore-keys: |
cross-build-go${{ matrix.go }}-${{ matrix.goos }}
- name: Checkout code into the Go module directory
uses: actions/checkout@v3
- name: Run Build
env:
CGO_ENABLED: 0
+9 -3
View File
@@ -10,9 +10,15 @@ on:
- master
- 2.*
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:
@@ -20,15 +26,15 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
- uses: actions/setup-go@v4
with:
go-version: '~1.18.4'
go-version: '~1.19.6'
check-latest: true
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.47
version: v1.50
# Windows times out frequently after about 5m50s if we don't set a longer timeout.
args: --timeout 10m
# Optional: show only new issues if it's a pull request. The default value is `false`.
+16 -28
View File
@@ -11,13 +11,13 @@ jobs:
strategy:
matrix:
os: [ ubuntu-latest ]
go: [ '1.19' ]
go: [ '1.20' ]
include:
# Set the minimum Go patch version for the given Go minor
# Usable via ${{ matrix.GO_SEMVER }}
- go: '1.19'
GO_SEMVER: '~1.19.0'
- go: '1.20'
GO_SEMVER: '~1.20.1'
runs-on: ${{ matrix.os }}
# https://github.com/sigstore/cosign/issues/1258#issuecomment-1002251233
@@ -29,17 +29,17 @@ jobs:
contents: write
steps:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.GO_SEMVER }}
check-latest: true
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.GO_SEMVER }}
check-latest: true
# Force fetch upstream tags -- because 65 minutes
# tl;dr: actions/checkout@v3 runs this line:
# git -c protocol.version=2 fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 origin +ebc278ec98bb24f2852b61fde2a9bf2e3d83818b:refs/tags/
@@ -61,8 +61,8 @@ jobs:
go env
printf "\n\nSystem environment:\n\n"
env
echo "::set-output name=version_tag::${GITHUB_REF/refs\/tags\//}"
echo "::set-output name=short_sha::$(git rev-parse --short HEAD)"
echo "version_tag=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
# Add "pip install" CLI tools to PATH
echo ~/.local/bin >> $GITHUB_PATH
@@ -74,10 +74,10 @@ jobs:
TAG_MINOR=`echo ${TAG#v} | sed -e "s#$SEMVER_RE#\2#"`
TAG_PATCH=`echo ${TAG#v} | sed -e "s#$SEMVER_RE#\3#"`
TAG_SPECIAL=`echo ${TAG#v} | sed -e "s#$SEMVER_RE#\4#"`
echo "::set-output name=tag_major::${TAG_MAJOR}"
echo "::set-output name=tag_minor::${TAG_MINOR}"
echo "::set-output name=tag_patch::${TAG_PATCH}"
echo "::set-output name=tag_special::${TAG_SPECIAL}"
echo "tag_major=${TAG_MAJOR}" >> $GITHUB_OUTPUT
echo "tag_minor=${TAG_MINOR}" >> $GITHUB_OUTPUT
echo "tag_patch=${TAG_PATCH}" >> $GITHUB_OUTPUT
echo "tag_special=${TAG_SPECIAL}" >> $GITHUB_OUTPUT
# Cloudsmith CLI tooling for pushing releases
# See https://help.cloudsmith.io/docs/cli
@@ -94,18 +94,6 @@ jobs:
# tags are only accepted if signed by Matt's key
git verify-tag "${{ steps.vars.outputs.version_tag }}" || exit 1
- name: Cache the build cache
uses: actions/cache@v2
with:
# In order:
# * Module download cache
# * Build cache (Linux)
path: |
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-go${{ matrix.go }}-release-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go${{ matrix.go }}-release
- name: Install Cosign
uses: sigstore/cosign-installer@main
- name: Cosign version
@@ -116,7 +104,7 @@ jobs:
run: syft version
# GoReleaser will take care of publishing those artifacts into the release
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
uses: goreleaser/goreleaser-action@v4
with:
version: latest
args: release --rm-dist --timeout 60m
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
# See https://github.com/peter-evans/repository-dispatch
- name: Trigger event on caddyserver/dist
uses: peter-evans/repository-dispatch@v1
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.REPO_DISPATCH_TOKEN }}
repository: caddyserver/dist
@@ -25,7 +25,7 @@ jobs:
client-payload: '{"tag": "${{ github.event.release.tag_name }}"}'
- name: Trigger event on caddyserver/caddy-docker
uses: peter-evans/repository-dispatch@v1
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.REPO_DISPATCH_TOKEN }}
repository: caddyserver/caddy-docker
+4
View File
@@ -96,3 +96,7 @@ issues:
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
+64 -7
View File
@@ -4,7 +4,9 @@ before:
# This is so we can run goreleaser on tag without Git complaining of being dirty. The main.go in cmd/caddy directory
# cannot be built within that directory due to changes necessary for the build causing Git to be dirty, which
# subsequently causes gorleaser to refuse running.
- rm -rf caddy-build caddy-dist
- rm -rf caddy-build caddy-dist vendor
# vendor Caddy deps
- go mod vendor
- mkdir -p caddy-build
- cp cmd/caddy/main.go caddy-build/main.go
- /bin/sh -c 'cd ./caddy-build && go mod init caddy'
@@ -14,6 +16,8 @@ before:
# as of Go 1.16, `go` commands no longer automatically change go.{mod,sum}. We now have to explicitly
# run `go mod tidy`. The `/bin/sh -c '...'` is because goreleaser can't find cd in PATH without shell invocation.
- /bin/sh -c 'cd ./caddy-build && go mod tidy'
# vendor the deps of the prepared to-build module
- /bin/sh -c 'cd ./caddy-build && go mod vendor'
- git clone --depth 1 https://github.com/caddyserver/dist caddy-dist
- mkdir -p caddy-dist/man
- go mod download
@@ -66,24 +70,78 @@ builds:
- -mod=readonly
ldflags:
- -s -w
signs:
- cmd: cosign
signature: "${artifact}.sig"
certificate: '{{ trimsuffix (trimsuffix .Env.artifact ".zip") ".tar.gz" }}.pem'
args: ["sign-blob", "--output-signature=${signature}", "--output-certificate", "${certificate}", "${artifact}"]
args: ["sign-blob", "--yes", "--output-signature=${signature}", "--output-certificate", "${certificate}", "${artifact}"]
artifacts: all
sboms:
- artifacts: binary
documents:
- '{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{if .Arm}}v{{ .Arm }}{{end}}.sbom'
- >-
{{ .ProjectName }}_
{{- .Version }}_
{{- if eq .Os "darwin" }}mac{{ else }}{{ .Os }}{{ end }}_
{{- .Arch }}
{{- with .Arm }}v{{ . }}{{ end }}
{{- with .Mips }}_{{ . }}{{ end }}
{{- if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}.sbom
cmd: syft
args: ["$artifact", "--file", "${document}", "--output", "cyclonedx-json"]
archives:
- format_overrides:
- id: default
format_overrides:
- goos: windows
format: zip
replacements:
darwin: mac
name_template: >-
{{ .ProjectName }}_
{{- .Version }}_
{{- if eq .Os "darwin" }}mac{{ else }}{{ .Os }}{{ end }}_
{{- .Arch }}
{{- with .Arm }}v{{ . }}{{ end }}
{{- with .Mips }}_{{ . }}{{ end }}
{{- if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}
# packge the 'caddy-build' directory into a tarball,
# allowing users to build the exact same set of files as ours.
- id: source
meta: true
rlcp: true
name_template: "{{ .ProjectName }}_{{ .Version }}_buildable-artifact"
files:
- src: LICENSE
dst: ./LICENSE
- src: README.md
dst: ./README.md
- src: AUTHORS
dst: ./AUTHORS
- src: ./caddy-build
dst: ./
source:
enabled: true
name_template: '{{ .ProjectName }}_{{ .Version }}_src'
format: 'tar.gz'
# This will make the destination paths be relative to the longest common
# path prefix between all the files matched and the source glob.
# Enabling this essentially mimic the behavior of nfpm's contents section.
# It will be the default by June 2023.
#
# Default: false
rlcp: true
# Additional files/template/globs you want to add to the source archive.
#
# Default: empty.
files:
- vendor
checksum:
algorithm: sha512
@@ -128,7 +186,6 @@ nfpms:
preremove: ./caddy-dist/scripts/preremove.sh
postremove: ./caddy-dist/scripts/postremove.sh
release:
github:
owner: caddyserver
+20 -8
View File
@@ -1,13 +1,19 @@
<p align="center">
<a href="https://caddyserver.com"><img src="https://user-images.githubusercontent.com/1128849/36338535-05fb646a-136f-11e8-987b-e6901e717d5a.png" alt="Caddy" width="450"></a>
<a href="https://caddyserver.com">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/1128849/210187358-e2c39003-9a5e-4dd5-a783-6deb6483ee72.svg">
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/1128849/210187356-dfb7f1c5-ac2e-43aa-bb23-fc014280ae1f.svg">
<img src="https://user-images.githubusercontent.com/1128849/210187356-dfb7f1c5-ac2e-43aa-bb23-fc014280ae1f.svg" alt="Caddy" width="550">
</picture>
</a>
<br>
<h3 align="center">a <a href="https://zerossl.com"><img src="https://caddyserver.com/resources/images/zerossl-logo.svg" height="28" valign="middle"></a> project</h3>
<h3 align="center">a <a href="https://zerossl.com"><img src="https://user-images.githubusercontent.com/55066419/208327323-2770dc16-ec09-43a0-9035-c5b872c2ad7f.svg" height="28" style="vertical-align: -7.7px" valign="middle"></a> project</h3>
</p>
<hr>
<h3 align="center">Every site on HTTPS</h3>
<p align="center">Caddy is an extensible server platform that uses TLS by default.</p>
<p align="center">
<a href="https://github.com/caddyserver/caddy/actions?query=workflow%3ACross-Platform"><img src="https://github.com/caddyserver/caddy/workflows/Cross-Platform/badge.svg"></a>
<a href="https://github.com/caddyserver/caddy/actions/workflows/ci.yml"><img src="https://github.com/caddyserver/caddy/actions/workflows/ci.yml/badge.svg"></a>
<a href="https://pkg.go.dev/github.com/caddyserver/caddy/v2"><img src="https://img.shields.io/badge/godoc-reference-%23007d9c.svg"></a>
<br>
<a href="https://twitter.com/caddyserver" title="@caddyserver on Twitter"><img src="https://img.shields.io/badge/twitter-@caddyserver-55acee.svg" alt="@caddyserver on Twitter"></a>
@@ -40,7 +46,13 @@
<p align="center">
<b>Powered by</b>
<br>
<a href="https://github.com/caddyserver/certmagic"><img src="https://user-images.githubusercontent.com/1128849/49704830-49d37200-fbd5-11e8-8385-767e0cd033c3.png" alt="CertMagic" width="250"></a>
<a href="https://github.com/caddyserver/certmagic">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/55066419/206946718-740b6371-3df3-4d72-a822-47e4c48af999.png">
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/1128849/49704830-49d37200-fbd5-11e8-8385-767e0cd033c3.png">
<img src="https://user-images.githubusercontent.com/1128849/49704830-49d37200-fbd5-11e8-8385-767e0cd033c3.png" alt="CertMagic" width="250">
</picture>
</a>
</p>
@@ -58,7 +70,7 @@
- **Stays up when other servers go down** due to TLS/OCSP/certificate-related issues
- **Production-ready** after serving trillions of requests and managing millions of TLS certificates
- **Scales to hundreds of thousands of sites** as proven in production
- **HTTP/1.1, HTTP/2, and HTTP/3** supported all by default
- **HTTP/1.1, HTTP/2, and HTTP/3** all supported by default
- **Highly extensible** [modular architecture](https://caddyserver.com/docs/architecture) lets Caddy do anything without bloat
- **Runs anywhere** with **no external dependencies** (not even libc)
- Written in Go, a language with higher **memory safety guarantees** than other servers
@@ -75,10 +87,10 @@ See [our online documentation](https://caddyserver.com/docs/install) for other i
Requirements:
- [Go 1.18 or newer](https://golang.org/dl/)
- [Go 1.19 or newer](https://golang.org/dl/)
### For development
_**Note:** These steps [will not embed proper version information](https://github.com/golang/go/issues/29228). For that, please follow the instructions in the next section._
```bash
@@ -185,4 +197,4 @@ Matthew Holt began developing Caddy in 2014 while studying computer science at B
Caddy is a project of [ZeroSSL](https://zerossl.com), a Stack Holdings company.
Debian package repository hosting is graciously provided by [Cloudsmith](https://cloudsmith.com). Cloudsmith is the only fully hosted, cloud-native, universal package management solution, that enables your organization to create, store and share packages in any format, to any place, with total confidence.
Debian package repository hosting is graciously provided by [Cloudsmith](https://cloudsmith.com). Cloudsmith is the only fully hosted, cloud-native, universal package management solution, that enables your organization to create, store and share packages in any format, to any place, with total confidence.
+19 -3
View File
@@ -46,6 +46,17 @@ import (
"go.uber.org/zap/zapcore"
)
func init() {
// The hard-coded default `DefaultAdminListen` can be overidden
// by setting the `CADDY_ADMIN` environment variable.
// The environment variable may be used by packagers to change
// the default admin address to something more appropriate for
// that platform. See #5317 for discussion.
if env, exists := os.LookupEnv("CADDY_ADMIN"); exists {
DefaultAdminListen = env
}
}
// AdminConfig configures Caddy's API endpoint, which is used
// to manage Caddy while it is running.
type AdminConfig struct {
@@ -57,7 +68,9 @@ type AdminConfig struct {
// The address to which the admin endpoint's listener should
// bind itself. Can be any single network address that can be
// parsed by Caddy. Accepts placeholders. Default: localhost:2019
// parsed by Caddy. Accepts placeholders.
// Default: the value of the `CADDY_ADMIN` environment variable,
// or `localhost:2019` otherwise.
Listen string `json:"listen,omitempty"`
// If true, CORS headers will be emitted, and requests to the
@@ -572,12 +585,13 @@ func replaceRemoteAdminServer(ctx Context, cfg *Config) error {
}
func (ident *IdentityConfig) certmagicConfig(logger *zap.Logger, makeCache bool) *certmagic.Config {
var cmCfg *certmagic.Config
if ident == nil {
// user might not have configured identity; that's OK, we can still make a
// certmagic config, although it'll be mostly useless for remote management
ident = new(IdentityConfig)
}
cmCfg := &certmagic.Config{
template := certmagic.Config{
Storage: DefaultStorage, // do not act as part of a cluster (this is for the server's local identity)
Logger: logger,
Issuers: ident.issuers,
@@ -587,9 +601,11 @@ func (ident *IdentityConfig) certmagicConfig(logger *zap.Logger, makeCache bool)
GetConfigForCert: func(certmagic.Certificate) (*certmagic.Config, error) {
return cmCfg, nil
},
Logger: logger.Named("cache"),
})
}
return certmagic.New(identityCertCache, *cmCfg)
cmCfg = certmagic.New(identityCertCache, template)
return cmCfg
}
// IdentityCredentials returns this instance's configured, managed identity credentials
+16 -8
View File
@@ -314,7 +314,7 @@ func unsyncedDecodeAndRun(cfgJSON []byte, allowPersist bool) error {
strippedCfgJSON := RemoveMetaFields(cfgJSON)
var newCfg *Config
err := strictUnmarshalJSON(strippedCfgJSON, &newCfg)
err := StrictUnmarshalJSON(strippedCfgJSON, &newCfg)
if err != nil {
return err
}
@@ -864,13 +864,21 @@ func Version() (simple, full string) {
// bi.Main... hopefully.
var module *debug.Module
bi, ok := debug.ReadBuildInfo()
if ok {
// find the Caddy module in the dependency list
for _, dep := range bi.Deps {
if dep.Path == ImportPath {
module = dep
break
}
if !ok {
if CustomVersion != "" {
full = CustomVersion
simple = CustomVersion
return
}
full = "unknown"
simple = "unknown"
return
}
// find the Caddy module in the dependency list
for _, dep := range bi.Deps {
if dep.Path == ImportPath {
module = dep
break
}
}
if module != nil {
+4 -4
View File
@@ -54,7 +54,7 @@ func (a Adapter) Adapt(body []byte, options map[string]any) ([]byte, []caddyconf
// lint check: see if input was properly formatted; sometimes messy files files parse
// successfully but result in logical errors (the Caddyfile is a bad format, I'm sorry)
if warning, different := formattingDifference(filename, body); different {
if warning, different := FormattingDifference(filename, body); different {
warnings = append(warnings, warning)
}
@@ -63,10 +63,10 @@ func (a Adapter) Adapt(body []byte, options map[string]any) ([]byte, []caddyconf
return result, warnings, err
}
// formattingDifference returns a warning and true if the formatted version
// FormattingDifference returns a warning and true if the formatted version
// is any different from the input; empty warning and false otherwise.
// TODO: also perform this check on imported files
func formattingDifference(filename string, body []byte) (caddyconfig.Warning, bool) {
func FormattingDifference(filename string, body []byte) (caddyconfig.Warning, bool) {
// replace windows-style newlines to normalize comparison
normalizedBody := bytes.Replace(body, []byte("\r\n"), []byte("\n"), -1)
@@ -88,7 +88,7 @@ func formattingDifference(filename string, body []byte) (caddyconfig.Warning, bo
return caddyconfig.Warning{
File: filename,
Line: line,
Message: "Caddyfile input is not formatted; run the 'caddy fmt' command to fix inconsistencies",
Message: "Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies",
}, true
}
+22 -36
View File
@@ -20,7 +20,6 @@ import (
"io"
"log"
"strconv"
"strings"
)
// Dispenser is a type that dispenses tokens, similarly to a lexer,
@@ -101,12 +100,12 @@ func (d *Dispenser) nextOnSameLine() bool {
d.cursor++
return true
}
if d.cursor >= len(d.tokens) {
if d.cursor >= len(d.tokens)-1 {
return false
}
if d.cursor < len(d.tokens)-1 &&
d.tokens[d.cursor].File == d.tokens[d.cursor+1].File &&
d.tokens[d.cursor].Line+d.numLineBreaks(d.cursor) == d.tokens[d.cursor+1].Line {
curr := d.tokens[d.cursor]
next := d.tokens[d.cursor+1]
if curr.File == next.File && curr.Line+curr.NumLineBreaks() == next.Line {
d.cursor++
return true
}
@@ -122,12 +121,12 @@ func (d *Dispenser) NextLine() bool {
d.cursor++
return true
}
if d.cursor >= len(d.tokens) {
if d.cursor >= len(d.tokens)-1 {
return false
}
if d.cursor < len(d.tokens)-1 &&
(d.tokens[d.cursor].File != d.tokens[d.cursor+1].File ||
d.tokens[d.cursor].Line+d.numLineBreaks(d.cursor) < d.tokens[d.cursor+1].Line) {
curr := d.tokens[d.cursor]
next := d.tokens[d.cursor+1]
if curr.File != next.File || curr.Line+curr.NumLineBreaks() < next.Line {
d.cursor++
return true
}
@@ -203,14 +202,17 @@ func (d *Dispenser) Val() string {
}
// ValRaw gets the raw text of the current token (including quotes).
// If the token was a heredoc, then the delimiter is not included,
// because that is not relevant to any unmarshaling logic at this time.
// If there is no token loaded, it returns empty string.
func (d *Dispenser) ValRaw() string {
if d.cursor < 0 || d.cursor >= len(d.tokens) {
return ""
}
quote := d.tokens[d.cursor].wasQuoted
if quote > 0 {
return string(quote) + d.tokens[d.cursor].Text + string(quote) // string literal
if quote > 0 && quote != '<' {
// string literal
return string(quote) + d.tokens[d.cursor].Text + string(quote)
}
return d.tokens[d.cursor].Text
}
@@ -438,14 +440,14 @@ func (d *Dispenser) Delete() []Token {
return d.tokens
}
// numLineBreaks counts how many line breaks are in the token
// value given by the token index tknIdx. It returns 0 if the
// token does not exist or there are no line breaks.
func (d *Dispenser) numLineBreaks(tknIdx int) int {
if tknIdx < 0 || tknIdx >= len(d.tokens) {
return 0
// DeleteN is the same as Delete, but can delete many tokens at once.
// If there aren't N tokens available to delete, none are deleted.
func (d *Dispenser) DeleteN(amount int) []Token {
if amount > 0 && d.cursor >= (amount-1) && d.cursor <= len(d.tokens)-1 {
d.tokens = append(d.tokens[:d.cursor-(amount-1)], d.tokens[d.cursor+1:]...)
d.cursor -= amount
}
return strings.Count(d.tokens[tknIdx].Text, "\n")
return d.tokens
}
// isNewLine determines whether the current token is on a different
@@ -468,18 +470,10 @@ func (d *Dispenser) isNewLine() bool {
return true
}
// The previous token may contain line breaks if
// it was quoted and spanned multiple lines. e.g:
//
// dir "foo
// bar
// baz"
prevLineBreaks := d.numLineBreaks(d.cursor - 1)
// If the previous token (incl line breaks) ends
// on a line earlier than the current token,
// then the current token is on a new line
return prev.Line+prevLineBreaks < curr.Line
return prev.Line+prev.NumLineBreaks() < curr.Line
}
// isNextOnNewLine determines whether the current token is on a different
@@ -502,16 +496,8 @@ func (d *Dispenser) isNextOnNewLine() bool {
return true
}
// The current token may contain line breaks if
// it was quoted and spanned multiple lines. e.g:
//
// dir "foo
// bar
// baz"
currLineBreaks := d.numLineBreaks(d.cursor)
// If the current token (incl line breaks) ends
// on a line earlier than the next token,
// then the next token is on a new line
return curr.Line+currLineBreaks < next.Line
return curr.Line+curr.NumLineBreaks() < next.Line
}
+142
View File
@@ -0,0 +1,142 @@
// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package caddyfile
import (
"regexp"
"strconv"
"strings"
"github.com/caddyserver/caddy/v2"
"go.uber.org/zap"
)
// parseVariadic determines if the token is a variadic placeholder,
// and if so, determines the index range (start/end) of args to use.
// Returns a boolean signaling whether a variadic placeholder was found,
// and the start and end indices.
func parseVariadic(token Token, argCount int) (bool, int, int) {
if !strings.HasPrefix(token.Text, "{args[") {
return false, 0, 0
}
if !strings.HasSuffix(token.Text, "]}") {
return false, 0, 0
}
argRange := strings.TrimSuffix(strings.TrimPrefix(token.Text, "{args["), "]}")
if argRange == "" {
caddy.Log().Named("caddyfile").Warn(
"Placeholder "+token.Text+" cannot have an empty index",
zap.String("file", token.File+":"+strconv.Itoa(token.Line)))
return false, 0, 0
}
start, end, found := strings.Cut(argRange, ":")
// If no ":" delimiter is found, this is not a variadic.
// The replacer will pick this up.
if !found {
return false, 0, 0
}
var (
startIndex = 0
endIndex = argCount
err error
)
if start != "" {
startIndex, err = strconv.Atoi(start)
if err != nil {
caddy.Log().Named("caddyfile").Warn(
"Variadic placeholder "+token.Text+" has an invalid start index",
zap.String("file", token.File+":"+strconv.Itoa(token.Line)))
return false, 0, 0
}
}
if end != "" {
endIndex, err = strconv.Atoi(end)
if err != nil {
caddy.Log().Named("caddyfile").Warn(
"Variadic placeholder "+token.Text+" has an invalid end index",
zap.String("file", token.File+":"+strconv.Itoa(token.Line)))
return false, 0, 0
}
}
// bound check
if startIndex < 0 || startIndex > endIndex || endIndex > argCount {
caddy.Log().Named("caddyfile").Warn(
"Variadic placeholder "+token.Text+" indices are out of bounds, only "+strconv.Itoa(argCount)+" argument(s) exist",
zap.String("file", token.File+":"+strconv.Itoa(token.Line)))
return false, 0, 0
}
return true, startIndex, endIndex
}
// makeArgsReplacer prepares a Replacer which can replace
// non-variadic args placeholders in imported tokens.
func makeArgsReplacer(args []string) *caddy.Replacer {
repl := caddy.NewEmptyReplacer()
repl.Map(func(key string) (any, bool) {
// TODO: Remove the deprecated {args.*} placeholder
// support at some point in the future
if matches := argsRegexpIndexDeprecated.FindStringSubmatch(key); len(matches) > 0 {
value, err := strconv.Atoi(matches[1])
if err != nil {
caddy.Log().Named("caddyfile").Warn(
"Placeholder {args." + matches[1] + "} has an invalid index")
return nil, false
}
if value >= len(args) {
caddy.Log().Named("caddyfile").Warn(
"Placeholder {args." + matches[1] + "} index is out of bounds, only " + strconv.Itoa(len(args)) + " argument(s) exist")
return nil, false
}
caddy.Log().Named("caddyfile").Warn(
"Placeholder {args." + matches[1] + "} deprecated, use {args[" + matches[1] + "]} instead")
return args[value], true
}
// Handle args[*] form
if matches := argsRegexpIndex.FindStringSubmatch(key); len(matches) > 0 {
if strings.Contains(matches[1], ":") {
caddy.Log().Named("caddyfile").Warn(
"Variadic placeholder {args[" + matches[1] + "]} must be a token on its own")
return nil, false
}
value, err := strconv.Atoi(matches[1])
if err != nil {
caddy.Log().Named("caddyfile").Warn(
"Placeholder {args[" + matches[1] + "]} has an invalid index")
return nil, false
}
if value >= len(args) {
caddy.Log().Named("caddyfile").Warn(
"Placeholder {args[" + matches[1] + "]} index is out of bounds, only " + strconv.Itoa(len(args)) + " argument(s) exist")
return nil, false
}
return args[value], true
}
// Not an args placeholder, ignore
return nil, false
})
return repl
}
var (
argsRegexpIndexDeprecated = regexp.MustCompile(`args\.(.+)`)
argsRegexpIndex = regexp.MustCompile(`args\[(.+)]`)
)
+192 -32
View File
@@ -17,7 +17,10 @@ package caddyfile
import (
"bufio"
"bytes"
"fmt"
"io"
"regexp"
"strings"
"unicode"
)
@@ -35,15 +38,41 @@ type (
// Token represents a single parsable unit.
Token struct {
File string
Line int
Text string
wasQuoted rune // enclosing quote character, if any
inSnippet bool
snippetName string
File string
origFile string
Line int
Text string
wasQuoted rune // enclosing quote character, if any
heredocMarker string
snippetName string
}
)
// Tokenize takes bytes as input and lexes it into
// a list of tokens that can be parsed as a Caddyfile.
// Also takes a filename to fill the token's File as
// the source of the tokens, which is important to
// determine relative paths for `import` directives.
func Tokenize(input []byte, filename string) ([]Token, error) {
l := lexer{}
if err := l.load(bytes.NewReader(input)); err != nil {
return nil, err
}
var tokens []Token
for {
found, err := l.next()
if err != nil {
return nil, err
}
if !found {
break
}
l.token.File = filename
tokens = append(tokens, l.token)
}
return tokens, nil
}
// load prepares the lexer to scan an input for tokens.
// It discards any leading byte order mark.
func (l *lexer) load(input io.Reader) error {
@@ -75,28 +104,93 @@ func (l *lexer) load(input io.Reader) error {
// may be escaped. The rest of the line is skipped
// if a "#" character is read in. Returns true if
// a token was loaded; false otherwise.
func (l *lexer) next() bool {
func (l *lexer) next() (bool, error) {
var val []rune
var comment, quoted, btQuoted, escaped bool
var comment, quoted, btQuoted, inHeredoc, heredocEscaped, escaped bool
var heredocMarker string
makeToken := func(quoted rune) bool {
l.token.Text = string(val)
l.token.wasQuoted = quoted
l.token.heredocMarker = heredocMarker
return true
}
for {
// Read a character in; if err then if we had
// read some characters, make a token. If we
// reached EOF, then no more tokens to read.
// If no EOF, then we had a problem.
ch, _, err := l.reader.ReadRune()
if err != nil {
if len(val) > 0 {
return makeToken(0)
if inHeredoc {
return false, fmt.Errorf("incomplete heredoc <<%s on line #%d, expected ending marker %s", heredocMarker, l.line+l.skippedLines, heredocMarker)
}
return makeToken(0), nil
}
if err == io.EOF {
return false
return false, nil
}
panic(err)
return false, err
}
// detect whether we have the start of a heredoc
if !inHeredoc && !heredocEscaped && len(val) > 1 && string(val[:2]) == "<<" {
if ch == '<' {
return false, fmt.Errorf("too many '<' for heredoc on line #%d; only use two, for example <<END", l.line)
}
if ch == '\r' {
continue
}
// after hitting a newline, we know that the heredoc marker
// is the characters after the two << and the newline.
// we reset the val because the heredoc is syntax we don't
// want to keep.
if ch == '\n' {
heredocMarker = string(val[2:])
if !heredocMarkerRegexp.Match([]byte(heredocMarker)) {
return false, fmt.Errorf("heredoc marker on line #%d must contain only alpha-numeric characters, dashes and underscores; got '%s'", l.line, heredocMarker)
}
inHeredoc = true
l.skippedLines++
val = nil
continue
}
val = append(val, ch)
continue
}
// if we're in a heredoc, all characters are read as-is
if inHeredoc {
val = append(val, ch)
if ch == '\n' {
l.skippedLines++
}
// check if we're done, i.e. that the last few characters are the marker
if len(val) > len(heredocMarker) && heredocMarker == string(val[len(val)-len(heredocMarker):]) {
// set the final value
val, err = l.finalizeHeredoc(val, heredocMarker)
if err != nil {
return false, err
}
// set the line counter, and make the token
l.line += l.skippedLines
l.skippedLines = 0
return makeToken('<'), nil
}
// stay in the heredoc until we find the ending marker
continue
}
// track whether we found an escape '\' for the next
// iteration to be contextually aware
if !escaped && !btQuoted && ch == '\\' {
escaped = true
continue
@@ -111,26 +205,29 @@ func (l *lexer) next() bool {
}
escaped = false
} else {
if quoted && ch == '"' {
return makeToken('"')
}
if btQuoted && ch == '`' {
return makeToken('`')
if (quoted && ch == '"') || (btQuoted && ch == '`') {
return makeToken(ch), nil
}
}
// allow quoted text to wrap continue on multiple lines
if ch == '\n' {
l.line += 1 + l.skippedLines
l.skippedLines = 0
}
// collect this character as part of the quoted token
val = append(val, ch)
continue
}
if unicode.IsSpace(ch) {
// ignore CR altogether, we only actually care about LF (\n)
if ch == '\r' {
continue
}
// end of the line
if ch == '\n' {
// newlines can be escaped to chain arguments
// onto multiple lines; else, increment the line count
if escaped {
l.skippedLines++
escaped = false
@@ -138,14 +235,18 @@ func (l *lexer) next() bool {
l.line += 1 + l.skippedLines
l.skippedLines = 0
}
// comments (#) are single-line only
comment = false
}
// any kind of space means we're at the end of this token
if len(val) > 0 {
return makeToken(0)
return makeToken(0), nil
}
continue
}
// comments must be at the start of a token,
// in other words, preceded by space or newline
if ch == '#' && len(val) == 0 {
comment = true
}
@@ -166,7 +267,12 @@ func (l *lexer) next() bool {
}
if escaped {
val = append(val, '\\')
// allow escaping the first < to skip the heredoc syntax
if ch == '<' {
heredocEscaped = true
} else {
val = append(val, '\\')
}
escaped = false
}
@@ -174,24 +280,78 @@ func (l *lexer) next() bool {
}
}
// Tokenize takes bytes as input and lexes it into
// a list of tokens that can be parsed as a Caddyfile.
// Also takes a filename to fill the token's File as
// the source of the tokens, which is important to
// determine relative paths for `import` directives.
func Tokenize(input []byte, filename string) ([]Token, error) {
l := lexer{}
if err := l.load(bytes.NewReader(input)); err != nil {
return nil, err
// finalizeHeredoc takes the runes read as the heredoc text and the marker,
// and processes the text to strip leading whitespace, returning the final
// value without the leading whitespace.
func (l *lexer) finalizeHeredoc(val []rune, marker string) ([]rune, error) {
stringVal := string(val)
// find the last newline of the heredoc, which is where the contents end
lastNewline := strings.LastIndex(stringVal, "\n")
// collapse the content, then split into separate lines
lines := strings.Split(stringVal[:lastNewline+1], "\n")
// figure out how much whitespace we need to strip from the front of every line
// by getting the string that precedes the marker, on the last line
paddingToStrip := stringVal[lastNewline+1 : len(stringVal)-len(marker)]
// iterate over each line and strip the whitespace from the front
var out string
for lineNum, lineText := range lines[:len(lines)-1] {
// find an exact match for the padding
index := strings.Index(lineText, paddingToStrip)
// if the padding doesn't match exactly at the start then we can't safely strip
if index != 0 {
return nil, fmt.Errorf("mismatched leading whitespace in heredoc <<%s on line #%d [%s], expected whitespace [%s] to match the closing marker", marker, l.line+lineNum+1, lineText, paddingToStrip)
}
// strip, then append the line, with the newline, to the output.
// also removes all "\r" because Windows.
out += strings.ReplaceAll(lineText[len(paddingToStrip):]+"\n", "\r", "")
}
var tokens []Token
for l.next() {
l.token.File = filename
tokens = append(tokens, l.token)
// Remove the trailing newline from the loop
if len(out) > 0 && out[len(out)-1] == '\n' {
out = out[:len(out)-1]
}
return tokens, nil
// return the final value
return []rune(out), nil
}
// originalFile gets original filename before import modification.
func (t Token) originalFile() string {
if t.origFile != "" {
return t.origFile
}
return t.File
}
// updateFile updates the token's source filename for error display
// and remembers the original filename. Used during "import" processing.
func (t *Token) updateFile(file string) {
if t.origFile == "" {
t.origFile = t.File
}
t.File = file
}
func (t Token) Quoted() bool {
return t.wasQuoted > 0
}
// NumLineBreaks counts how many line breaks are in the token text.
func (t Token) NumLineBreaks() int {
lineBreaks := strings.Count(t.Text, "\n")
if t.wasQuoted == '<' {
// heredocs have an extra linebreak because the opening
// delimiter is on its own line and is not included in the
// token Text itself, and the trailing newline is removed.
lineBreaks += 2
}
return lineBreaks
}
var heredocMarkerRegexp = regexp.MustCompile("^[A-Za-z0-9_-]+$")
+176 -10
View File
@@ -18,13 +18,13 @@ import (
"testing"
)
type lexerTestCase struct {
input []byte
expected []Token
}
func TestLexer(t *testing.T) {
testCases := []lexerTestCase{
testCases := []struct {
input []byte
expected []Token
expectErr bool
errorMessage string
}{
{
input: []byte(`host:123`),
expected: []Token{
@@ -249,12 +249,178 @@ func TestLexer(t *testing.T) {
{Line: 1, Text: `quotes`},
},
},
{
input: []byte(`heredoc <<EOF
content
EOF same-line-arg
`),
expected: []Token{
{Line: 1, Text: `heredoc`},
{Line: 1, Text: "content"},
{Line: 3, Text: `same-line-arg`},
},
},
{
input: []byte(`heredoc <<VERY-LONG-MARKER
content
VERY-LONG-MARKER same-line-arg
`),
expected: []Token{
{Line: 1, Text: `heredoc`},
{Line: 1, Text: "content"},
{Line: 3, Text: `same-line-arg`},
},
},
{
input: []byte(`heredoc <<EOF
extra-newline
EOF same-line-arg
`),
expected: []Token{
{Line: 1, Text: `heredoc`},
{Line: 1, Text: "extra-newline\n"},
{Line: 4, Text: `same-line-arg`},
},
},
{
input: []byte(`heredoc <<EOF
EOF same-line-arg
`),
expected: []Token{
{Line: 1, Text: `heredoc`},
{Line: 1, Text: ""},
{Line: 2, Text: `same-line-arg`},
},
},
{
input: []byte(`heredoc <<EOF
content
EOF same-line-arg
`),
expected: []Token{
{Line: 1, Text: `heredoc`},
{Line: 1, Text: "content"},
{Line: 3, Text: `same-line-arg`},
},
},
{
input: []byte(`prev-line
heredoc <<EOF
multi
line
content
EOF same-line-arg
next-line
`),
expected: []Token{
{Line: 1, Text: `prev-line`},
{Line: 2, Text: `heredoc`},
{Line: 2, Text: "\tmulti\n\tline\n\tcontent"},
{Line: 6, Text: `same-line-arg`},
{Line: 7, Text: `next-line`},
},
},
{
input: []byte(`heredoc <EOF
content
EOF same-line-arg
`),
expected: []Token{
{Line: 1, Text: `heredoc`},
{Line: 1, Text: `<EOF`},
{Line: 2, Text: `content`},
{Line: 3, Text: `EOF`},
{Line: 3, Text: `same-line-arg`},
},
},
{
input: []byte(`heredoc <<s
s
`),
expected: []Token{
{Line: 1, Text: `heredoc`},
{Line: 1, Text: ""},
},
},
{
input: []byte("\u000Aheredoc \u003C\u003C\u0073\u0073\u000A\u00BF\u0057\u0001\u0000\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u003D\u001F\u000A\u0073\u0073\u000A\u00BF\u0057\u0001\u0000\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u003D\u001F\u000A\u00BF\u00BF\u0057\u0001\u0000\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u003D\u001F"),
expected: []Token{
{
Line: 2,
Text: "heredoc",
},
{
Line: 2,
Text: "\u00BF\u0057\u0001\u0000\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u003D\u001F",
},
{
Line: 5,
Text: "\u00BF\u0057\u0001\u0000\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u003D\u001F",
},
{
Line: 6,
Text: "\u00BF\u00BF\u0057\u0001\u0000\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u003D\u001F",
},
},
},
{
input: []byte(`heredoc <<HERE SAME LINE
content
HERE same-line-arg
`),
expectErr: true,
errorMessage: "heredoc marker on line #1 must contain only alpha-numeric characters, dashes and underscores; got 'HERE SAME LINE'",
},
{
input: []byte(`heredoc <<<EOF
content
EOF same-line-arg
`),
expectErr: true,
errorMessage: "too many '<' for heredoc on line #1; only use two, for example <<END",
},
{
input: []byte(`heredoc <<EOF
content
`),
expectErr: true,
errorMessage: "incomplete heredoc <<EOF on line #3, expected ending marker EOF",
},
{
input: []byte(`heredoc <<EOF
content
EOF
`),
expectErr: true,
errorMessage: "mismatched leading whitespace in heredoc <<EOF on line #2 [\tcontent], expected whitespace [\t\t] to match the closing marker",
},
{
input: []byte(`heredoc <<EOF
content
EOF
`),
expectErr: true,
errorMessage: "mismatched leading whitespace in heredoc <<EOF on line #2 [ content], expected whitespace [\t\t] to match the closing marker",
},
}
for i, testCase := range testCases {
actual, err := Tokenize(testCase.input, "")
if testCase.expectErr {
if err == nil {
t.Fatalf("expected error, got actual: %v", actual)
continue
}
if err.Error() != testCase.errorMessage {
t.Fatalf("expected error '%v', got: %v", testCase.errorMessage, err)
}
continue
}
if err != nil {
t.Errorf("%v", err)
t.Fatalf("%v", err)
}
lexerCompare(t, i, testCase.expected, actual)
}
@@ -262,17 +428,17 @@ func TestLexer(t *testing.T) {
func lexerCompare(t *testing.T, n int, expected, actual []Token) {
if len(expected) != len(actual) {
t.Errorf("Test case %d: expected %d token(s) but got %d", n, len(expected), len(actual))
t.Fatalf("Test case %d: expected %d token(s) but got %d", n, len(expected), len(actual))
}
for i := 0; i < len(actual) && i < len(expected); i++ {
if actual[i].Line != expected[i].Line {
t.Errorf("Test case %d token %d ('%s'): expected line %d but was line %d",
t.Fatalf("Test case %d token %d ('%s'): expected line %d but was line %d",
n, i, expected[i].Text, expected[i].Line, actual[i].Line)
break
}
if actual[i].Text != expected[i].Text {
t.Errorf("Test case %d token %d: expected text '%s' but was '%s'",
t.Fatalf("Test case %d token %d: expected text '%s' but was '%s'",
n, i, expected[i].Text, actual[i].Text)
break
}
+51 -28
View File
@@ -20,7 +20,6 @@ import (
"io"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/caddyserver/caddy/v2"
@@ -61,20 +60,12 @@ func Parse(filename string, input []byte) ([]ServerBlock, error) {
// It returns all the tokens from the input, unstructured
// and in order. It may mutate input as it expands env vars.
func allTokens(filename string, input []byte) ([]Token, error) {
inputCopy, err := replaceEnvVars(input)
if err != nil {
return nil, err
}
tokens, err := Tokenize(inputCopy, filename)
if err != nil {
return nil, err
}
return tokens, nil
return Tokenize(replaceEnvVars(input), filename)
}
// replaceEnvVars replaces all occurrences of environment variables.
// It mutates the underlying array and returns the updated slice.
func replaceEnvVars(input []byte) ([]byte, error) {
func replaceEnvVars(input []byte) []byte {
var offset int
for {
begin := bytes.Index(input[offset:], spanOpen)
@@ -115,7 +106,7 @@ func replaceEnvVars(input []byte) ([]byte, error) {
// continue at the end of the replacement
offset = begin + len(envVarBytes)
}
return input, nil
return input
}
type parser struct {
@@ -181,11 +172,10 @@ func (p *parser) begin() error {
return err
}
// Just as we need to track which file the token comes from, we need to
// keep track of which snippets do the tokens come from. This is helpful
// in tracking import cycles across files/snippets by namespacing them. Without
// this we end up with false-positives in cycle-detection.
// keep track of which snippet the token comes from. This is helpful
// in tracking import cycles across files/snippets by namespacing them.
// Without this, we end up with false-positives in cycle-detection.
for k, v := range tokens {
v.inSnippet = true
v.snippetName = name
tokens[k] = v
}
@@ -345,11 +335,8 @@ func (p *parser) doImport() error {
// grab remaining args as placeholder replacements
args := p.RemainingArgs()
// add args to the replacer
repl := caddy.NewEmptyReplacer()
for index, arg := range args {
repl.Set("args."+strconv.Itoa(index), arg)
}
// set up a replacer for non-variadic args replacement
repl := makeArgsReplacer(args)
// splice out the import directive and its arguments
// (2 tokens, plus the length of args)
@@ -397,6 +384,20 @@ func (p *parser) doImport() error {
} else {
return p.Errf("File to import not found: %s", importPattern)
}
} else {
// See issue #5295 - should skip any files that start with a . when iterating over them.
sep := string(filepath.Separator)
segGlobPattern := strings.Split(globPattern, sep)
if strings.HasPrefix(segGlobPattern[len(segGlobPattern)-1], "*") {
var tmpMatches []string
for _, m := range matches {
seg := strings.Split(m, sep)
if !strings.HasPrefix(seg[len(seg)-1], ".") {
tmpMatches = append(tmpMatches, m)
}
}
matches = tmpMatches
}
}
// collect all the imported tokens
@@ -410,8 +411,8 @@ func (p *parser) doImport() error {
nodes = matches
}
nodeName := p.File()
if p.Token().inSnippet {
nodeName := p.Token().originalFile()
if p.Token().snippetName != "" {
nodeName += fmt.Sprintf(":%s", p.Token().snippetName)
}
p.importGraph.addNode(nodeName)
@@ -422,13 +423,29 @@ func (p *parser) doImport() error {
}
// copy the tokens so we don't overwrite p.definedSnippets
tokensCopy := make([]Token, len(importedTokens))
copy(tokensCopy, importedTokens)
tokensCopy := make([]Token, 0, len(importedTokens))
// run the argument replacer on the tokens
for index, token := range tokensCopy {
token.Text = repl.ReplaceKnown(token.Text, "")
tokensCopy[index] = token
// golang for range slice return a copy of value
// similarly, append also copy value
for _, token := range importedTokens {
// set the token's file to refer to import directive line number and snippet name
if token.snippetName != "" {
token.updateFile(fmt.Sprintf("%s:%d (import %s)", token.File, p.Line(), token.snippetName))
} else {
token.updateFile(fmt.Sprintf("%s:%d (import)", token.File, p.Line()))
}
foundVariadic, startIndex, endIndex := parseVariadic(token, len(args))
if foundVariadic {
for _, arg := range args[startIndex:endIndex] {
token.Text = arg
tokensCopy = append(tokensCopy, token)
}
} else {
token.Text = repl.ReplaceKnown(token.Text, "")
tokensCopy = append(tokensCopy, token)
}
}
// splice the imported tokens in the place of the import statement
@@ -459,6 +476,12 @@ func (p *parser) doSingleImport(importFile string) ([]Token, error) {
return nil, p.Errf("Could not read imported file %s: %v", importFile, err)
}
// only warning in case of empty files
if len(input) == 0 || len(strings.TrimSpace(string(input))) == 0 {
caddy.Log().Warn("Import file is empty", zap.String("file", importFile))
return []Token{}, nil
}
importedTokens, err := allTokens(importFile, input)
if err != nil {
return nil, p.Errf("Could not read tokens while importing %s: %v", importFile, err)
+100 -4
View File
@@ -21,6 +21,88 @@ import (
"testing"
)
func TestParseVariadic(t *testing.T) {
var args = make([]string, 10)
for i, tc := range []struct {
input string
result bool
}{
{
input: "",
result: false,
},
{
input: "{args[1",
result: false,
},
{
input: "1]}",
result: false,
},
{
input: "{args[:]}aaaaa",
result: false,
},
{
input: "aaaaa{args[:]}",
result: false,
},
{
input: "{args.}",
result: false,
},
{
input: "{args.1}",
result: false,
},
{
input: "{args[]}",
result: false,
},
{
input: "{args[:]}",
result: true,
},
{
input: "{args[:]}",
result: true,
},
{
input: "{args[0:]}",
result: true,
},
{
input: "{args[:0]}",
result: true,
},
{
input: "{args[-1:]}",
result: false,
},
{
input: "{args[:11]}",
result: false,
},
{
input: "{args[10:0]}",
result: false,
},
{
input: "{args[0:10]}",
result: true,
},
} {
token := Token{
File: "test",
Line: 1,
Text: tc.input,
}
if v, _, _ := parseVariadic(token, len(args)); v != tc.result {
t.Errorf("Test %d error expectation failed Expected: %t, got %t", i, tc.result, v)
}
}
}
func TestAllTokens(t *testing.T) {
input := []byte("a b c\nd e")
expected := []string{"a", "b", "c", "d", "e"}
@@ -187,6 +269,23 @@ func TestParseOneAndImport(t *testing.T) {
{`import testdata/not_found.txt`, true, []string{}, []int{}},
// empty file should just log a warning, and result in no tokens
{`import testdata/empty.txt`, false, []string{}, []int{}},
{`import testdata/only_white_space.txt`, false, []string{}, []int{}},
// import path/to/dir/* should skip any files that start with a . when iterating over them.
{`localhost
dir1 arg1
import testdata/glob/*`, false, []string{
"localhost",
}, []int{2, 3, 1}},
// import path/to/dir/.* should continue to read all dotfiles in a dir.
{`import testdata/glob/.*`, false, []string{
"host1",
}, []int{1, 2}},
{`""`, false, []string{}, []int{}},
{``, false, []string{}, []int{}},
@@ -604,10 +703,7 @@ func TestEnvironmentReplacement(t *testing.T) {
expect: "}{$",
},
} {
actual, err := replaceEnvVars([]byte(test.input))
if err != nil {
t.Fatal(err)
}
actual := replaceEnvVars([]byte(test.input))
if !bytes.Equal(actual, []byte(test.expect)) {
t.Errorf("Test %d: Expected: '%s' but got '%s'", i, test.expect, actual)
}
View File
+4
View File
@@ -0,0 +1,4 @@
host1 {
dir1
dir2 arg1
}
+2
View File
@@ -0,0 +1,2 @@
dir2 arg1 arg2
dir3
+1 -1
View File
@@ -1 +1 @@
{args.0}
{args[0]}
+1 -1
View File
@@ -1 +1 @@
{args.0} {args.1}
{args[0]} {args[1]}
+7
View File
@@ -0,0 +1,7 @@
 
+88 -21
View File
@@ -24,6 +24,7 @@ import (
"reflect"
"strconv"
"strings"
"time"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig"
@@ -75,16 +76,22 @@ func parseBind(h Helper) ([]ConfigValue, error) {
// trusted_leaf_cert <base64_der>
// trusted_leaf_cert_file <filename>
// }
// alpn <values...>
// load <paths...>
// ca <acme_ca_endpoint>
// ca_root <pem_file>
// dns <provider_name> [...]
// alpn <values...>
// load <paths...>
// ca <acme_ca_endpoint>
// ca_root <pem_file>
// key_type [ed25519|p256|p384|rsa2048|rsa4096]
// dns <provider_name> [...]
// propagation_delay <duration>
// propagation_timeout <duration>
// resolvers <dns_servers...>
// dns_ttl <duration>
// dns_challenge_override_domain <domain>
// on_demand
// eab <key_id> <mac_key>
// issuer <module_name> [...]
// get_certificate <module_name> [...]
// insecure_secrets_log <log_file>
// eab <key_id> <mac_key>
// issuer <module_name> [...]
// get_certificate <module_name> [...]
// insecure_secrets_log <log_file>
// }
func parseTLS(h Helper) ([]ConfigValue, error) {
cp := new(caddytls.ConnectionPolicy)
@@ -363,6 +370,75 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
}
acmeIssuer.Challenges.DNS.Resolvers = args
case "propagation_delay":
arg := h.RemainingArgs()
if len(arg) != 1 {
return nil, h.ArgErr()
}
delayStr := arg[0]
delay, err := caddy.ParseDuration(delayStr)
if err != nil {
return nil, h.Errf("invalid propagation_delay duration %s: %v", delayStr, err)
}
if acmeIssuer == nil {
acmeIssuer = new(caddytls.ACMEIssuer)
}
if acmeIssuer.Challenges == nil {
acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
}
if acmeIssuer.Challenges.DNS == nil {
acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
}
acmeIssuer.Challenges.DNS.PropagationDelay = caddy.Duration(delay)
case "propagation_timeout":
arg := h.RemainingArgs()
if len(arg) != 1 {
return nil, h.ArgErr()
}
timeoutStr := arg[0]
var timeout time.Duration
if timeoutStr == "-1" {
timeout = time.Duration(-1)
} else {
var err error
timeout, err = caddy.ParseDuration(timeoutStr)
if err != nil {
return nil, h.Errf("invalid propagation_timeout duration %s: %v", timeoutStr, err)
}
}
if acmeIssuer == nil {
acmeIssuer = new(caddytls.ACMEIssuer)
}
if acmeIssuer.Challenges == nil {
acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
}
if acmeIssuer.Challenges.DNS == nil {
acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
}
acmeIssuer.Challenges.DNS.PropagationTimeout = caddy.Duration(timeout)
case "dns_ttl":
arg := h.RemainingArgs()
if len(arg) != 1 {
return nil, h.ArgErr()
}
ttlStr := arg[0]
ttl, err := caddy.ParseDuration(ttlStr)
if err != nil {
return nil, h.Errf("invalid dns_ttl duration %s: %v", ttlStr, err)
}
if acmeIssuer == nil {
acmeIssuer = new(caddytls.ACMEIssuer)
}
if acmeIssuer.Challenges == nil {
acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
}
if acmeIssuer.Challenges.DNS == nil {
acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
}
acmeIssuer.Challenges.DNS.TTL = caddy.Duration(ttl)
case "dns_challenge_override_domain":
arg := h.RemainingArgs()
if len(arg) != 1 {
@@ -655,29 +731,20 @@ func parseError(h Helper) (caddyhttp.MiddlewareHandler, error) {
// parseRoute parses the route directive.
func parseRoute(h Helper) (caddyhttp.MiddlewareHandler, error) {
sr := new(caddyhttp.Subroute)
allResults, err := parseSegmentAsConfig(h)
if err != nil {
return nil, err
}
for _, result := range allResults {
switch handler := result.Value.(type) {
case caddyhttp.Route:
sr.Routes = append(sr.Routes, handler)
case caddyhttp.Subroute:
// directives which return a literal subroute instead of a route
// means they intend to keep those handlers together without
// them being reordered; we're doing that anyway since we're in
// the route directive, so just append its handlers
sr.Routes = append(sr.Routes, handler.Routes...)
switch result.Value.(type) {
case caddyhttp.Route, caddyhttp.Subroute:
default:
return nil, h.Errf("%s directive returned something other than an HTTP route or subroute: %#v (only handler directives can be used in routes)", result.directive, result.Value)
}
}
return sr, nil
return buildSubroute(allResults, h.groupCounter, false)
}
func parseHandle(h Helper) (caddyhttp.MiddlewareHandler, error) {
@@ -1,6 +1,7 @@
package httpcaddyfile
import (
"strings"
"testing"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -213,3 +214,45 @@ func TestRedirDirectiveSyntax(t *testing.T) {
}
}
}
func TestImportErrorLine(t *testing.T) {
for i, tc := range []struct {
input string
errorFunc func(err error) bool
}{
{
input: `(t1) {
abort {args[:]}
}
:8080 {
import t1
import t1 true
}`,
errorFunc: func(err error) bool {
return err != nil && strings.Contains(err.Error(), "Caddyfile:6 (import t1):2")
},
},
{
input: `(t1) {
abort {args[:]}
}
:8080 {
import t1 true
}`,
errorFunc: func(err error) bool {
return err != nil && strings.Contains(err.Error(), "Caddyfile:5 (import t1):2")
},
},
} {
adapter := caddyfile.Adapter{
ServerType: ServerType{},
}
_, _, err := adapter.Adapt([]byte(tc.input), nil)
if !tc.errorFunc(err) {
t.Errorf("Test %d error expectation failed, got %s", i, err)
continue
}
}
}
+18 -17
View File
@@ -289,7 +289,7 @@ func ParseSegmentAsSubroute(h Helper) (caddyhttp.MiddlewareHandler, error) {
return nil, err
}
return buildSubroute(allResults, h.groupCounter)
return buildSubroute(allResults, h.groupCounter, true)
}
// parseSegmentAsConfig parses the segment such that its subdirectives
@@ -427,26 +427,16 @@ func sortRoutes(routes []ConfigValue) {
jPathLen = len(jPM[0])
}
// some directives involve setting values which can overwrite
// each other, so it makes most sense to reverse the order so
// that the lease specific matcher is first; everything else
// has most-specific matcher first
if iDir == "vars" {
sortByPath := func() bool {
// we can only confidently compare path lengths if both
// directives have a single path to match (issue #5037)
if iPathLen > 0 && jPathLen > 0 {
// sort least-specific (shortest) path first
return iPathLen < jPathLen
}
// if both paths are the same except for a trailing wildcard,
// sort by the shorter path first (which is more specific)
if strings.TrimSuffix(iPM[0], "*") == strings.TrimSuffix(jPM[0], "*") {
return iPathLen < jPathLen
}
// if both directives don't have a single path to compare,
// sort whichever one has no matcher first; if both have
// no matcher, sort equally (stable sort preserves order)
return len(iRoute.MatcherSetsRaw) == 0 && len(jRoute.MatcherSetsRaw) > 0
} else {
// we can only confidently compare path lengths if both
// directives have a single path to match (issue #5037)
if iPathLen > 0 && jPathLen > 0 {
// sort most-specific (longest) path first
return iPathLen > jPathLen
}
@@ -455,7 +445,18 @@ func sortRoutes(routes []ConfigValue) {
// sort whichever one has a matcher first; if both have
// a matcher, sort equally (stable sort preserves order)
return len(iRoute.MatcherSetsRaw) > 0 && len(jRoute.MatcherSetsRaw) == 0
}()
// some directives involve setting values which can overwrite
// each other, so it makes most sense to reverse the order so
// that the least-specific matcher is first, allowing the last
// matching one to win
if iDir == "vars" {
return !sortByPath
}
// everything else is most-specific matcher first
return sortByPath
})
}
+25 -9
View File
@@ -241,7 +241,9 @@ func (st ServerType) Setup(inputServerBlocks []caddyfile.ServerBlock,
if _, ok := options["debug"]; ok {
customLogs = append(customLogs, namedCustomLog{
name: caddy.DefaultLoggerName,
log: &caddy.CustomLog{Level: zap.DebugLevel.CapitalString()},
log: &caddy.CustomLog{
BaseLog: caddy.BaseLog{Level: zap.DebugLevel.CapitalString()},
},
})
}
}
@@ -286,6 +288,17 @@ func (st ServerType) Setup(inputServerBlocks []caddyfile.ServerBlock,
if adminConfig, ok := options["admin"].(*caddy.AdminConfig); ok && adminConfig != nil {
cfg.Admin = adminConfig
}
if pc, ok := options["persist_config"].(string); ok && pc == "off" {
if cfg.Admin == nil {
cfg.Admin = new(caddy.AdminConfig)
}
if cfg.Admin.Config == nil {
cfg.Admin.Config = new(caddy.ConfigSettings)
}
cfg.Admin.Config.Persist = new(bool)
}
if len(customLogs) > 0 {
if cfg.Logging == nil {
cfg.Logging = &caddy.Logging{
@@ -618,7 +631,7 @@ func (st *ServerType) serversFromPairings(
// set up each handler directive, making sure to honor directive order
dirRoutes := sblock.pile["route"]
siteSubroute, err := buildSubroute(dirRoutes, groupCounter)
siteSubroute, err := buildSubroute(dirRoutes, groupCounter, true)
if err != nil {
return nil, err
}
@@ -706,7 +719,7 @@ func (st *ServerType) serversFromPairings(
err := applyServerOptions(servers, options, warnings)
if err != nil {
return nil, err
return nil, fmt.Errorf("applying global server options: %v", err)
}
return servers, nil
@@ -959,14 +972,16 @@ func appendSubrouteToRouteList(routeList caddyhttp.RouteList,
// buildSubroute turns the config values, which are expected to be routes
// into a clean and orderly subroute that has all the routes within it.
func buildSubroute(routes []ConfigValue, groupCounter counter) (*caddyhttp.Subroute, error) {
for _, val := range routes {
if !directiveIsOrdered(val.directive) {
return nil, fmt.Errorf("directive '%s' is not an ordered HTTP handler, so it cannot be used here", val.directive)
func buildSubroute(routes []ConfigValue, groupCounter counter, needsSorting bool) (*caddyhttp.Subroute, error) {
if needsSorting {
for _, val := range routes {
if !directiveIsOrdered(val.directive) {
return nil, fmt.Errorf("directive '%s' is not an ordered HTTP handler, so it cannot be used here", val.directive)
}
}
}
sortRoutes(routes)
sortRoutes(routes)
}
subroute := new(caddyhttp.Subroute)
@@ -1315,6 +1330,7 @@ func placeholderShorthands() []string {
"{tls_client_certificate_pem}", "{http.request.tls.client.certificate_pem}",
"{tls_client_certificate_der_base64}", "{http.request.tls.client.certificate_der_base64}",
"{upstream_hostport}", "{http.reverse_proxy.upstream.hostport}",
"{client_ip}", "{http.vars.client_ip}",
}
}
+16
View File
@@ -54,6 +54,7 @@ func init() {
RegisterGlobalOption("ocsp_stapling", parseOCSPStaplingOptions)
RegisterGlobalOption("log", parseLogOptions)
RegisterGlobalOption("preferred_chains", parseOptPreferredChains)
RegisterGlobalOption("persist_config", parseOptPersistConfig)
}
func parseOptTrue(d *caddyfile.Dispenser, _ any) (any, error) { return true, nil }
@@ -386,6 +387,21 @@ func parseOptOnDemand(d *caddyfile.Dispenser, _ any) (any, error) {
return ond, nil
}
func parseOptPersistConfig(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume parameter name
if !d.Next() {
return "", d.ArgErr()
}
val := d.Val()
if d.Next() {
return "", d.ArgErr()
}
if val != "off" {
return "", d.Errf("persist_config must be 'off'")
}
return val, nil
}
func parseOptAutoHTTPS(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume parameter name
if !d.Next() {
+29 -17
View File
@@ -15,6 +15,7 @@
package httpcaddyfile
import (
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddypki"
@@ -26,23 +27,24 @@ func init() {
// parsePKIApp parses the global log option. Syntax:
//
// pki {
// ca [<id>] {
// name <name>
// root_cn <name>
// intermediate_cn <name>
// root {
// cert <path>
// key <path>
// format <format>
// }
// intermediate {
// cert <path>
// key <path>
// format <format>
// }
// }
// }
// pki {
// ca [<id>] {
// name <name>
// root_cn <name>
// intermediate_cn <name>
// intermediate_lifetime <duration>
// root {
// cert <path>
// key <path>
// format <format>
// }
// intermediate {
// cert <path>
// key <path>
// format <format>
// }
// }
// }
//
// When the CA ID is unspecified, 'local' is assumed.
func parsePKIApp(d *caddyfile.Dispenser, existingVal any) (any, error) {
@@ -83,6 +85,16 @@ func parsePKIApp(d *caddyfile.Dispenser, existingVal any) (any, error) {
}
pkiCa.IntermediateCommonName = d.Val()
case "intermediate_lifetime":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, err
}
pkiCa.IntermediateLifetime = caddy.Duration(dur)
case "root":
if pkiCa.Root == nil {
pkiCa.Root = new(caddypki.KeyPair)
+73 -1
View File
@@ -33,6 +33,7 @@ type serverOptions struct {
ListenerAddress string
// These will all map 1:1 to the caddyhttp.Server struct
Name string
ListenerWrappersRaw []json.RawMessage
ReadTimeout caddy.Duration
ReadHeaderTimeout caddy.Duration
@@ -42,6 +43,8 @@ type serverOptions struct {
MaxHeaderBytes int
Protocols []string
StrictSNIHost *bool
TrustedProxiesRaw json.RawMessage
ClientIPHeaders []string
ShouldLogCredentials bool
Metrics *caddyhttp.Metrics
}
@@ -57,6 +60,15 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
}
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "name":
if serverOpts.ListenerAddress == "" {
return nil, d.Errf("cannot set a name for a server without a listener address")
}
if !d.NextArg() {
return nil, d.ArgErr()
}
serverOpts.Name = d.Val()
case "listener_wrappers":
for nesting := d.Nesting(); d.NextBlock(nesting); {
modID := "caddy.listeners." + d.Val()
@@ -176,6 +188,39 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
}
serverOpts.StrictSNIHost = &boolVal
case "trusted_proxies":
if !d.NextArg() {
return nil, d.Err("trusted_proxies expects an IP range source module name as its first argument")
}
modID := "http.ip_sources." + d.Val()
unm, err := caddyfile.UnmarshalModule(d, modID)
if err != nil {
return nil, err
}
source, ok := unm.(caddyhttp.IPRangeSource)
if !ok {
return nil, fmt.Errorf("module %s (%T) is not an IP range source", modID, unm)
}
jsonSource := caddyconfig.JSONModuleObject(
source,
"source",
source.(caddy.Module).CaddyModule().ID.Name(),
nil,
)
serverOpts.TrustedProxiesRaw = jsonSource
case "client_ip_headers":
headers := d.RemainingArgs()
for _, header := range headers {
if sliceContains(serverOpts.ClientIPHeaders, header) {
return nil, d.Errf("client IP header %s specified more than once", header)
}
serverOpts.ClientIPHeaders = append(serverOpts.ClientIPHeaders, header)
}
if nesting := d.Nesting(); d.NextBlock(nesting) {
return nil, d.ArgErr()
}
case "metrics":
if d.NextArg() {
return nil, d.ArgErr()
@@ -238,7 +283,22 @@ func applyServerOptions(
return nil
}
for _, server := range servers {
// check for duplicate names, which would clobber the config
existingNames := map[string]bool{}
for _, opts := range serverOpts {
if opts.Name == "" {
continue
}
if existingNames[opts.Name] {
return fmt.Errorf("cannot use duplicate server name '%s'", opts.Name)
}
existingNames[opts.Name] = true
}
// collect the server name overrides
nameReplacements := map[string]string{}
for key, server := range servers {
// find the options that apply to this server
opts := func() *serverOptions {
for _, entry := range serverOpts {
@@ -269,6 +329,8 @@ func applyServerOptions(
server.MaxHeaderBytes = opts.MaxHeaderBytes
server.Protocols = opts.Protocols
server.StrictSNIHost = opts.StrictSNIHost
server.TrustedProxiesRaw = opts.TrustedProxiesRaw
server.ClientIPHeaders = opts.ClientIPHeaders
server.Metrics = opts.Metrics
if opts.ShouldLogCredentials {
if server.Logs == nil {
@@ -276,6 +338,16 @@ func applyServerOptions(
}
server.Logs.ShouldLogCredentials = opts.ShouldLogCredentials
}
if opts.Name != "" {
nameReplacements[key] = opts.Name
}
}
// rename the servers if marked to do so
for old, new := range nameReplacements {
servers[new] = servers[old]
delete(servers, old)
}
return nil
+19 -19
View File
@@ -206,8 +206,8 @@ func (st ServerType) buildTLSApp(
}
// associate our new automation policy with this server block's hosts
ap.Subjects = sblock.hostsFromKeysNotHTTP(httpPort)
sort.Strings(ap.Subjects) // solely for deterministic test results
ap.SubjectsRaw = sblock.hostsFromKeysNotHTTP(httpPort)
sort.Strings(ap.SubjectsRaw) // solely for deterministic test results
// if a combination of public and internal names were given
// for this same server block and no issuer was specified, we
@@ -217,7 +217,7 @@ func (st ServerType) buildTLSApp(
var ap2 *caddytls.AutomationPolicy
if len(ap.Issuers) == 0 {
var internal, external []string
for _, s := range ap.Subjects {
for _, s := range ap.SubjectsRaw {
if !certmagic.SubjectQualifiesForCert(s) {
return nil, warnings, fmt.Errorf("subject does not qualify for certificate: '%s'", s)
}
@@ -235,10 +235,10 @@ func (st ServerType) buildTLSApp(
}
}
if len(external) > 0 && len(internal) > 0 {
ap.Subjects = external
ap.SubjectsRaw = external
apCopy := *ap
ap2 = &apCopy
ap2.Subjects = internal
ap2.SubjectsRaw = internal
ap2.IssuersRaw = []json.RawMessage{caddyconfig.JSONModuleObject(caddytls.InternalIssuer{}, "module", "internal", &warnings)}
}
}
@@ -339,14 +339,14 @@ func (st ServerType) buildTLSApp(
for h := range httpsHostsSharedWithHostlessKey {
al = append(al, h)
if !certmagic.SubjectQualifiesForPublicCert(h) {
internalAP.Subjects = append(internalAP.Subjects, h)
internalAP.SubjectsRaw = append(internalAP.SubjectsRaw, h)
}
}
}
if len(al) > 0 {
tlsApp.CertificatesRaw["automate"] = caddyconfig.JSON(al, &warnings)
}
if len(internalAP.Subjects) > 0 {
if len(internalAP.SubjectsRaw) > 0 {
if tlsApp.Automation == nil {
tlsApp.Automation = new(caddytls.AutomationConfig)
}
@@ -412,7 +412,7 @@ func (st ServerType) buildTLSApp(
// for convenience)
automationHostSet := make(map[string]struct{})
for _, ap := range tlsApp.Automation.Policies {
for _, s := range ap.Subjects {
for _, s := range ap.SubjectsRaw {
if _, ok := automationHostSet[s]; ok {
return nil, warnings, fmt.Errorf("hostname appears in more than one automation policy, making certificate management ambiguous: %s", s)
}
@@ -533,7 +533,7 @@ func consolidateAutomationPolicies(aps []*caddytls.AutomationPolicy) []*caddytls
if automationPolicyIsSubset(aps[j], aps[i]) {
return false
}
return len(aps[i].Subjects) > len(aps[j].Subjects)
return len(aps[i].SubjectsRaw) > len(aps[j].SubjectsRaw)
})
emptyAPCount := 0
@@ -541,7 +541,7 @@ func consolidateAutomationPolicies(aps []*caddytls.AutomationPolicy) []*caddytls
// compute the number of empty policies (disregarding subjects) - see #4128
emptyAP := new(caddytls.AutomationPolicy)
for i := 0; i < len(aps); i++ {
emptyAP.Subjects = aps[i].Subjects
emptyAP.SubjectsRaw = aps[i].SubjectsRaw
if reflect.DeepEqual(aps[i], emptyAP) {
emptyAPCount++
if !automationPolicyHasAllPublicNames(aps[i]) {
@@ -583,7 +583,7 @@ outer:
aps[i].KeyType == aps[j].KeyType &&
aps[i].OnDemand == aps[j].OnDemand &&
aps[i].RenewalWindowRatio == aps[j].RenewalWindowRatio {
if len(aps[i].Subjects) > 0 && len(aps[j].Subjects) == 0 {
if len(aps[i].SubjectsRaw) > 0 && len(aps[j].SubjectsRaw) == 0 {
// later policy (at j) has no subjects ("catch-all"), so we can
// remove the identical-but-more-specific policy that comes first
// AS LONG AS it is not shadowed by another policy before it; e.g.
@@ -598,9 +598,9 @@ outer:
}
} else {
// avoid repeated subjects
for _, subj := range aps[j].Subjects {
if !sliceContains(aps[i].Subjects, subj) {
aps[i].Subjects = append(aps[i].Subjects, subj)
for _, subj := range aps[j].SubjectsRaw {
if !sliceContains(aps[i].SubjectsRaw, subj) {
aps[i].SubjectsRaw = append(aps[i].SubjectsRaw, subj)
}
}
aps = append(aps[:j], aps[j+1:]...)
@@ -616,15 +616,15 @@ outer:
// automationPolicyIsSubset returns true if a's subjects are a subset
// of b's subjects.
func automationPolicyIsSubset(a, b *caddytls.AutomationPolicy) bool {
if len(b.Subjects) == 0 {
if len(b.SubjectsRaw) == 0 {
return true
}
if len(a.Subjects) == 0 {
if len(a.SubjectsRaw) == 0 {
return false
}
for _, aSubj := range a.Subjects {
for _, aSubj := range a.SubjectsRaw {
var inSuperset bool
for _, bSubj := range b.Subjects {
for _, bSubj := range b.SubjectsRaw {
if certmagic.MatchWildcard(aSubj, bSubj) {
inSuperset = true
break
@@ -662,7 +662,7 @@ func subjectQualifiesForPublicCert(ap *caddytls.AutomationPolicy, subj string) b
}
func automationPolicyHasAllPublicNames(ap *caddytls.AutomationPolicy) bool {
for _, subj := range ap.Subjects {
for _, subj := range ap.SubjectsRaw {
if !subjectQualifiesForPublicCert(ap, subj) {
return false
}
+2 -2
View File
@@ -47,8 +47,8 @@ func TestAutomationPolicyIsSubset(t *testing.T) {
expect: false,
},
} {
apA := &caddytls.AutomationPolicy{Subjects: test.a}
apB := &caddytls.AutomationPolicy{Subjects: test.b}
apA := &caddytls.AutomationPolicy{SubjectsRaw: test.a}
apB := &caddytls.AutomationPolicy{SubjectsRaw: test.b}
if actual := automationPolicyIsSubset(apA, apB); actual != test.expect {
t.Errorf("Test %d: Expected %t but got %t (A: %v B: %v)", i, test.expect, actual, test.a, test.b)
}
+3 -2
View File
@@ -124,6 +124,7 @@ func attemptHttpCall(client *http.Client, request *http.Request) (*http.Response
if err != nil {
return nil, fmt.Errorf("problem calling http loader url: %v", err)
} else if resp.StatusCode < 200 || resp.StatusCode > 499 {
resp.Body.Close()
return nil, fmt.Errorf("bad response status code from http loader url: %v", resp.StatusCode)
}
return resp, nil
@@ -134,16 +135,16 @@ func doHttpCallWithRetries(ctx caddy.Context, client *http.Client, request *http
var err error
const maxAttempts = 10
// attempt up to 10 times
for i := 0; i < maxAttempts; i++ {
resp, err = attemptHttpCall(client, request)
if err != nil && i < maxAttempts-1 {
// wait 500ms before reattempting, or until context is done
select {
case <-time.After(time.Millisecond * 500):
case <-ctx.Done():
return resp, ctx.Err()
}
} else {
break
}
}
+22 -5
View File
@@ -114,7 +114,7 @@ func (tc *Tester) initServer(rawConfig string, configType string) error {
return nil
}
err := validateTestPrerequisites()
err := validateTestPrerequisites(tc.t)
if err != nil {
tc.t.Skipf("skipping tests as failed integration prerequisites. %s", err)
return nil
@@ -218,15 +218,20 @@ func (tc *Tester) ensureConfigRunning(rawConfig string, configType string) error
if reflect.DeepEqual(expected, fetchConfig(client)) {
return nil
}
time.Sleep(10 * time.Millisecond)
time.Sleep(1 * time.Second)
}
tc.t.Errorf("POSTed configuration isn't active")
return errors.New("EnsureConfigRunning: POSTed configuration isn't active")
}
const initConfig = `{
admin localhost:2999
}
`
// validateTestPrerequisites ensures the certificates are available in the
// designated path and Caddy sub-process is running.
func validateTestPrerequisites() error {
func validateTestPrerequisites(t *testing.T) error {
// check certificates are found
for _, certName := range Default.Certifcates {
@@ -236,15 +241,27 @@ func validateTestPrerequisites() error {
}
if isCaddyAdminRunning() != nil {
// setup the init config file, and set the cleanup afterwards
f, err := os.CreateTemp("", "")
if err != nil {
return err
}
t.Cleanup(func() {
os.Remove(f.Name())
})
if _, err := f.WriteString(initConfig); err != nil {
return err
}
// start inprocess caddy server
os.Args = []string{"caddy", "run", "--config", "./test.init.config", "--adapter", "caddyfile"}
os.Args = []string{"caddy", "run", "--config", f.Name(), "--adapter", "caddyfile"}
go func() {
caddycmd.Main()
}()
// wait for caddy to start serving the initial config
for retries := 10; retries > 0 && isCaddyAdminRunning() != nil; retries-- {
time.Sleep(10 * time.Millisecond)
time.Sleep(1 * time.Second)
}
}
@@ -0,0 +1,108 @@
{
pki {
ca internal {
name "Internal"
root_cn "Internal Root Cert"
intermediate_cn "Internal Intermediate Cert"
}
ca internal-long-lived {
name "Long-lived"
root_cn "Internal Root Cert 2"
intermediate_cn "Internal Intermediate Cert 2"
}
}
}
acme-internal.example.com {
acme_server {
ca internal
}
}
acme-long-lived.example.com {
acme_server {
ca internal-long-lived
lifetime 7d
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"acme-long-lived.example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"ca": "internal-long-lived",
"handler": "acme_server",
"lifetime": 604800000000000
}
]
}
]
}
],
"terminal": true
},
{
"match": [
{
"host": [
"acme-internal.example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"ca": "internal",
"handler": "acme_server"
}
]
}
]
}
],
"terminal": true
}
]
}
}
},
"pki": {
"certificate_authorities": {
"internal": {
"name": "Internal",
"root_common_name": "Internal Root Cert",
"intermediate_common_name": "Internal Intermediate Cert"
},
"internal-long-lived": {
"name": "Long-lived",
"root_common_name": "Internal Root Cert 2",
"intermediate_common_name": "Internal Intermediate Cert 2"
}
}
}
}
}
@@ -0,0 +1,36 @@
{
http_port 8080
persist_config off
admin {
origins localhost:2019 [::1]:2019 127.0.0.1:2019 192.168.10.128
}
}
:80
----------
{
"admin": {
"listen": "localhost:2019",
"origins": [
"localhost:2019",
"[::1]:2019",
"127.0.0.1:2019",
"192.168.10.128"
],
"config": {
"persist": false
}
},
"apps": {
"http": {
"http_port": 8080,
"servers": {
"srv0": {
"listen": [
":80"
]
}
}
}
}
}
@@ -0,0 +1,25 @@
{
persist_config off
}
:8881 {
}
----------
{
"admin": {
"config": {
"persist": false
}
},
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":8881"
]
}
}
}
}
}
@@ -165,4 +165,4 @@ acme-bar.example.com {
}
}
}
}
}
@@ -14,6 +14,9 @@
log_credentials
protocols h1 h2 h2c h3
strict_sni_host
trusted_proxies static private_ranges
client_ip_headers Custom-Real-Client-IP X-Forwarded-For
client_ip_headers A-Third-One
}
}
@@ -55,6 +58,22 @@ foo.com {
}
],
"strict_sni_host": true,
"trusted_proxies": {
"ranges": [
"192.168.0.0/16",
"172.16.0.0/12",
"10.0.0.0/8",
"127.0.0.1/8",
"fd00::/8",
"::1"
],
"source": "static"
},
"client_ip_headers": [
"Custom-Real-Client-IP",
"X-Forwarded-For",
"A-Third-One"
],
"logs": {
"should_log_credentials": true
},
@@ -0,0 +1,78 @@
:8881 {
route {
handle /foo/* {
respond "Foo"
}
handle {
respond "Bar"
}
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":8881"
],
"routes": [
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"group": "group2",
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "Foo",
"handler": "static_response"
}
]
}
]
}
],
"match": [
{
"path": [
"/foo/*"
]
}
]
},
{
"group": "group2",
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "Bar",
"handler": "static_response"
}
]
}
]
}
]
}
]
}
]
}
]
}
}
}
}
}
@@ -0,0 +1,50 @@
example.com {
respond <<EOF
<html>
<head><title>Foo</title>
<body>Foo</body>
</html>
EOF 200
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "\u003chtml\u003e\n \u003chead\u003e\u003ctitle\u003eFoo\u003c/title\u003e\n \u003cbody\u003eFoo\u003c/body\u003e\n\u003c/html\u003e",
"handler": "static_response",
"status_code": 200
}
]
}
]
}
],
"terminal": true
}
]
}
}
}
}
}
@@ -1,6 +1,6 @@
(logging) {
log {
output file /var/log/caddy/{args.0}.access.log
output file /var/log/caddy/{args[0]}.access.log
}
}
@@ -100,16 +100,16 @@ vars {
],
"source": "{http.request.host}"
},
{
"foo": "bar",
"handler": "vars"
},
{
"abc": true,
"def": 1,
"ghi": 2.3,
"handler": "vars",
"jkl": "mn op"
},
{
"foo": "bar",
"handler": "vars"
}
]
}
@@ -43,6 +43,9 @@
@matcher11 remote_ip private_ranges
respond @matcher11 "remote_ip matcher with private ranges"
@matcher12 client_ip private_ranges
respond @matcher12 "client_ip matcher with private ranges"
}
----------
{
@@ -250,6 +253,28 @@
"handler": "static_response"
}
]
},
{
"match": [
{
"client_ip": {
"ranges": [
"192.168.0.0/16",
"172.16.0.0/12",
"10.0.0.0/8",
"127.0.0.1/8",
"fd00::/8",
"::1"
]
}
}
],
"handle": [
{
"body": "client_ip matcher with private ranges",
"handler": "static_response"
}
]
}
]
}
@@ -74,6 +74,7 @@ route {
]
},
{
"group": "group0",
"handle": [
{
"handler": "rewrite",
@@ -129,4 +130,4 @@ route {
}
}
}
}
}
@@ -11,6 +11,7 @@
resolvers 8.8.8.8 8.8.4.4
dial_timeout 2s
dial_fallback_delay 300ms
versions ipv6
}
}
}
@@ -66,7 +67,10 @@
"8.8.4.4"
]
},
"source": "a"
"source": "a",
"versions": {
"ipv6": true
}
},
"handler": "reverse_proxy"
}
@@ -113,4 +117,4 @@
}
}
}
}
}
@@ -1,4 +1,3 @@
https://example.com {
reverse_proxy /path https://localhost:54321 {
header_up Host {upstream_hostport}
@@ -7,7 +6,7 @@ https://example.com {
method GET
rewrite /rewritten?uri={uri}
buffer_requests
request_buffers 4KB
transport http {
read_buffer 10MB
@@ -24,13 +23,12 @@ https://example.com {
max_conns_per_host 5
keepalive_idle_conns_per_host 2
keepalive_interval 30s
tls_renegotiation freely
tls_except_ports 8181 8182
}
}
}
----------
{
"apps": {
@@ -56,7 +54,6 @@ https://example.com {
{
"handle": [
{
"buffer_requests": true,
"handler": "reverse_proxy",
"headers": {
"request": {
@@ -70,6 +67,7 @@ https://example.com {
}
}
},
"request_buffers": 4000,
"rewrite": {
"method": "GET",
"uri": "/rewritten?uri={http.request.uri}"
@@ -0,0 +1,77 @@
{
servers :443 {
name https
}
servers :8000 {
name app1
}
servers :8001 {
name app2
}
servers 123.123.123.123:8002 {
name bind-server
}
}
example.com {
}
:8000 {
}
:8001, :8002 {
}
:8002 {
bind 123.123.123.123 222.222.222.222
}
----------
{
"apps": {
"http": {
"servers": {
"app1": {
"listen": [
":8000"
]
},
"app2": {
"listen": [
":8001"
]
},
"bind-server": {
"listen": [
"123.123.123.123:8002",
"222.222.222.222:8002"
]
},
"https": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"terminal": true
}
]
},
"srv4": {
"listen": [
":8002"
]
}
}
}
}
}
@@ -1,12 +1,15 @@
*.example.com {
@foo host foo.example.com
handle @foo {
handle_path /strip* {
handle_path /strip {
respond "this should be first"
}
handle {
handle_path /strip* {
respond "this should be second"
}
handle {
respond "this should be last"
}
}
handle {
respond "this should be last"
@@ -35,13 +38,13 @@
"handler": "subroute",
"routes": [
{
"group": "group5",
"group": "group6",
"handle": [
{
"handler": "subroute",
"routes": [
{
"group": "group2",
"group": "group3",
"handle": [
{
"handler": "subroute",
@@ -68,17 +71,25 @@
"match": [
{
"path": [
"/strip*"
"/strip"
]
}
]
},
{
"group": "group2",
"group": "group3",
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "rewrite",
"strip_path_prefix": "/strip"
}
]
},
{
"handle": [
{
@@ -89,6 +100,31 @@
}
]
}
],
"match": [
{
"path": [
"/strip*"
]
}
]
},
{
"group": "group3",
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "this should be last",
"handler": "static_response"
}
]
}
]
}
]
}
]
@@ -103,7 +139,7 @@
]
},
{
"group": "group5",
"group": "group6",
"handle": [
{
"handler": "subroute",
@@ -1,7 +1,8 @@
:80
vars /foobar foo last
vars /foo foo middle
vars /foo foo middle-last
vars /foo* foo middle-first
vars * foo first
----------
{
@@ -21,6 +22,21 @@ vars * foo first
}
]
},
{
"match": [
{
"path": [
"/foo*"
]
}
],
"handle": [
{
"foo": "middle-first",
"handler": "vars"
}
]
},
{
"match": [
{
@@ -31,7 +47,7 @@ vars * foo first
],
"handle": [
{
"foo": "middle",
"foo": "middle-last",
"handler": "vars"
}
]
@@ -0,0 +1,76 @@
localhost
respond "hello from localhost"
tls {
dns_ttl 5m10s
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "hello from localhost",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"localhost"
],
"issuers": [
{
"challenges": {
"dns": {
"ttl": 310000000000
}
},
"module": "acme"
},
{
"challenges": {
"dns": {
"ttl": 310000000000
}
},
"module": "zerossl"
}
]
}
]
}
}
}
}
@@ -0,0 +1,81 @@
localhost
respond "hello from localhost"
tls {
issuer acme {
dns_ttl 5m10s
}
issuer zerossl {
dns_ttl 10m20s
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "hello from localhost",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"localhost"
],
"issuers": [
{
"challenges": {
"dns": {
"ttl": 310000000000
}
},
"module": "acme"
},
{
"challenges": {
"dns": {
"ttl": 620000000000
}
},
"module": "zerossl"
}
]
}
]
}
}
}
}
@@ -0,0 +1,79 @@
localhost
respond "hello from localhost"
tls {
propagation_delay 5m10s
propagation_timeout 10m20s
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "hello from localhost",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"localhost"
],
"issuers": [
{
"challenges": {
"dns": {
"propagation_delay": 310000000000,
"propagation_timeout": 620000000000
}
},
"module": "acme"
},
{
"challenges": {
"dns": {
"propagation_delay": 310000000000,
"propagation_timeout": 620000000000
}
},
"module": "zerossl"
}
]
}
]
}
}
}
}
+101
View File
@@ -0,0 +1,101 @@
package integration
import (
"testing"
"github.com/caddyserver/caddy/v2/caddytest"
)
func TestLeafCertLifetimeLessThanIntermediate(t *testing.T) {
caddytest.AssertLoadError(t, `
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"ca": "internal",
"handler": "acme_server",
"lifetime": 604800000000000
}
]
}
]
}
]
}
]
}
}
},
"pki": {
"certificate_authorities": {
"internal": {
"install_trust": false,
"intermediate_lifetime": 604800000000000,
"name": "Internal CA"
}
}
}
}
}
`, "json", "certificate lifetime (168h0m0s) should be less than intermediate certificate lifetime (168h0m0s)")
}
func TestIntermediateLifetimeLessThanRoot(t *testing.T) {
caddytest.AssertLoadError(t, `
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"ca": "internal",
"handler": "acme_server",
"lifetime": 2592000000000000
}
]
}
]
}
]
}
]
}
}
},
"pki": {
"certificate_authorities": {
"internal": {
"install_trust": false,
"intermediate_lifetime": 311040000000000000,
"name": "Internal CA"
}
}
}
}
}
`, "json", "intermediate certificate lifetime must be less than root certificate lifetime (86400h0m0s)")
}
+72 -159
View File
@@ -22,80 +22,38 @@ func TestSRVReverseProxy(t *testing.T) {
},
"apps": {
"pki": {
"certificate_authorities" : {
"local" : {
"install_trust": false
"certificate_authorities": {
"local": {
"install_trust": false
}
}
},
"http": {
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
":8080"
],
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"lookup_srv": "srv.host.service.consul"
}
"http": {
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
":18080"
],
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"dynamic_upstreams": {
"source": "srv",
"name": "srv.host.service.consul"
}
}
]
}
]
}
]
}
]
}
}
}
}
}
`, "json")
}
func TestSRVWithDial(t *testing.T) {
caddytest.AssertLoadError(t, `
{
"apps": {
"pki": {
"certificate_authorities" : {
"local" : {
"install_trust": false
}
}
}
},
"http": {
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
":8080"
],
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "tcp/address.to.upstream:80",
"lookup_srv": "srv.host.service.consul"
}
]
}
]
}
]
}
}
}
}
}
`, "json", `upstream: specifying dial address is incompatible with lookup_srv: 0: {\"dial\": \"tcp/address.to.upstream:80\", \"lookup_srv\": \"srv.host.service.consul\"}`)
}
`, "json")
}
func TestDialWithPlaceholderUnix(t *testing.T) {
@@ -138,41 +96,41 @@ func TestDialWithPlaceholderUnix(t *testing.T) {
},
"apps": {
"pki": {
"certificate_authorities" : {
"local" : {
"install_trust": false
}
"certificate_authorities": {
"local": {
"install_trust": false
}
}
},
"http": {
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
":8080"
],
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "unix/{http.request.header.X-Caddy-Upstream-Dial}"
}
},
"http": {
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
":18080"
],
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "unix/{http.request.header.X-Caddy-Upstream-Dial}"
}
]
}
]
}
]
}
]
}
]
}
}
}
}
}
}
}
}
}
`, "json")
req, err := http.NewRequest(http.MethodGet, "http://localhost:8080", nil)
req, err := http.NewRequest(http.MethodGet, "http://localhost:18080", nil)
if err != nil {
t.Fail()
return
@@ -190,18 +148,18 @@ func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
},
"apps": {
"pki": {
"certificate_authorities" : {
"local" : {
"install_trust": false
}
"certificate_authorities": {
"local": {
"install_trust": false
}
}
},
},
"http": {
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
":8080"
":18080"
],
"routes": [
{
@@ -264,14 +222,14 @@ func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
}
}
}
`, "json")
`, "json")
req, err := http.NewRequest(http.MethodGet, "http://localhost:9080", nil)
if err != nil {
t.Fail()
return
}
req.Header.Set("X-Caddy-Upstream-Dial", "localhost:8080")
req.Header.Set("X-Caddy-Upstream-Dial", "localhost:18080")
tester.AssertResponse(req, 200, "Hello, World!")
}
@@ -284,18 +242,18 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
},
"apps": {
"pki": {
"certificate_authorities" : {
"local" : {
"install_trust": false
}
"certificate_authorities": {
"local": {
"install_trust": false
}
}
},
},
"http": {
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
":8080"
":18080"
],
"routes": [
{
@@ -340,7 +298,7 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "tcp/{http.request.header.X-Caddy-Upstream-Dial}:8080"
"dial": "tcp/{http.request.header.X-Caddy-Upstream-Dial}:18080"
}
]
}
@@ -358,7 +316,7 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
}
}
}
`, "json")
`, "json")
req, err := http.NewRequest(http.MethodGet, "http://localhost:9080", nil)
if err != nil {
@@ -369,51 +327,6 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
tester.AssertResponse(req, 200, "Hello, World!")
}
func TestSRVWithActiveHealthcheck(t *testing.T) {
caddytest.AssertLoadError(t, `
{
"apps": {
"pki": {
"certificate_authorities" : {
"local" : {
"install_trust": false
}
}
},
"http": {
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
":8080"
],
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"health_checks": {
"active": {
"path": "/ok"
}
},
"upstreams": [
{
"lookup_srv": "srv.host.service.consul"
}
]
}
]
}
]
}
}
}
}
}
`, "json", `upstream: lookup_srv is incompatible with active health checks: 0: {\"dial\": \"\", \"lookup_srv\": \"srv.host.service.consul\"}`)
}
func TestReverseProxyHealthCheck(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
@@ -440,7 +353,7 @@ func TestReverseProxyHealthCheck(t *testing.T) {
health_timeout 100ms
}
}
`, "caddyfile")
`, "caddyfile")
time.Sleep(100 * time.Millisecond) // TODO: for some reason this test seems particularly flaky, getting 503 when it should be 200, unless we wait
tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
-3
View File
@@ -1,3 +0,0 @@
{
admin localhost:2999
}
+1 -1
View File
@@ -1 +1 @@
respond "'I am {args.0}', hears {args.1}"
respond "'I am {args[0]}', hears {args[1]}"
+4 -4
View File
@@ -19,10 +19,10 @@
// There is no need to modify the Caddy source code to customize your
// builds. You can easily build a custom Caddy with these simple steps:
//
// 1. Copy this file (main.go) into a new folder
// 2. Edit the imports below to include the modules you want plugged in
// 3. Run `go mod init caddy`
// 4. Run `go install` or `go build` - you now have a custom binary!
// 1. Copy this file (main.go) into a new folder
// 2. Edit the imports below to include the modules you want plugged in
// 3. Run `go mod init caddy`
// 4. Run `go install` or `go build` - you now have a custom binary!
//
// Or you can use xcaddy which does it all for you as a command:
// https://github.com/caddyserver/xcaddy
+17 -8
View File
@@ -101,20 +101,29 @@ const fullDocsFooter = `Full documentation is available at:
https://caddyserver.com/docs/command-line`
func init() {
rootCmd.SetHelpTemplate(rootCmd.HelpTemplate() + "\n" + fullDocsFooter)
rootCmd.SetHelpTemplate(rootCmd.HelpTemplate() + "\n" + fullDocsFooter + "\n")
}
func caddyCmdToCoral(caddyCmd Command) *cobra.Command {
func caddyCmdToCobra(caddyCmd Command) *cobra.Command {
cmd := &cobra.Command{
Use: caddyCmd.Name,
Short: caddyCmd.Short,
Long: caddyCmd.Long,
RunE: func(cmd *cobra.Command, _ []string) error {
fls := cmd.Flags()
_, err := caddyCmd.Func(Flags{fls})
return err
},
}
cmd.Flags().AddGoFlagSet(caddyCmd.Flags)
if caddyCmd.CobraFunc != nil {
caddyCmd.CobraFunc(cmd)
} else {
cmd.RunE = WrapCommandFuncForCobra(caddyCmd.Func)
cmd.Flags().AddGoFlagSet(caddyCmd.Flags)
}
return cmd
}
// WrapCommandFuncForCobra wraps a Caddy CommandFunc for use
// in a cobra command's RunE field.
func WrapCommandFuncForCobra(f CommandFunc) func(cmd *cobra.Command, _ []string) error {
return func(cmd *cobra.Command, _ []string) error {
_, err := f(Flags{cmd.Flags()})
return err
}
}
+32 -13
View File
@@ -208,6 +208,16 @@ func cmdRun(fl Flags) (int, error) {
}
}
// create pidfile now, in case loading config takes a while (issue #5477)
if runCmdPidfileFlag != "" {
err := caddy.PIDFile(runCmdPidfileFlag)
if err != nil {
caddy.Log().Error("unable to write PID file",
zap.String("pidfile", runCmdPidfileFlag),
zap.Error(err))
}
}
// run the initial config
err = caddy.Load(config, true)
if err != nil {
@@ -242,16 +252,6 @@ func cmdRun(fl Flags) (int, error) {
go watchConfigFile(configFile, runCmdConfigAdapterFlag)
}
// create pidfile
if runCmdPidfileFlag != "" {
err := caddy.PIDFile(runCmdPidfileFlag)
if err != nil {
caddy.Log().Error("unable to write PID file",
zap.String("pidfile", runCmdPidfileFlag),
zap.Error(err))
}
}
// warn if the environment does not provide enough information about the disk
hasXDG := os.Getenv("XDG_DATA_HOME") != "" &&
os.Getenv("XDG_CONFIG_HOME") != "" &&
@@ -490,7 +490,7 @@ func cmdAdaptConfig(fl Flags) (int, error) {
// validate output if requested
if adaptCmdValidateFlag {
var cfg *caddy.Config
err = json.Unmarshal(adaptedConfig, &cfg)
err = caddy.StrictUnmarshalJSON(adaptedConfig, &cfg)
if err != nil {
return caddy.ExitCodeFailedStartup, fmt.Errorf("decoding config: %v", err)
}
@@ -506,6 +506,15 @@ func cmdAdaptConfig(fl Flags) (int, error) {
func cmdValidateConfig(fl Flags) (int, error) {
validateCmdConfigFlag := fl.String("config")
validateCmdAdapterFlag := fl.String("adapter")
runCmdLoadEnvfileFlag := fl.String("envfile")
// load all additional envs as soon as possible
if runCmdLoadEnvfileFlag != "" {
if err := loadEnvFromFile(runCmdLoadEnvfileFlag); err != nil {
return caddy.ExitCodeFailedStartup,
fmt.Errorf("loading additional environment variables: %v", err)
}
}
input, _, err := LoadConfig(validateCmdConfigFlag, validateCmdAdapterFlag)
if err != nil {
@@ -514,7 +523,7 @@ func cmdValidateConfig(fl Flags) (int, error) {
input = caddy.RemoveMetaFields(input)
var cfg *caddy.Config
err = json.Unmarshal(input, &cfg)
err = caddy.StrictUnmarshalJSON(input, &cfg)
if err != nil {
return caddy.ExitCodeFailedStartup, fmt.Errorf("decoding config: %v", err)
}
@@ -558,7 +567,10 @@ func cmdFmt(fl Flags) (int, error) {
if err := os.WriteFile(formatCmdConfigFile, output, 0600); err != nil {
return caddy.ExitCodeFailedStartup, fmt.Errorf("overwriting formatted file: %v", err)
}
} else if fl.Bool("diff") {
return caddy.ExitCodeSuccess, nil
}
if fl.Bool("diff") {
diff := difflib.Diff(
strings.Split(string(input), "\n"),
strings.Split(string(output), "\n"))
@@ -576,6 +588,13 @@ func cmdFmt(fl Flags) (int, error) {
fmt.Print(string(output))
}
if warning, diff := caddyfile.FormattingDifference(formatCmdConfigFile, input); diff {
return caddy.ExitCodeFailedStartup, fmt.Errorf(`%s:%d: Caddyfile input is not formatted; Tip: use '--overwrite' to update your Caddyfile in-place instead of previewing it. Consult '--help' for more options`,
warning.File,
warning.Line,
)
}
return caddy.ExitCodeSuccess, nil
}
+132 -135
View File
@@ -34,12 +34,6 @@ type Command struct {
// Required.
Name string
// Func is a function that executes a subcommand using
// the parsed flags. It returns an exit code and any
// associated error.
// Required.
Func CommandFunc
// Usage is a brief message describing the syntax of
// the subcommand's flags and args. Use [] to indicate
// optional parameters and <> to enclose literal values
@@ -60,7 +54,21 @@ type Command struct {
Long string
// Flags is the flagset for command.
// This is ignored if CobraFunc is set.
Flags *flag.FlagSet
// Func is a function that executes a subcommand using
// the parsed flags. It returns an exit code and any
// associated error.
// Required if CobraFunc is not set.
Func CommandFunc
// CobraFunc allows further configuration of the command
// via cobra's APIs. If this is set, then Func and Flags
// are ignored, with the assumption that they are set in
// this function. A caddycmd.WrapCommandFuncForCobra helper
// exists to simplify porting CommandFunc to Cobra's RunE.
CobraFunc func(*cobra.Command)
}
// CommandFunc is a command's function. It runs the
@@ -79,7 +87,6 @@ var commands = make(map[string]Command)
func init() {
RegisterCommand(Command{
Name: "start",
Func: cmdStart,
Usage: "[--config <path> [--adapter <name>]] [--envfile <path>] [--watch] [--pidfile <file>]",
Short: "Starts the Caddy process in the background and then returns",
Long: `
@@ -91,22 +98,21 @@ the KEY=VALUE format will be loaded into the Caddy process.
On Windows, the spawned child process will remain attached to the terminal, so
closing the window will forcefully stop Caddy; to avoid forgetting this, try
using 'caddy run' instead to keep it in the foreground.`,
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("start", flag.ExitOnError)
fs.String("config", "", "Configuration file")
fs.String("envfile", "", "Environment file to load")
fs.String("adapter", "", "Name of config adapter to apply")
fs.String("pidfile", "", "Path of file to which to write process ID")
fs.Bool("watch", false, "Reload changed config file automatically")
return fs
}(),
using 'caddy run' instead to keep it in the foreground.
`,
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().StringP("config", "c", "", "Configuration file")
cmd.Flags().StringP("adapter", "a", "", "Name of config adapter to apply")
cmd.Flags().StringP("envfile", "", "", "Environment file to load")
cmd.Flags().BoolP("watch", "w", false, "Reload changed config file automatically")
cmd.Flags().StringP("pidfile", "", "", "Path of file to which to write process ID")
cmd.RunE = WrapCommandFuncForCobra(cmdStart)
},
})
RegisterCommand(Command{
Name: "run",
Func: cmdRun,
Usage: "[--config <path> [--adapter <name>]] [--envfile <path>] [--environ] [--resume] [--watch] [--pidfile <fil>]",
Usage: "[--config <path> [--adapter <name>]] [--envfile <path>] [--environ] [--resume] [--watch] [--pidfile <file>]",
Short: `Starts the Caddy process and blocks indefinitely`,
Long: `
Starts the Caddy process, optionally bootstrapped with an initial config file,
@@ -138,44 +144,42 @@ save file. It is not an error if --resume is used and no autosave file exists.
If --watch is specified, the config file will be loaded automatically after
changes. ⚠️ This can make unintentional config changes easier; only use this
option in a local development environment.`,
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("run", flag.ExitOnError)
fs.String("config", "", "Configuration file")
fs.String("adapter", "", "Name of config adapter to apply")
fs.String("envfile", "", "Environment file to load")
fs.Bool("environ", false, "Print environment")
fs.Bool("resume", false, "Use saved config, if any (and prefer over --config file)")
fs.Bool("watch", false, "Watch config file for changes and reload it automatically")
fs.String("pidfile", "", "Path of file to which to write process ID")
fs.String("pingback", "", "Echo confirmation bytes to this address on success")
return fs
}(),
option in a local development environment.
`,
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().StringP("config", "c", "", "Configuration file")
cmd.Flags().StringP("adapter", "a", "", "Name of config adapter to apply")
cmd.Flags().StringP("envfile", "", "", "Environment file to load")
cmd.Flags().BoolP("environ", "e", false, "Print environment")
cmd.Flags().BoolP("resume", "r", false, "Use saved config, if any (and prefer over --config file)")
cmd.Flags().BoolP("watch", "w", false, "Watch config file for changes and reload it automatically")
cmd.Flags().StringP("pidfile", "", "", "Path of file to which to write process ID")
cmd.Flags().StringP("pingback", "", "", "Echo confirmation bytes to this address on success")
cmd.RunE = WrapCommandFuncForCobra(cmdRun)
},
})
RegisterCommand(Command{
Name: "stop",
Func: cmdStop,
Usage: "[--address <interface>] [--config <path> [--adapter <name>]]",
Usage: "[--config <path> [--adapter <name>]] [--address <interface>]",
Short: "Gracefully stops a started Caddy process",
Long: `
Stops the background Caddy process as gracefully as possible.
It requires that the admin API is enabled and accessible, since it will
use the API's /stop endpoint. The address of this request can be customized
using the --address flag, or from the given --config, if not the default.`,
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("stop", flag.ExitOnError)
fs.String("address", "", "The address to use to reach the admin API endpoint, if not the default")
fs.String("config", "", "Configuration file to use to parse the admin address, if --address is not used")
fs.String("adapter", "", "Name of config adapter to apply (when --config is used)")
return fs
}(),
using the --address flag, or from the given --config, if not the default.
`,
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().StringP("config", "c", "", "Configuration file to use to parse the admin address, if --address is not used")
cmd.Flags().StringP("adapter", "a", "", "Name of config adapter to apply (when --config is used)")
cmd.Flags().StringP("address", "", "", "The address to use to reach the admin API endpoint, if not the default")
cmd.RunE = WrapCommandFuncForCobra(cmdStop)
},
})
RegisterCommand(Command{
Name: "reload",
Func: cmdReload,
Usage: "--config <path> [--adapter <name>] [--address <interface>]",
Short: "Changes the config of the running Caddy instance",
Long: `
@@ -185,20 +189,19 @@ workflows revolving around config files.
Since the admin endpoint is configurable, the endpoint configuration is loaded
from the --address flag if specified; otherwise it is loaded from the given
config file; otherwise the default is assumed.`,
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("reload", flag.ExitOnError)
fs.String("config", "", "Configuration file (required)")
fs.String("adapter", "", "Name of config adapter to apply")
fs.String("address", "", "Address of the administration listener, if different from config")
fs.Bool("force", false, "Force config reload, even if it is the same")
return fs
}(),
config file; otherwise the default is assumed.
`,
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().StringP("config", "c", "", "Configuration file (required)")
cmd.Flags().StringP("adapter", "a", "", "Name of config adapter to apply")
cmd.Flags().StringP("address", "", "", "Address of the administration listener, if different from config")
cmd.Flags().BoolP("force", "f", false, "Force config reload, even if it is the same")
cmd.RunE = WrapCommandFuncForCobra(cmdReload)
},
})
RegisterCommand(Command{
Name: "version",
Func: cmdVersion,
Short: "Prints the version",
Long: `
Prints the version of this Caddy binary.
@@ -213,31 +216,29 @@ detailed version information is printed as given by Go modules.
For more details about the full version string, see the Go module
documentation: https://go.dev/doc/modules/version-numbers
`,
Func: cmdVersion,
})
RegisterCommand(Command{
Name: "list-modules",
Func: cmdListModules,
Usage: "[--packages] [--versions]",
Usage: "[--packages] [--versions] [--skip-standard]",
Short: "Lists the installed Caddy modules",
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("list-modules", flag.ExitOnError)
fs.Bool("packages", false, "Print package paths")
fs.Bool("versions", false, "Print version information")
fs.Bool("skip-standard", false, "Skip printing standard modules")
return fs
}(),
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().BoolP("packages", "", false, "Print package paths")
cmd.Flags().BoolP("versions", "", false, "Print version information")
cmd.Flags().BoolP("skip-standard", "s", false, "Skip printing standard modules")
cmd.RunE = WrapCommandFuncForCobra(cmdListModules)
},
})
RegisterCommand(Command{
Name: "build-info",
Func: cmdBuildInfo,
Short: "Prints information about this build",
Func: cmdBuildInfo,
})
RegisterCommand(Command{
Name: "environ",
Func: cmdEnviron,
Short: "Prints the environment",
Long: `
Prints the environment as seen by this Caddy process.
@@ -257,11 +258,11 @@ by adding the "--environ" flag.
Environments may contain sensitive data.
`,
Func: cmdEnviron,
})
RegisterCommand(Command{
Name: "adapt",
Func: cmdAdaptConfig,
Usage: "--config <path> [--adapter <name>] [--pretty] [--validate]",
Short: "Adapts a configuration to Caddy's native JSON",
Long: `
@@ -273,38 +274,40 @@ for human readability.
If --validate is used, the adapted config will be checked for validity.
If the config is invalid, an error will be printed to stderr and a non-
zero exit status will be returned.`,
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("adapt", flag.ExitOnError)
fs.String("config", "", "Configuration file to adapt (required)")
fs.String("adapter", "caddyfile", "Name of config adapter")
fs.Bool("pretty", false, "Format the output for human readability")
fs.Bool("validate", false, "Validate the output")
return fs
}(),
zero exit status will be returned.
`,
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().StringP("config", "c", "", "Configuration file to adapt (required)")
cmd.Flags().StringP("adapter", "a", "caddyfile", "Name of config adapter")
cmd.Flags().BoolP("pretty", "p", false, "Format the output for human readability")
cmd.Flags().BoolP("validate", "", false, "Validate the output")
cmd.RunE = WrapCommandFuncForCobra(cmdAdaptConfig)
},
})
RegisterCommand(Command{
Name: "validate",
Func: cmdValidateConfig,
Usage: "--config <path> [--adapter <name>]",
Usage: "--config <path> [--adapter <name>] [--envfile <path>]",
Short: "Tests whether a configuration file is valid",
Long: `
Loads and provisions the provided config, but does not start running it.
This reveals any errors with the configuration through the loading and
provisioning stages.`,
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("validate", flag.ExitOnError)
fs.String("config", "", "Input configuration file")
fs.String("adapter", "", "Name of config adapter")
return fs
}(),
provisioning stages.
If --envfile is specified, an environment file with environment variables in
the KEY=VALUE format will be loaded into the Caddy process.
`,
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().StringP("config", "c", "", "Input configuration file")
cmd.Flags().StringP("adapter", "a", "", "Name of config adapter")
cmd.Flags().StringP("envfile", "", "", "Environment file to load")
cmd.RunE = WrapCommandFuncForCobra(cmdValidateConfig)
},
})
RegisterCommand(Command{
Name: "fmt",
Func: cmdFmt,
Usage: "[--overwrite] [<path>]",
Usage: "[--overwrite] [--diff] [<path>]",
Short: "Formats a Caddyfile",
Long: `
Formats the Caddyfile by adding proper indentation and spaces to improve
@@ -320,32 +323,30 @@ is not a valid patch format.
If you wish you use stdin instead of a regular file, use - as the path.
When reading from stdin, the --overwrite flag has no effect: the result
is always printed to stdout.`,
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("fmt", flag.ExitOnError)
fs.Bool("overwrite", false, "Overwrite the input file with the results")
fs.Bool("diff", false, "Print the differences between the input file and the formatted output")
return fs
}(),
is always printed to stdout.
`,
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().BoolP("overwrite", "w", false, "Overwrite the input file with the results")
cmd.Flags().BoolP("diff", "d", false, "Print the differences between the input file and the formatted output")
cmd.RunE = WrapCommandFuncForCobra(cmdFmt)
},
})
RegisterCommand(Command{
Name: "upgrade",
Func: cmdUpgrade,
Short: "Upgrade Caddy (EXPERIMENTAL)",
Long: `
Downloads an updated Caddy binary with the same modules/plugins at the
latest versions. EXPERIMENTAL: May be changed or removed.`,
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("upgrade", flag.ExitOnError)
fs.Bool("keep-backup", false, "Keep the backed up binary, instead of deleting it")
return fs
}(),
latest versions. EXPERIMENTAL: May be changed or removed.
`,
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().BoolP("keep-backup", "k", false, "Keep the backed up binary, instead of deleting it")
cmd.RunE = WrapCommandFuncForCobra(cmdUpgrade)
},
})
RegisterCommand(Command{
Name: "add-package",
Func: cmdAddPackage,
Usage: "<packages...>",
Short: "Adds Caddy packages (EXPERIMENTAL)",
Long: `
@@ -353,11 +354,10 @@ Downloads an updated Caddy binary with the specified packages (module/plugin)
added. Retains existing packages. Returns an error if the any of packages are
already included. EXPERIMENTAL: May be changed or removed.
`,
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("add-package", flag.ExitOnError)
fs.Bool("keep-backup", false, "Keep the backed up binary, instead of deleting it")
return fs
}(),
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().BoolP("keep-backup", "k", false, "Keep the backed up binary, instead of deleting it")
cmd.RunE = WrapCommandFuncForCobra(cmdAddPackage)
},
})
RegisterCommand(Command{
@@ -370,31 +370,14 @@ Downloads an updated Caddy binaries without the specified packages (module/plugi
Returns an error if any of the packages are not included.
EXPERIMENTAL: May be changed or removed.
`,
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("remove-package", flag.ExitOnError)
fs.Bool("keep-backup", false, "Keep the backed up binary, instead of deleting it")
return fs
}(),
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().BoolP("keep-backup", "k", false, "Keep the backed up binary, instead of deleting it")
cmd.RunE = WrapCommandFuncForCobra(cmdRemovePackage)
},
})
RegisterCommand(Command{
Name: "manpage",
Func: func(fl Flags) (int, error) {
dir := strings.TrimSpace(fl.String("directory"))
if dir == "" {
return caddy.ExitCodeFailedQuit, fmt.Errorf("designated output directory and specified section are required")
}
if err := os.MkdirAll(dir, 0755); err != nil {
return caddy.ExitCodeFailedQuit, err
}
if err := doc.GenManTree(rootCmd, &doc.GenManHeader{
Title: "Caddy",
Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections
}, dir); err != nil {
return caddy.ExitCodeFailedQuit, err
}
return caddy.ExitCodeSuccess, nil
},
Name: "manpage",
Usage: "--directory <path>",
Short: "Generates the manual pages for Caddy commands",
Long: `
@@ -404,11 +387,25 @@ tagged into section 8 (System Administration).
The manual page files are generated into the directory specified by the
argument of --directory. If the directory does not exist, it will be created.
`,
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("manpage", flag.ExitOnError)
fs.String("directory", "", "The output directory where the manpages are generated")
return fs
}(),
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().StringP("directory", "o", "", "The output directory where the manpages are generated")
cmd.RunE = WrapCommandFuncForCobra(func(fl Flags) (int, error) {
dir := strings.TrimSpace(fl.String("directory"))
if dir == "" {
return caddy.ExitCodeFailedQuit, fmt.Errorf("designated output directory and specified section are required")
}
if err := os.MkdirAll(dir, 0755); err != nil {
return caddy.ExitCodeFailedQuit, err
}
if err := doc.GenManTree(rootCmd, &doc.GenManHeader{
Title: "Caddy",
Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections
}, dir); err != nil {
return caddy.ExitCodeFailedQuit, err
}
return caddy.ExitCodeSuccess, nil
})
},
})
// source: https://github.com/spf13/cobra/blob/main/shell_completions.md
@@ -456,7 +453,7 @@ argument of --directory. If the directory does not exist, it will be created.
`, rootCmd.Root().Name()),
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.ExactValidArgs(1),
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
RunE: func(cmd *cobra.Command, args []string) error {
switch args[0] {
case "bash":
@@ -492,7 +489,7 @@ func RegisterCommand(cmd Command) {
if cmd.Name == "" {
panic("command name is required")
}
if cmd.Func == nil {
if cmd.Func == nil && cmd.CobraFunc == nil {
panic("command function missing")
}
if cmd.Short == "" {
@@ -504,7 +501,7 @@ func RegisterCommand(cmd Command) {
if !commandNameRegex.MatchString(cmd.Name) {
panic("invalid command name")
}
rootCmd.AddCommand(caddyCmdToCoral(cmd))
rootCmd.AddCommand(caddyCmdToCobra(cmd))
}
var commandNameRegex = regexp.MustCompile(`^[a-z0-9]$|^([a-z0-9]+-?[a-z0-9]*)+[a-z0-9]$`)
+7 -6
View File
@@ -374,18 +374,19 @@ func parseEnvFile(envInput io.Reader) (map[string]string, error) {
}
// quoted value: support newlines
if strings.HasPrefix(val, `"`) {
for !(strings.HasSuffix(line, `"`) && !strings.HasSuffix(line, `\"`)) {
val = strings.ReplaceAll(val, `\"`, `"`)
if strings.HasPrefix(val, `"`) || strings.HasPrefix(val, "'") {
quote := string(val[0])
for !(strings.HasSuffix(line, quote) && !strings.HasSuffix(line, `\`+quote)) {
val = strings.ReplaceAll(val, `\`+quote, quote)
if !scanner.Scan() {
break
}
lineNumber++
line = strings.ReplaceAll(scanner.Text(), `\"`, `"`)
line = strings.ReplaceAll(scanner.Text(), `\`+quote, quote)
val += "\n" + line
}
val = strings.TrimPrefix(val, `"`)
val = strings.TrimSuffix(val, `"`)
val = strings.TrimPrefix(val, quote)
val = strings.TrimSuffix(val, quote)
}
envMap[key] = val
+13 -8
View File
@@ -326,7 +326,7 @@ func (ctx Context) LoadModuleByID(id string, rawMsg json.RawMessage) (any, error
// fill in its config only if there is a config to fill in
if len(rawMsg) > 0 {
err := strictUnmarshalJSON(rawMsg, &val)
err := StrictUnmarshalJSON(rawMsg, &val)
if err != nil {
return nil, fmt.Errorf("decoding module config: %s: %v", modInfo, err)
}
@@ -426,15 +426,20 @@ func (ctx Context) App(name string) (any, error) {
return modVal, nil
}
// AppIsConfigured returns whether an app named name has been
// configured. Can be called before calling App() to avoid
// AppIfConfigured returns an app by its name if it has been
// configured. Can be called instead of App() to avoid
// instantiating an empty app when that's not desirable.
func (ctx Context) AppIsConfigured(name string) bool {
if _, ok := ctx.cfg.apps[name]; ok {
return true
func (ctx Context) AppIfConfigured(name string) (any, error) {
app, ok := ctx.cfg.apps[name]
if !ok || app == nil {
return nil, nil
}
appRaw := ctx.cfg.AppsRaw[name]
return appRaw != nil
appModule, err := ctx.App(name)
if err != nil {
return nil, err
}
return appModule, nil
}
// Storage returns the configured Caddy storage implementation.
+87 -79
View File
@@ -1,94 +1,106 @@
module github.com/caddyserver/caddy/v2
go 1.18
go 1.19
require (
github.com/BurntSushi/toml v1.2.0
github.com/Masterminds/sprig/v3 v3.2.2
github.com/alecthomas/chroma v0.10.0
github.com/BurntSushi/toml v1.2.1
github.com/Masterminds/sprig/v3 v3.2.3
github.com/alecthomas/chroma/v2 v2.5.0
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b
github.com/caddyserver/certmagic v0.17.2
github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac
github.com/dustin/go-humanize v1.0.1
github.com/go-chi/chi v4.1.2+incompatible
github.com/google/cel-go v0.12.5
github.com/google/cel-go v0.13.0
github.com/google/uuid v1.3.0
github.com/klauspost/compress v1.15.11
github.com/klauspost/cpuid/v2 v2.1.1
github.com/lucas-clemente/quic-go v0.29.2
github.com/mholt/acmez v1.0.4
github.com/prometheus/client_golang v1.12.2
github.com/smallstep/certificates v0.22.1
github.com/smallstep/cli v0.22.0
github.com/smallstep/nosql v0.4.0
github.com/smallstep/truststore v0.12.0
github.com/spf13/cobra v1.5.0
github.com/klauspost/compress v1.16.0
github.com/klauspost/cpuid/v2 v2.2.4
github.com/mastercactapus/proxyprotocol v0.0.4
github.com/mholt/acmez v1.1.0
github.com/prometheus/client_golang v1.14.0
github.com/quic-go/quic-go v0.33.0
github.com/smallstep/certificates v0.23.2
github.com/smallstep/nosql v0.6.0
github.com/smallstep/truststore v0.12.1
github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5
github.com/tailscale/tscert v0.0.0-20220316030059-54bbcb9f74e2
github.com/yuin/goldmark v1.5.2
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.34.0
go.opentelemetry.io/otel v1.9.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.0
go.opentelemetry.io/otel/sdk v1.4.0
go.uber.org/zap v1.23.0
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
golang.org/x/net v0.0.0-20220812165438-1d4ff48094d1
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad
gopkg.in/natefinch/lumberjack.v2 v2.0.0
github.com/tailscale/tscert v0.0.0-20230124224810-c6dc1f4049b2
github.com/yuin/goldmark v1.5.4
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20220924101305-151362477c87
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0
go.opentelemetry.io/contrib/propagators/autoprop v0.40.0
go.opentelemetry.io/otel v1.14.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0
go.opentelemetry.io/otel/sdk v1.14.0
go.uber.org/zap v1.24.0
golang.org/x/crypto v0.7.0
golang.org/x/net v0.8.0
golang.org/x/sync v0.1.0
golang.org/x/term v0.6.0
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
github.com/golang/glog v1.0.0 // indirect
github.com/golang/mock v1.6.0 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-19 v0.2.1 // indirect
github.com/quic-go/qtls-go1-20 v0.1.1 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.opentelemetry.io/contrib/propagators/aws v1.15.0 // indirect
go.opentelemetry.io/contrib/propagators/b3 v1.15.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.15.0 // indirect
go.opentelemetry.io/contrib/propagators/ot v1.15.0 // indirect
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
)
require (
filippo.io/edwards25519 v1.0.0-rc.1 // indirect
filippo.io/edwards25519 v1.0.0 // indirect
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.1.1 // indirect
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220418222510-f25a4f6275ed // indirect
github.com/Masterminds/semver/v3 v3.2.0 // indirect
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/dgraph-io/badger v1.6.2 // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd // 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.4.0 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/go-kit/kit v0.10.0 // indirect
github.com/go-logfmt/logfmt v0.5.0 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.10.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.2.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.9.0 // indirect
github.com/jackc/pgx/v4 v4.14.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/libdns/libdns v0.2.1 // indirect
github.com/manifoldco/promptui v0.9.0 // indirect
github.com/marten-seemann/qpack v0.2.1 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect
github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
@@ -98,41 +110,37 @@ require (
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/nxadm/tail v1.4.8 // indirect
github.com/onsi/ginkgo v1.16.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/rs/xid v1.2.1 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/rs/xid v1.4.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/sirupsen/logrus v1.8.1 // indirect
github.com/slackhq/nebula v1.5.2 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/slackhq/nebula v1.6.1 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
github.com/urfave/cli v1.22.5 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
github.com/urfave/cli v1.22.12 // indirect
go.etcd.io/bbolt v1.3.7 // indirect
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.0 // indirect
go.opentelemetry.io/otel/metric v0.31.0 // indirect
go.opentelemetry.io/otel/trace v1.9.0 // indirect
go.opentelemetry.io/proto/otlp v0.12.0 // indirect
go.step.sm/cli-utils v0.7.4 // indirect
go.step.sm/crypto v0.18.0 // indirect
go.step.sm/linkedca v0.18.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect
go.opentelemetry.io/otel/metric v0.37.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.step.sm/cli-utils v0.7.5 // indirect
go.step.sm/crypto v0.23.2
go.step.sm/linkedca v0.19.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b // indirect
golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
google.golang.org/grpc v1.47.0 // indirect
google.golang.org/protobuf v1.28.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/sys v0.6.0
golang.org/x/text v0.8.0 // indirect
golang.org/x/tools v0.6.0 // indirect
google.golang.org/grpc v1.53.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
howett.net/plist v1.0.0 // indirect
)
+229 -201
View File
@@ -13,18 +13,19 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y=
cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute v1.6.1 h1:2sMmt8prCn7DPaG4Pmh0N3Inmc8cT8ae5k1M6VJ9Wqc=
cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/iam v0.1.0 h1:W2vbGCrE3Z7J/x3WXLxxGl9LMSB2uhsAA7Ss/6u/qRY=
cloud.google.com/go/kms v1.4.0 h1:iElbfoE61VeLhnZcGOltqL8HIly8Nhbe5t6JlH9GXjo=
cloud.google.com/go/iam v0.12.0 h1:DRtTY29b75ciH6Ov1PHb4/iat2CLCvrOm40Q0a6DFpE=
cloud.google.com/go/kms v1.9.0 h1:b0votJQa/9DSsxgHwN33/tTLA7ZHVzfWhDCrfiXijSo=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@@ -35,40 +36,47 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
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/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
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.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
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.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA=
github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
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/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/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
github.com/alecthomas/chroma/v2 v2.5.0 h1:CQCdj1BiBV17sD4Bd32b/Bzuiq/EqoNTrnIhyQAZ+Rk=
github.com/alecthomas/chroma/v2 v2.5.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw=
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/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/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220418222510-f25a4f6275ed h1:ue9pVfIcP+QMEjfgo/Ez4ZjNZfonGgR6NgjMaJMu1Cg=
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220418222510-f25a4f6275ed/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
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=
@@ -80,10 +88,9 @@ github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o
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.44.37 h1:KvDxCX6dfJeEDC77U5GPGSP0ErecmNnhDHFxw+NIvlI=
github.com/aws/aws-sdk-go v1.44.185 h1:stasiou+Ucx2A0RyXRyPph4sLCBxVQK7DPPK8tNcl5g=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
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=
@@ -94,14 +101,15 @@ github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAh
github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE=
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/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
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.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/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 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
@@ -113,9 +121,9 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
@@ -143,18 +151,19 @@ github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdw
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.0.4-0.20200906165740-41ebdbffecfd h1:KoJOtZf+6wpQaDTuOWGuo61GxcPBIfhwRxRTaTWGCTc=
github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd/go.mod h1:YylP9MpCYGVZQrly/j/diqcdUetCRRePeBB0c2VGXsA=
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/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 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
github.com/dlclark/regexp2 v1.7.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-0.20200219035652-afde56e7acac h1:opbrjaN/L8gg6Xh5D04Tem+8xVcz6ajZlGCs49mQgyg=
github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac/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=
@@ -164,9 +173,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
@@ -174,9 +182,8 @@ github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw
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/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
@@ -189,19 +196,20 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
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-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
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 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
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.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/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=
@@ -214,6 +222,8 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
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.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
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-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -251,8 +261,8 @@ 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/cel-go v0.12.5 h1:DmzaiSgoaqGCjtpPQWl26/gND+yRpim56H1jCVev6d8=
github.com/google/cel-go v0.12.5/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw=
github.com/google/cel-go v0.13.0 h1:z+8OBOcmh7IeKyqwT/6IlnMvy621fYUqnTVPEdegGlU=
github.com/google/cel-go v0.13.0/go.mod h1:K2hpQgEjDp18J76a2DKFRlPBPpgRZgi6EbnpDgIhJ8s=
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=
@@ -264,8 +274,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
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/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -276,16 +285,18 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
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/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa h1:7MYGT2XEMam7Mtzv1yDUYXANedWvwk3HKkR3MyGowy8=
github.com/googleapis/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg=
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/googleapis/gax-go/v2 v2.4.0 h1:dS9eYAjhrE2RjmzYw2XAPvcXfmcQLtFEQWn0CR82awk=
github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ=
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=
@@ -297,8 +308,9 @@ github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd
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/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
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=
@@ -320,18 +332,21 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
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 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/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=
@@ -343,8 +358,8 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU
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.10.1 h1:DzdIHIjG1AxGwoEEqS+mGsURyjt4enSmqzACXvVzOT8=
github.com/jackc/pgconn v1.10.1/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=
@@ -360,26 +375,27 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW
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.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns=
github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
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.9.0 h1:/SH1RxEtltvJgsDqp3TbiTFApD3mey3iygpuEGeuBXk=
github.com/jackc/pgtype v1.9.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
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.14.0 h1:TgdrmgnM7VY72EuSQzBbBd4JA1RLqJolrw9nQVZABVc=
github.com/jackc/pgx/v4 v4.14.0/go.mod h1:jT3ibf/A0ZVCp89rtCIN0zCJxcE74ypROmHEZYsG/j8=
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.2.0/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/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
@@ -399,17 +415,17 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
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/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0=
github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
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/konsorten/go-windows-terminal-sequences v1.0.3/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.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
@@ -423,18 +439,12 @@ 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/lucas-clemente/quic-go v0.29.2 h1:O8Mt0O6LpvEW+wfC40vZdcw0DngwYzoxq5xULZNzSI8=
github.com/lucas-clemente/quic-go v0.29.2/go.mod h1:g6/h9YMmLuU54tL1gW25uIi3VlBp3uv+sBihplIuskE=
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/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-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI=
github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE=
github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
github.com/mastercactapus/proxyprotocol v0.0.4 h1:qSY75IZF30ZqIU9iW1ip3I7gTnm8wRAnGWqPxCBVgq0=
github.com/mastercactapus/proxyprotocol v0.0.4/go.mod h1:X8FRVEDZz9FkrIoL4QYTBF4Ka4ELwTv0sah0/5NxCPw=
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.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@@ -455,8 +465,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
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.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80=
github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY=
github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY=
github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs=
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=
@@ -492,22 +502,15 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE
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/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
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.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
github.com/onsi/gomega v1.4.3/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/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
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=
@@ -538,37 +541,51 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34=
github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
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/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
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.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
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.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
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/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A=
github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk=
github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0=
github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA=
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/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
github.com/rs/xid v1.4.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=
@@ -588,21 +605,19 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
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/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/slackhq/nebula v1.5.2 h1:wuIOHsOnrNw3rQx8yPxXiGu8wAtAxxtUI/K8W7Vj7EI=
github.com/slackhq/nebula v1.5.2/go.mod h1:xaCM6wqbFk/NRmmUe1bv88fWBm3a1UioXJVIpR52WlE=
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5/go.mod h1:TC9A4+RjIOS+HyTH7wG17/gSqVv95uDw2J64dQZx7RE=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/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.22.1 h1:oAwb9tj+M3sKnlKLqPpJUOsn1HbxKOJ4MVCuvDHHjy8=
github.com/smallstep/certificates v0.22.1/go.mod h1:3R1PxdLOEqbNaWp88WiUByQAHGL+b9NefaQ5q6oTIZo=
github.com/smallstep/cli v0.22.0 h1:Mbb2CkunxAVig7Cr1NymS2NhjeOvkZqLPsfe9ZKMEfk=
github.com/smallstep/cli v0.22.0/go.mod h1:147IhymdB7JJYz1vN7XP2JS1YE+7soYX1yB/1gpZWh8=
github.com/smallstep/nosql v0.4.0 h1:Go3WYwttUuvwqMtFiiU4g7kBIlY+hR0bIZAqVdakQ3M=
github.com/smallstep/nosql v0.4.0/go.mod h1:yKZT5h7cdIVm6wEKM9+jN5dgK80Hljpuy8HNsnI7Gzo=
github.com/smallstep/truststore v0.12.0 h1:973Aa6fA7Ob/GCxqziosDzkQq6tV0Le6IUe4sikyW+U=
github.com/smallstep/truststore v0.12.0/go.mod h1:HwHKRcBi0RUxxw1LYDpTRhYC4jZUuxPpkHdVonlkoDM=
github.com/smallstep/certificates v0.23.2 h1:7KSx9WfZ3CILV0XlsTrl+PK58YE4CHSgqobB6+ieQWs=
github.com/smallstep/certificates v0.23.2/go.mod h1:YuQAlNuYrRA5z+meSH9D0XsUFH45TyAhNgyV5kYuQpQ=
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 v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@@ -617,8 +632,8 @@ 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.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
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=
@@ -633,39 +648,44 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J
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/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
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 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/tailscale/tscert v0.0.0-20220316030059-54bbcb9f74e2 h1:xwMw7LFhV9dbvot9A7NLClP9udqbjrQlIwWMH8e7uiQ=
github.com/tailscale/tscert v0.0.0-20220316030059-54bbcb9f74e2/go.mod h1:hL4gB6APAasMR2NNi/JHzqKkxW3EPQlFgLEq9PMi2t0=
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.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/tailscale/tscert v0.0.0-20230124224810-c6dc1f4049b2 h1:TrgfmCXwtWyFw85UkRGXt9qZRzdzt3nWt2Rerdecn0w=
github.com/tailscale/tscert v0.0.0-20230124224810-c6dc1f4049b2/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/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8=
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
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.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.5/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU=
github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 h1:yHfZyN55+5dp1wG7wDKv8HQ044moxkyGq12KFFMFDxg=
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594/go.mod h1:U9ihbh+1ZN7fR5Se3daSPoz1CGF9IYtSvWwVQtnzGHU=
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.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20220924101305-151362477c87 h1:Py16JEzkSdKAtEFJjiaYLYBOWGXc1r/xHj/Q/5lA37k=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20220924101305-151362477c87/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
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.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
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=
@@ -677,57 +697,61 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.34.0 h1:9NkMW03wwEzPtP/KciZ4Ozu/Uz5ZA7kfqXJIObnrjGU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.34.0/go.mod h1:548ZsYzmT4PL4zWKRd8q/N4z0Wxzn/ZxUE+lkEpwWQA=
go.opentelemetry.io/otel v1.4.0/go.mod h1:jeAqMFKy2uLIxCtKxoFj0FAL5zAPKQagc3+GtBWakzk=
go.opentelemetry.io/otel v1.9.0 h1:8WZNQFIB2a71LnANS9JeyidJKKGOOremcUtb/OtHISw=
go.opentelemetry.io/otel v1.9.0/go.mod h1:np4EoPGzoPs3O67xUVNoPPcmSvsfOxNlNA4F4AC+0Eo=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.0 h1:j7AwzDdAQBJjcqayAaYbvpYeZzII7cEe5qJTu+De6UY=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.0 h1:lRpP10E8oTGVmY1nVXcwelCT1Z8ca41/l5ce7AqLAss=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.0/go.mod h1:3oS+j2WUoJVyj6/BzQN/52G17lNJDulngsOxDm1w2PY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.0 h1:buSx4AMC/0Z232slPhicN/fU5KIlj0bMngct5pcZhkI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.0/go.mod h1:ew1NcwkHo0QFT3uTm3m2IVZMkZdVIpbOYNPasgWwpdk=
go.opentelemetry.io/otel/metric v0.31.0 h1:6SiklT+gfWAwWUR0meEMxQBtihpiEs4c+vL9spDTqUs=
go.opentelemetry.io/otel/metric v0.31.0/go.mod h1:ohmwj9KTSIeBnDBm/ZwH2PSZxZzoOaG2xZeekTRzL5A=
go.opentelemetry.io/otel/sdk v1.4.0 h1:LJE4SW3jd4lQTESnlpQZcBhQ3oci0U2MLR5uhicfTHQ=
go.opentelemetry.io/otel/sdk v1.4.0/go.mod h1:71GJPNJh4Qju6zJuYl1CrYtXbrgfau/M9UAggqiy1UE=
go.opentelemetry.io/otel/trace v1.4.0/go.mod h1:uc3eRsqDfWs9R7b92xbQbU42/eTNz4N+gLP8qJCi4aE=
go.opentelemetry.io/otel/trace v1.9.0 h1:oZaCNJUjWcg60VXWee8lJKlqhPbXAPB51URuR47pQYc=
go.opentelemetry.io/otel/trace v1.9.0/go.mod h1:2737Q0MuG8q1uILYm2YYVkAyLtOofiTNGg6VODnOiPo=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 h1:lE9EJyw3/JhrjWH/hEy9FptnalDQgj7vpbgC2KCCCxE=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0/go.mod h1:pcQ3MM3SWvrA71U4GDqv9UFDJ3HQsW7y5ZO3tDTlUdI=
go.opentelemetry.io/contrib/propagators/autoprop v0.40.0 h1:Lj33jj7eIrBfIShiK8NU91u2BglKnUS1UUxVemuQJtw=
go.opentelemetry.io/contrib/propagators/autoprop v0.40.0/go.mod h1:6QO816FeZ+6zahs6hYqbUCCsnNBm7o+t4iwVySpzcdI=
go.opentelemetry.io/contrib/propagators/aws v1.15.0 h1:FLe+bRTMAhEALItDQt1U2S/rdq8/rGGJTJpOpCDvMu0=
go.opentelemetry.io/contrib/propagators/aws v1.15.0/go.mod h1:Z/nqdjqKjErrS3gYoEMZt8//dt8VZbqalD0V+7vh7lM=
go.opentelemetry.io/contrib/propagators/b3 v1.15.0 h1:bMaonPyFcAvZ4EVzkUNkfnUHP5Zi63CIDlA3dRsEg8Q=
go.opentelemetry.io/contrib/propagators/b3 v1.15.0/go.mod h1:VjU0g2v6HSQ+NwfifambSLAeBgevjIcqmceaKWEzl0c=
go.opentelemetry.io/contrib/propagators/jaeger v1.15.0 h1:xdJjwy5t/8I+TZehMMQ+r2h50HREihH2oMUhimQ+jug=
go.opentelemetry.io/contrib/propagators/jaeger v1.15.0/go.mod h1:tU0nwW4QTvKceNUP60/PQm0FI8zDSwey7gIFt3RR/yw=
go.opentelemetry.io/contrib/propagators/ot v1.15.0 h1:iBNejawWy7wWZ5msuZDNcMjBy14Wc0v3gCAXukGHN/Q=
go.opentelemetry.io/contrib/propagators/ot v1.15.0/go.mod h1:0P7QQ+MHt6SXR1ATaMpewSiWlp8NbKErNLKcaU4EEKI=
go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 h1:/fXHZHGvro6MVqV34fJzDhi7sHGpX3Ej/Qjmfn003ho=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0/go.mod h1:UFG7EBMRdXyFstOwH028U0sVf+AvukSGhF0g8+dmNG8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 h1:TKf2uAs2ueguzLaxOCBXNpHxfO/aC7PAdDsSH0IbeRQ=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0/go.mod h1:HrbCVv40OOLTABmOn1ZWty6CHXkU8DK/Urc43tHug70=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 h1:ap+y8RXX3Mu9apKVtOkM6WSFESLM8K3wNQyOU8sWHcc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0/go.mod h1:5w41DY6S9gZrbjuq6Y+753e96WfPha5IcsOSZTtullM=
go.opentelemetry.io/otel/metric v0.37.0 h1:pHDQuLQOZwYD+Km0eb657A25NaRzy0a+eLyKfDXedEs=
go.opentelemetry.io/otel/metric v0.37.0/go.mod h1:DmdaHfGt54iV6UKxsV9slj2bBRJcKC1B1uvDLIioc1s=
go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY=
go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM=
go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.12.0 h1:CMJ/3Wp7iOWES+CYLfnBv+DVmPbB+kmy9PJ92XvlR6c=
go.opentelemetry.io/proto/otlp v0.12.0/go.mod h1:TsIjwGWIx5VFYv9KGVlOpxoBl5Dy+63SUguV7GGvlSQ=
go.step.sm/cli-utils v0.7.4 h1:oI7PStZqlvjPZ0u2EB4lN7yZ4R3ShTotdGL/L84Oorg=
go.step.sm/cli-utils v0.7.4/go.mod h1:taSsY8haLmXoXM3ZkywIyRmVij/4Aj0fQbNTlJvv71I=
go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.step.sm/cli-utils v0.7.5 h1:jyp6X8k8mN1B0uWJydTid0C++8tQhm2kaaAdXKQQzdk=
go.step.sm/cli-utils v0.7.5/go.mod h1:taSsY8haLmXoXM3ZkywIyRmVij/4Aj0fQbNTlJvv71I=
go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0=
go.step.sm/crypto v0.18.0 h1:saD/tMG7uKJmUIPyOyudidVTHPnozTU02CDd+oqwKn0=
go.step.sm/crypto v0.18.0/go.mod h1:qZ+pNU1nV+THwP7TPTNCRMRr9xrRURhETTAK7U5psfw=
go.step.sm/linkedca v0.18.0 h1:uxRBd2WDvJNZ2i0nJm/QmG4lkRxWoebYKJinchX7T7o=
go.step.sm/linkedca v0.18.0/go.mod h1:qSuYlIIhvPmA2+DSSS03E2IXhbXWTLW61Xh9zDQJ3VM=
go.step.sm/crypto v0.23.2 h1:XGmQH9Pkpxop47cjYlUhF10L5roPCbu1BCZXopbeW8I=
go.step.sm/crypto v0.23.2/go.mod h1:/IXGz8al8k7u7OV0RTWIi8TRVqO2FMyZVpedV+6Da6U=
go.step.sm/linkedca v0.19.0 h1:xuagkR35wrJI2gnu6FAM+q3VmjwsHScvGcJsfZ0GdsI=
go.step.sm/linkedca v0.19.0/go.mod h1:b7vWPrHfYLEOTSUZitFEcztVCpTc+ileIN85CwEAluM=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
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/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
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.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
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.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
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=
@@ -744,8 +768,11 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh
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-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
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.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -756,8 +783,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -779,8 +806,9 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.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=
@@ -812,26 +840,30 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220812165438-1d4ff48094d1 h1:mx1QvUwXKGgh+3SB51PH4G1TouzL84rLG0CtpdX+TTg=
golang.org/x/net v0.0.0-20220812165438-1d4ff48094d1/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
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.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/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/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb h1:8tDJ3aechhddbdPAxpycgXHJRMLpk/Ab+aa4OgdN5/g=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M=
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=
@@ -840,10 +872,11 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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=
@@ -868,11 +901,8 @@ golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7w
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-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -889,34 +919,37 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/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-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/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-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
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.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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=
@@ -926,8 +959,10 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/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.3.8-0.20211004125949-5bd84dd9b33b h1:NXqSWXSRUSCaFuvitrWtU169I3876zRTalMRbfd6LL0=
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
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.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
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=
@@ -980,20 +1015,17 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
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=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0=
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
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.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@@ -1011,7 +1043,7 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.84.0 h1:NMB9J4cCxs9xEm+1Z9QiO3eFvn7EnQj3Eo3hN6ugVlg=
google.golang.org/api v0.108.0 h1:WVBc/faN0DkKtR43Q/7+tPny9ZoLZdIiAyG5Q9vFClg=
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.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1051,8 +1083,9 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad h1:kqrS+lhvaMHCxul6sKQvKJ8nAAhlVItmZV822hYFH/U=
google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA=
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
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.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
@@ -1072,11 +1105,10 @@ google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
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=
@@ -1090,25 +1122,24 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
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.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/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/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
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/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/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.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
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.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
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 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
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=
@@ -1118,12 +1149,10 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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.0-20210107192922-496545a6307b/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=
@@ -1134,7 +1163,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+1 -4
View File
@@ -12,10 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// TODO: Go 1.19 introduced the "unix" build tag. We have to support Go 1.18 until Go 1.20 is released.
// When Go 1.19 is our minimum, change this build tag to simply "!unix".
// (see similar change needed in listen_unix.go)
//go:build !(aix || android || darwin || dragonfly || freebsd || hurd || illumos || ios || linux || netbsd || openbsd || solaris)
//go:build !unix
package caddy
+33 -7
View File
@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// TODO: Go 1.19 introduced the "unix" build tag. We have to support Go 1.18 until Go 1.20 is released.
// When Go 1.19 is our minimum, remove this build tag, since "_unix" in the filename will do this.
// (see also change needed in listen.go)
//go:build aix || android || darwin || dragonfly || freebsd || hurd || illumos || ios || linux || netbsd || openbsd || solaris
// Even though the filename ends in _unix.go, we still have to specify the
// build constraint here, because the filename convention only works for
// literal GOOS values, and "unix" is a shortcut unique to build tags.
//go:build unix
package caddy
@@ -34,7 +34,7 @@ import (
// reuseUnixSocket copies and reuses the unix domain socket (UDS) if we already
// have it open; if not, unlink it so we can have it. No-op if not a unix network.
func reuseUnixSocket(network, addr string) (any, error) {
if !isUnixNetwork(network) {
if !IsUnixNetwork(network) {
return nil, nil
}
@@ -98,12 +98,24 @@ func listenTCPOrUnix(ctx context.Context, lnKey string, network, address string,
}
return reusePort(network, address, c)
}
return config.Listen(ctx, network, address)
// even though SO_REUSEPORT lets us bind the socket multiple times,
// we still put it in the listenerPool so we can count how many
// configs are using this socket; necessary to ensure we can know
// whether to enforce shutdown delays, for example (see #5393).
ln, err := config.Listen(ctx, network, address)
if err == nil {
listenerPool.LoadOrStore(lnKey, nil)
}
// lightly wrap the listener so that when it is closed,
// we can decrement the usage pool counter
return deleteListener{ln, lnKey}, err
}
// reusePort sets SO_REUSEPORT. Ineffective for unix sockets.
func reusePort(network, address string, conn syscall.RawConn) error {
if isUnixNetwork(network) {
if IsUnixNetwork(network) {
return nil
}
return conn.Control(func(descriptor uintptr) {
@@ -116,3 +128,17 @@ func reusePort(network, address string, conn syscall.RawConn) error {
}
})
}
// deleteListener is a type that simply deletes itself
// from the listenerPool when it closes. It is used
// solely for the purpose of reference counting (i.e.
// counting how many configs are using a given socket).
type deleteListener struct {
net.Listener
lnKey string
}
func (dl deleteListener) Close() error {
_, _ = listenerPool.Delete(dl.lnKey)
return dl.Listener.Close()
}
+11 -9
View File
@@ -30,8 +30,8 @@ import (
"syscall"
"time"
"github.com/lucas-clemente/quic-go"
"github.com/lucas-clemente/quic-go/http3"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/http3"
"go.uber.org/zap"
)
@@ -205,7 +205,7 @@ func (na NetworkAddress) listen(ctx context.Context, portOffset uint, config net
// IsUnixNetwork returns true if na.Network is
// unix, unixgram, or unixpacket.
func (na NetworkAddress) IsUnixNetwork() bool {
return isUnixNetwork(na.Network)
return IsUnixNetwork(na.Network)
}
// JoinHostPort is like net.JoinHostPort, but where the port
@@ -289,8 +289,9 @@ func (na NetworkAddress) String() string {
return JoinNetworkAddress(na.Network, na.Host, na.port())
}
func isUnixNetwork(netw string) bool {
return netw == "unix" || netw == "unixgram" || netw == "unixpacket"
// IsUnixNetwork returns true if the netw is a unix network.
func IsUnixNetwork(netw string) bool {
return strings.HasPrefix(netw, "unix")
}
// ParseNetworkAddress parses addr into its individual
@@ -310,7 +311,7 @@ func ParseNetworkAddress(addr string) (NetworkAddress, error) {
if network == "" {
network = "tcp"
}
if isUnixNetwork(network) {
if IsUnixNetwork(network) {
return NetworkAddress{
Network: network,
Host: host,
@@ -353,7 +354,7 @@ func SplitNetworkAddress(a string) (network, host, port string, err error) {
network = strings.ToLower(strings.TrimSpace(beforeSlash))
a = afterSlash
}
if isUnixNetwork(network) {
if IsUnixNetwork(network) {
host = a
return
}
@@ -384,7 +385,7 @@ func JoinNetworkAddress(network, host, port string) string {
if network != "" {
a = network + "/"
}
if (host != "" && port == "") || isUnixNetwork(network) {
if (host != "" && port == "") || IsUnixNetwork(network) {
a += host
} else if port != "" {
a += net.JoinHostPort(host, port)
@@ -440,6 +441,7 @@ func ListenQUIC(ln net.PacketConn, tlsConf *tls.Config, activeRequests *int64) (
sharedEarlyListener, _, err := listenerPool.LoadOrNew(lnKey, func() (Destructor, error) {
earlyLn, err := quic.ListenEarly(ln, http3.ConfigureTLSConfig(tlsConf), &quic.Config{
Allow0RTT: func(net.Addr) bool { return true },
RequireAddressValidation: func(clientAddr net.Addr) bool {
var highLoad bool
if activeRequests != nil {
@@ -462,7 +464,7 @@ func ListenQUIC(ln net.PacketConn, tlsConf *tls.Config, activeRequests *int64) (
// of closes) because closing the quic.EarlyListener doesn't actually close
// the underlying PacketConn, but we need to for unix sockets since we dup
// the file descriptor and thus need to close the original; track issue:
// https://github.com/lucas-clemente/quic-go/issues/3560#issuecomment-1258959608
// https://github.com/quic-go/quic-go/issues/3560#issuecomment-1258959608
var unix *unixConn
if uc, ok := ln.(*unixConn); ok {
unix = uc
+115 -125
View File
@@ -62,7 +62,7 @@ type Logging struct {
// in dependencies that are not designed specifically for use
// in Caddy. Because it is global and unstructured, the sink
// lacks most advanced features and customizations.
Sink *StandardLibLog `json:"sink,omitempty"`
Sink *SinkLog `json:"sink,omitempty"`
// Logs are your logs, keyed by an arbitrary name of your
// choosing. The default log can be customized by defining
@@ -259,55 +259,11 @@ func (wdest writerDestructor) Destruct() error {
return wdest.Close()
}
// StandardLibLog configures the default Go standard library
// global logger in the log package. This is necessary because
// module dependencies which are not built specifically for
// Caddy will use the standard logger. This is also known as
// the "sink" logger.
type StandardLibLog struct {
// BaseLog contains the common logging parameters for logging.
type BaseLog struct {
// The module that writes out log entries for the sink.
WriterRaw json.RawMessage `json:"writer,omitempty" caddy:"namespace=caddy.logging.writers inline_key=output"`
writer io.WriteCloser
}
func (sll *StandardLibLog) provision(ctx Context, logging *Logging) error {
if sll.WriterRaw != nil {
mod, err := ctx.LoadModule(sll, "WriterRaw")
if err != nil {
return fmt.Errorf("loading sink log writer module: %v", err)
}
wo := mod.(WriterOpener)
var isNew bool
sll.writer, isNew, err = logging.openWriter(wo)
if err != nil {
return fmt.Errorf("opening sink log writer %#v: %v", mod, err)
}
if isNew {
log.Printf("[INFO] Redirecting sink to: %s", wo)
log.SetOutput(sll.writer)
log.Printf("[INFO] Redirected sink to here (%s)", wo)
}
}
return nil
}
// CustomLog represents a custom logger configuration.
//
// By default, a log will emit all log entries. Some entries
// will be skipped if sampling is enabled. Further, the Include
// and Exclude parameters define which loggers (by name) are
// allowed or rejected from emitting in this log. If both Include
// and Exclude are populated, their values must be mutually
// exclusive, and longer namespaces have priority. If neither
// are populated, all logs are emitted.
type CustomLog struct {
// The writer defines where log entries are emitted.
WriterRaw json.RawMessage `json:"writer,omitempty" caddy:"namespace=caddy.logging.writers inline_key=output"`
// The encoder is how the log entries are formatted or encoded.
EncoderRaw json.RawMessage `json:"encoder,omitempty" caddy:"namespace=caddy.logging.encoders inline_key=format"`
@@ -321,16 +277,6 @@ type CustomLog struct {
// servers.
Sampling *LogSampling `json:"sampling,omitempty"`
// Include defines the names of loggers to emit in this
// log. For example, to include only logs emitted by the
// admin API, you would include "admin.api".
Include []string `json:"include,omitempty"`
// Exclude defines the names of loggers that should be
// skipped by this log. For example, to exclude only
// HTTP access logs, you would exclude "http.log.access".
Exclude []string `json:"exclude,omitempty"`
writerOpener WriterOpener
writer io.WriteCloser
encoder zapcore.Encoder
@@ -338,8 +284,23 @@ type CustomLog struct {
core zapcore.Core
}
func (cl *CustomLog) provision(ctx Context, logging *Logging) error {
// Replace placeholder for log level
func (cl *BaseLog) provisionCommon(ctx Context, logging *Logging) error {
if cl.WriterRaw != nil {
mod, err := ctx.LoadModule(cl, "WriterRaw")
if err != nil {
return fmt.Errorf("loading log writer module: %v", err)
}
cl.writerOpener = mod.(WriterOpener)
}
if cl.writerOpener == nil {
cl.writerOpener = StderrWriter{}
}
var err error
cl.writer, _, err = logging.openWriter(cl.writerOpener)
if err != nil {
return fmt.Errorf("opening log writer using %#v: %v", cl.writerOpener, err)
}
repl := NewReplacer()
level, err := repl.ReplaceOrErr(cl.Level, true, true)
if err != nil {
@@ -365,6 +326,101 @@ func (cl *CustomLog) provision(ctx Context, logging *Logging) error {
return fmt.Errorf("unrecognized log level: %s", cl.Level)
}
if cl.EncoderRaw != nil {
mod, err := ctx.LoadModule(cl, "EncoderRaw")
if err != nil {
return fmt.Errorf("loading log encoder module: %v", err)
}
cl.encoder = mod.(zapcore.Encoder)
}
if cl.encoder == nil {
// only allow colorized output if this log is going to stdout or stderr
var colorize bool
switch cl.writerOpener.(type) {
case StdoutWriter, StderrWriter,
*StdoutWriter, *StderrWriter:
colorize = true
}
cl.encoder = newDefaultProductionLogEncoder(colorize)
}
cl.buildCore()
return nil
}
func (cl *BaseLog) buildCore() {
// logs which only discard their output don't need
// to perform encoding or any other processing steps
// at all, so just shorcut to a nop core instead
if _, ok := cl.writerOpener.(*DiscardWriter); ok {
cl.core = zapcore.NewNopCore()
return
}
c := zapcore.NewCore(
cl.encoder,
zapcore.AddSync(cl.writer),
cl.levelEnabler,
)
if cl.Sampling != nil {
if cl.Sampling.Interval == 0 {
cl.Sampling.Interval = 1 * time.Second
}
if cl.Sampling.First == 0 {
cl.Sampling.First = 100
}
if cl.Sampling.Thereafter == 0 {
cl.Sampling.Thereafter = 100
}
c = zapcore.NewSamplerWithOptions(c, cl.Sampling.Interval,
cl.Sampling.First, cl.Sampling.Thereafter)
}
cl.core = c
}
// SinkLog configures the default Go standard library
// global logger in the log package. This is necessary because
// module dependencies which are not built specifically for
// Caddy will use the standard logger. This is also known as
// the "sink" logger.
type SinkLog struct {
BaseLog
}
func (sll *SinkLog) provision(ctx Context, logging *Logging) error {
if err := sll.provisionCommon(ctx, logging); err != nil {
return err
}
ctx.cleanupFuncs = append(ctx.cleanupFuncs, zap.RedirectStdLog(zap.New(sll.core)))
return nil
}
// CustomLog represents a custom logger configuration.
//
// By default, a log will emit all log entries. Some entries
// will be skipped if sampling is enabled. Further, the Include
// and Exclude parameters define which loggers (by name) are
// allowed or rejected from emitting in this log. If both Include
// and Exclude are populated, their values must be mutually
// exclusive, and longer namespaces have priority. If neither
// are populated, all logs are emitted.
type CustomLog struct {
BaseLog
// Include defines the names of loggers to emit in this
// log. For example, to include only logs emitted by the
// admin API, you would include "admin.api".
Include []string `json:"include,omitempty"`
// Exclude defines the names of loggers that should be
// skipped by this log. For example, to exclude only
// HTTP access logs, you would exclude "http.log.access".
Exclude []string `json:"exclude,omitempty"`
}
func (cl *CustomLog) provision(ctx Context, logging *Logging) error {
if err := cl.provisionCommon(ctx, logging); err != nil {
return err
}
// If both Include and Exclude lists are populated, then each item must
// be a superspace or subspace of an item in the other list, because
// populating both lists means that any given item is either a rule
@@ -394,75 +450,9 @@ func (cl *CustomLog) provision(ctx Context, logging *Logging) error {
return fmt.Errorf("when both include and exclude are populated, each element must be a superspace or subspace of one in the other list; check '%s' in include", allow)
}
}
if cl.WriterRaw != nil {
mod, err := ctx.LoadModule(cl, "WriterRaw")
if err != nil {
return fmt.Errorf("loading log writer module: %v", err)
}
cl.writerOpener = mod.(WriterOpener)
}
if cl.writerOpener == nil {
cl.writerOpener = StderrWriter{}
}
cl.writer, _, err = logging.openWriter(cl.writerOpener)
if err != nil {
return fmt.Errorf("opening log writer using %#v: %v", cl.writerOpener, err)
}
if cl.EncoderRaw != nil {
mod, err := ctx.LoadModule(cl, "EncoderRaw")
if err != nil {
return fmt.Errorf("loading log encoder module: %v", err)
}
cl.encoder = mod.(zapcore.Encoder)
}
if cl.encoder == nil {
// only allow colorized output if this log is going to stdout or stderr
var colorize bool
switch cl.writerOpener.(type) {
case StdoutWriter, StderrWriter,
*StdoutWriter, *StderrWriter:
colorize = true
}
cl.encoder = newDefaultProductionLogEncoder(colorize)
}
cl.buildCore()
return nil
}
func (cl *CustomLog) buildCore() {
// logs which only discard their output don't need
// to perform encoding or any other processing steps
// at all, so just shorcut to a nop core instead
if _, ok := cl.writerOpener.(*DiscardWriter); ok {
cl.core = zapcore.NewNopCore()
return
}
c := zapcore.NewCore(
cl.encoder,
zapcore.AddSync(cl.writer),
cl.levelEnabler,
)
if cl.Sampling != nil {
if cl.Sampling.Interval == 0 {
cl.Sampling.Interval = 1 * time.Second
}
if cl.Sampling.First == 0 {
cl.Sampling.First = 100
}
if cl.Sampling.Thereafter == 0 {
cl.Sampling.Thereafter = 100
}
c = zapcore.NewSamplerWithOptions(c, cl.Sampling.Interval,
cl.Sampling.First, cl.Sampling.Thereafter)
}
cl.core = c
}
func (cl *CustomLog) matchesModule(moduleID string) bool {
return cl.loggerAllowed(moduleID, true)
}
+2 -2
View File
@@ -333,11 +333,11 @@ func ParseStructTag(tag string) (map[string]string, error) {
return results, nil
}
// strictUnmarshalJSON is like json.Unmarshal but returns an error
// StrictUnmarshalJSON is like json.Unmarshal but returns an error
// if any of the fields are unrecognized. Useful when decoding
// module configurations, where you want to be more sure they're
// correct.
func strictUnmarshalJSON(data []byte, v any) error {
func StrictUnmarshalJSON(data []byte, v any) error {
dec := json.NewDecoder(bytes.NewReader(data))
dec.DisallowUnknownFields()
return dec.Decode(v)
+63 -16
View File
@@ -54,16 +54,17 @@ func init() {
// `{http.request.duration_ms}` | Same as 'duration', but in milliseconds.
// `{http.request.uuid}` | The request unique identifier
// `{http.request.header.*}` | Specific request header field
// `{http.request.host.labels.*}` | Request host labels (0-based from right); e.g. for foo.example.com: 0=com, 1=example, 2=foo
// `{http.request.host}` | The host part of the request's Host header
// `{http.request.host.labels.*}` | Request host labels (0-based from right); e.g. for foo.example.com: 0=com, 1=example, 2=foo
// `{http.request.hostport}` | The host and port from the request's Host header
// `{http.request.method}` | The request method
// `{http.request.orig_method}` | The request's original method
// `{http.request.orig_uri}` | The request's original URI
// `{http.request.orig_uri.path}` | The request's original path
// `{http.request.orig_uri.path.*}` | Parts of the original path, split by `/` (0-based from left)
// `{http.request.orig_uri.path.dir}` | The request's original directory
// `{http.request.orig_uri.path.file}` | The request's original filename
// `{http.request.orig_uri.path}` | The request's original path
// `{http.request.orig_uri.query}` | The request's original query string (without `?`)
// `{http.request.orig_uri}` | The request's original URI
// `{http.request.port}` | The port part of the request's Host header
// `{http.request.proto}` | The protocol of the request
// `{http.request.remote.host}` | The host (IP) part of the remote client's address
@@ -88,13 +89,13 @@ func init() {
// `{http.request.tls.client.san.emails.*}` | SAN email addresses (index optional)
// `{http.request.tls.client.san.ips.*}` | SAN IP addresses (index optional)
// `{http.request.tls.client.san.uris.*}` | SAN URIs (index optional)
// `{http.request.uri}` | The full request URI
// `{http.request.uri.path}` | The path component of the request URI
// `{http.request.uri.path.*}` | Parts of the path, split by `/` (0-based from left)
// `{http.request.uri.path.dir}` | The directory, excluding leaf filename
// `{http.request.uri.path.file}` | The filename of the path, excluding directory
// `{http.request.uri.path}` | The path component of the request URI
// `{http.request.uri.query.*}` | Individual query string value
// `{http.request.uri.query}` | The query string (without `?`)
// `{http.request.uri}` | The full request URI
// `{http.request.uri.query.*}` | Individual query string value
// `{http.response.header.*}` | Specific response header field
// `{http.vars.*}` | Custom variables in the HTTP handler chain
// `{http.shutting_down}` | True if the HTTP app is shutting down
@@ -222,6 +223,20 @@ func (app *App) Provision(ctx caddy.Context) error {
srv.StrictSNIHost = &trueBool
}
// set up the trusted proxies source
for srv.TrustedProxiesRaw != nil {
val, err := ctx.LoadModule(srv, "TrustedProxiesRaw")
if err != nil {
return fmt.Errorf("loading trusted proxies modules: %v", err)
}
srv.trustedProxies = val.(IPRangeSource)
}
// set the default client IP header to read from
if srv.ClientIPHeaders == nil {
srv.ClientIPHeaders = []string{"X-Forwarded-For"}
}
// process each listener address
for i := range srv.Listen {
lnOut, err := repl.ReplaceOrErr(srv.Listen[i], true, true)
@@ -342,6 +357,14 @@ func (app *App) Start() error {
MaxHeaderBytes: srv.MaxHeaderBytes,
Handler: srv,
ErrorLog: serverLogger,
ConnContext: func(ctx context.Context, c net.Conn) context.Context {
return context.WithValue(ctx, ConnCtxKey, c)
},
}
h2server := &http2.Server{
NewWriteScheduler: func() http2.WriteScheduler {
return http2.NewPriorityWriteScheduler(nil)
},
}
// disable HTTP/2, which we enabled by default during provisioning
@@ -363,6 +386,9 @@ func (app *App) Start() error {
}
}
}
} else {
//nolint:errcheck
http2.ConfigureServer(srv.server, h2server)
}
// this TLS config is used by the std lib to choose the actual TLS config for connections
@@ -372,9 +398,6 @@ func (app *App) Start() error {
// enable H2C if configured
if srv.protocol("h2c") {
h2server := &http2.Server{
IdleTimeout: time.Duration(srv.IdleTimeout),
}
srv.server.Handler = h2c.NewHandler(srv, h2server)
}
@@ -441,6 +464,17 @@ func (app *App) Start() error {
ln = srv.listenerWrappers[i].WrapListener(ln)
}
// handle http2 if use tls listener wrapper
if useTLS {
http2lnWrapper := &http2Listener{
Listener: ln,
server: srv.server,
h2server: h2server,
}
srv.h2listeners = append(srv.h2listeners, http2lnWrapper)
ln = http2lnWrapper
}
// if binding to port 0, the OS chooses a port for us;
// but the user won't know the port unless we print it
if !listenAddr.IsUnixNetwork() && listenAddr.StartPort == 0 && listenAddr.EndPort == 0 {
@@ -507,7 +541,7 @@ func (app *App) Stop() error {
// honor scheduled/delayed shutdown time
if delay {
app.logger.Debug("shutdown scheduled",
app.logger.Info("shutdown scheduled",
zap.Duration("delay_duration", time.Duration(app.ShutdownDelay)),
zap.Time("time", scheduledTime))
time.Sleep(time.Duration(app.ShutdownDelay))
@@ -518,9 +552,9 @@ func (app *App) Stop() error {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, time.Duration(app.GracePeriod))
defer cancel()
app.logger.Debug("servers shutting down; grace period initiated", zap.Duration("duration", time.Duration(app.GracePeriod)))
app.logger.Info("servers shutting down; grace period initiated", zap.Duration("duration", time.Duration(app.GracePeriod)))
} else {
app.logger.Debug("servers shutting down with eternal grace period")
app.logger.Info("servers shutting down with eternal grace period")
}
// goroutines aren't guaranteed to be scheduled right away,
@@ -554,7 +588,7 @@ func (app *App) Stop() error {
// TODO: we have to manually close our listeners because quic-go won't
// close listeners it didn't create along with the server itself...
// see https://github.com/lucas-clemente/quic-go/issues/3560
// see https://github.com/quic-go/quic-go/issues/3560
for _, el := range server.h3listeners {
if err := el.Close(); err != nil {
app.logger.Error("HTTP/3 listener close",
@@ -563,19 +597,32 @@ func (app *App) Stop() error {
}
}
// TODO: CloseGracefully, once implemented upstream (see https://github.com/lucas-clemente/quic-go/issues/2103)
// TODO: CloseGracefully, once implemented upstream (see https://github.com/quic-go/quic-go/issues/2103)
if err := server.h3server.Close(); err != nil {
app.logger.Error("HTTP/3 server shutdown",
zap.Error(err),
zap.Strings("addresses", server.Listen))
}
}
stopH2Listener := func(server *Server) {
defer finishedShutdown.Done()
startedShutdown.Done()
for i, s := range server.h2listeners {
if err := s.Shutdown(ctx); err != nil {
app.logger.Error("http2 listener shutdown",
zap.Error(err),
zap.Int("index", i))
}
}
}
for _, server := range app.Servers {
startedShutdown.Add(2)
finishedShutdown.Add(2)
startedShutdown.Add(3)
finishedShutdown.Add(3)
go stopServer(server)
go stopH3Server(server)
go stopH2Listener(server)
}
// block until all the goroutines have been run by the scheduler;
+3 -3
View File
@@ -285,7 +285,7 @@ uniqueDomainsLoop:
// one automation policy would be confusing and an error
if app.tlsApp.Automation != nil {
for _, ap := range app.tlsApp.Automation.Policies {
for _, apHost := range ap.Subjects {
for _, apHost := range ap.Subjects() {
if apHost == d {
continue uniqueDomainsLoop
}
@@ -518,7 +518,7 @@ func (app *App) createAutomationPolicies(ctx caddy.Context, internalNames []stri
}
// while we're here, is this the catch-all/base policy?
if !foundBasePolicy && len(ap.Subjects) == 0 {
if !foundBasePolicy && len(ap.SubjectsRaw) == 0 {
basePolicy = ap
foundBasePolicy = true
}
@@ -634,7 +634,7 @@ func (app *App) createAutomationPolicies(ctx caddy.Context, internalNames []stri
// rather they just want to change the CA for the set
// of names that would normally use the production API;
// anyway, that gets into the weeds a bit...
newPolicy.Subjects = internalNames
newPolicy.SubjectsRaw = internalNames
newPolicy.Issuers = []certmagic.Issuer{internalIssuer}
err := app.tlsApp.AddAutomationPolicy(newPolicy)
if err != nil {
+11 -2
View File
@@ -26,6 +26,7 @@ import (
"time"
"github.com/caddyserver/caddy/v2"
"golang.org/x/sync/singleflight"
)
func init() {
@@ -142,6 +143,7 @@ func (hba *HTTPBasicAuth) Provision(ctx caddy.Context) error {
if hba.HashCache != nil {
hba.HashCache.cache = make(map[string]bool)
hba.HashCache.mu = new(sync.RWMutex)
hba.HashCache.g = new(singleflight.Group)
}
return nil
@@ -190,12 +192,18 @@ func (hba HTTPBasicAuth) correctPassword(account Account, plaintextPassword []by
if ok {
return same, nil
}
// slow track: do the expensive op, then add it to the cache
same, err := compare()
// but perform it in a singleflight group so that multiple
// parallel requests using the same password don't cause a
// thundering herd problem by all performing the same hashing
// operation before the first one finishes and caches it.
v, err, _ := hba.HashCache.g.Do(cacheKey, func() (any, error) {
return compare()
})
if err != nil {
return false, err
}
same = v.(bool)
hba.HashCache.mu.Lock()
if len(hba.HashCache.cache) >= 1000 {
hba.HashCache.makeRoom() // keep cache size under control
@@ -223,6 +231,7 @@ func (hba HTTPBasicAuth) promptForCredentials(w http.ResponseWriter, err error)
// compute on every HTTP request.
type Cache struct {
mu *sync.RWMutex
g *singleflight.Group
// map of concatenated hashed password + plaintext password + salt, to result
cache map[string]bool
+4 -4
View File
@@ -27,10 +27,10 @@ func init() {
// parseCaddyfile sets up the handler from Caddyfile tokens. Syntax:
//
// basicauth [<matcher>] [<hash_algorithm> [<realm>]] {
// <username> <hashed_password_base64> [<salt_base64>]
// ...
// }
// basicauth [<matcher>] [<hash_algorithm> [<realm>]] {
// <username> <hashed_password_base64> [<salt_base64>]
// ...
// }
//
// If no hash algorithm is supplied, bcrypt will be assumed.
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
+7 -9
View File
@@ -18,20 +18,19 @@ import (
"bufio"
"bytes"
"encoding/base64"
"flag"
"fmt"
"os"
"os/signal"
"github.com/caddyserver/caddy/v2"
caddycmd "github.com/caddyserver/caddy/v2/cmd"
"github.com/spf13/cobra"
"golang.org/x/term"
)
func init() {
caddycmd.RegisterCommand(caddycmd.Command{
Name: "hash-password",
Func: cmdHashPassword,
Usage: "[--algorithm <name>] [--salt <string>] [--plaintext <password>]",
Short: "Hashes a password and writes base64",
Long: `
@@ -50,13 +49,12 @@ be provided (scrypt).
Note that scrypt is deprecated. Please use 'bcrypt' instead.
`,
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("hash-password", flag.ExitOnError)
fs.String("algorithm", "bcrypt", "Name of the hash algorithm")
fs.String("plaintext", "", "The plaintext password")
fs.String("salt", "", "The password salt")
return fs
}(),
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().StringP("plaintext", "p", "", "The plaintext password")
cmd.Flags().StringP("salt", "s", "", "The password salt")
cmd.Flags().StringP("algorithm", "a", "bcrypt", "Name of the hash algorithm")
cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdHashPassword)
},
})
}
+79 -2
View File
@@ -17,6 +17,7 @@ package caddyhttp
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
@@ -164,7 +165,7 @@ func (ws *WeakString) UnmarshalJSON(b []byte) error {
return nil
}
// MarshalJSON marshals was a boolean if true or false,
// MarshalJSON marshals as a boolean if true or false,
// a number if an integer, or a string otherwise.
func (ws WeakString) MarshalJSON() ([]byte, error) {
if ws == "true" {
@@ -204,6 +205,82 @@ func (ws WeakString) String() string {
return string(ws)
}
// Ratio is a type that unmarshals a valid numerical ratio string.
// Valid formats are:
// - a/b as a fraction (a / b)
// - a:b as a ratio (a / a+b)
// - a floating point number
type Ratio float64
// UnmarshalJSON satisfies json.Unmarshaler according to
// this type's documentation.
func (r *Ratio) UnmarshalJSON(b []byte) error {
if len(b) == 0 {
return io.EOF
}
if b[0] == byte('"') && b[len(b)-1] == byte('"') {
if !strings.Contains(string(b), "/") && !strings.Contains(string(b), ":") {
return fmt.Errorf("ratio string '%s' did not contain a slash '/' or colon ':'", string(b[1:len(b)-1]))
}
if strings.Contains(string(b), "/") {
left, right, _ := strings.Cut(string(b[1:len(b)-1]), "/")
num, err := strconv.Atoi(left)
if err != nil {
return fmt.Errorf("failed parsing numerator as integer %s: %v", left, err)
}
denom, err := strconv.Atoi(right)
if err != nil {
return fmt.Errorf("failed parsing denominator as integer %s: %v", right, err)
}
*r = Ratio(float64(num) / float64(denom))
return nil
}
if strings.Contains(string(b), ":") {
left, right, _ := strings.Cut(string(b[1:len(b)-1]), ":")
num, err := strconv.Atoi(left)
if err != nil {
return fmt.Errorf("failed parsing numerator as integer %s: %v", left, err)
}
denom, err := strconv.Atoi(right)
if err != nil {
return fmt.Errorf("failed parsing denominator as integer %s: %v", right, err)
}
*r = Ratio(float64(num) / (float64(num) + float64(denom)))
return nil
}
return fmt.Errorf("invalid ratio string '%s'", string(b[1:len(b)-1]))
}
if bytes.Equal(b, []byte("null")) {
return nil
}
float, err := strconv.ParseFloat(string(b), 64)
if err != nil {
return fmt.Errorf("failed parsing ratio as float %s: %v", b, err)
}
*r = Ratio(float)
return nil
}
func ParseRatio(r string) (Ratio, error) {
if strings.Contains(r, "/") {
left, right, _ := strings.Cut(r, "/")
num, err := strconv.Atoi(left)
if err != nil {
return 0, fmt.Errorf("failed parsing numerator as integer %s: %v", left, err)
}
denom, err := strconv.Atoi(right)
if err != nil {
return 0, fmt.Errorf("failed parsing denominator as integer %s: %v", right, err)
}
return Ratio(float64(num) / float64(denom)), nil
}
float, err := strconv.ParseFloat(r, 64)
if err != nil {
return 0, fmt.Errorf("failed parsing ratio as float %s: %v", r, err)
}
return Ratio(float), nil
}
// StatusCodeMatches returns true if a real HTTP status code matches
// the configured status code, which may be either a real HTTP status
// code or an integer representing a class of codes (e.g. 4 for all
@@ -232,7 +309,7 @@ func SanitizedPathJoin(root, reqPath string) string {
root = "."
}
path := filepath.Join(root, filepath.Clean("/"+reqPath))
path := filepath.Join(root, path.Clean("/"+reqPath))
// filepath.Join also cleans the path, and cleaning strips
// the trailing slash, so we need to re-add it afterwards.
+77 -1
View File
@@ -87,7 +87,7 @@ func TestSanitizedPathJoin(t *testing.T) {
}
actual := SanitizedPathJoin(tc.inputRoot, u.Path)
if actual != tc.expect {
t.Errorf("Test %d: SanitizedPathJoin('%s', '%s') => %s (expected '%s')",
t.Errorf("Test %d: SanitizedPathJoin('%s', '%s') => '%s' (expected '%s')",
i, tc.inputRoot, tc.inputPath, actual, tc.expect)
}
}
@@ -149,3 +149,79 @@ func TestCleanPath(t *testing.T) {
}
}
}
func TestUnmarshalRatio(t *testing.T) {
for i, tc := range []struct {
input []byte
expect float64
errMsg string
}{
{
input: []byte("null"),
expect: 0,
},
{
input: []byte(`"1/3"`),
expect: float64(1) / float64(3),
},
{
input: []byte(`"1/100"`),
expect: float64(1) / float64(100),
},
{
input: []byte(`"3:2"`),
expect: 0.6,
},
{
input: []byte(`"99:1"`),
expect: 0.99,
},
{
input: []byte(`"1/100"`),
expect: float64(1) / float64(100),
},
{
input: []byte(`0.1`),
expect: 0.1,
},
{
input: []byte(`0.005`),
expect: 0.005,
},
{
input: []byte(`0`),
expect: 0,
},
{
input: []byte(`"0"`),
errMsg: `ratio string '0' did not contain a slash '/' or colon ':'`,
},
{
input: []byte(`a`),
errMsg: `failed parsing ratio as float a: strconv.ParseFloat: parsing "a": invalid syntax`,
},
{
input: []byte(`"a/1"`),
errMsg: `failed parsing numerator as integer a: strconv.Atoi: parsing "a": invalid syntax`,
},
{
input: []byte(`"1/a"`),
errMsg: `failed parsing denominator as integer a: strconv.Atoi: parsing "a": invalid syntax`,
},
} {
ratio := Ratio(0)
err := ratio.UnmarshalJSON(tc.input)
if err != nil {
if tc.errMsg != "" {
if tc.errMsg != err.Error() {
t.Fatalf("Test %d: expected error: %v, got: %v", i, tc.errMsg, err)
}
continue
}
t.Fatalf("Test %d: invalid ratio: %v", i, err)
}
if ratio != Ratio(tc.expect) {
t.Fatalf("Test %d: expected %v, got %v", i, tc.expect, ratio)
}
}
}
+6 -4
View File
@@ -124,7 +124,7 @@ func (m *MatchExpression) Provision(ctx caddy.Context) error {
// create the CEL environment
env, err := cel.NewEnv(
cel.Function(placeholderFuncName, cel.SingletonBinaryImpl(m.caddyPlaceholderFunc), cel.Overload(
cel.Function(placeholderFuncName, cel.SingletonBinaryBinding(m.caddyPlaceholderFunc), cel.Overload(
placeholderFuncName+"_httpRequest_string",
[]*cel.Type{httpRequestObjectType, cel.StringType},
cel.AnyType,
@@ -191,15 +191,17 @@ func (m MatchExpression) caddyPlaceholderFunc(lhs, rhs ref.Val) ref.Val {
celReq, ok := lhs.(celHTTPRequest)
if !ok {
return types.NewErr(
"invalid request of type '%v' to "+placeholderFuncName+"(request, placeholderVarName)",
"invalid request of type '%v' to %s(request, placeholderVarName)",
lhs.Type(),
placeholderFuncName,
)
}
phStr, ok := rhs.(types.String)
if !ok {
return types.NewErr(
"invalid placeholder variable name of type '%v' to "+placeholderFuncName+"(request, placeholderVarName)",
"invalid placeholder variable name of type '%v' to %s(request, placeholderVarName)",
rhs.Type(),
placeholderFuncName,
)
}
@@ -345,7 +347,7 @@ func CELMatcherImpl(macroName, funcName string, matcherDataTypes []*cel.Type, fa
cel.Macros(macro),
cel.Function(funcName,
cel.Overload(funcName, append([]*cel.Type{requestType}, matcherDataTypes...), cel.BoolType),
cel.SingletonBinaryImpl(CELMatcherRuntimeFunction(funcName, fac))),
cel.SingletonBinaryBinding(CELMatcherRuntimeFunction(funcName, fac))),
}
programOptions := []cel.ProgramOption{
cel.CustomDecorator(CELMatcherDecorator(funcName, fac)),
+12 -12
View File
@@ -39,18 +39,18 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
// UnmarshalCaddyfile sets up the handler from Caddyfile tokens. Syntax:
//
// encode [<matcher>] <formats...> {
// gzip [<level>]
// zstd
// minimum_length <length>
// # response matcher block
// match {
// status <code...>
// header <field> [<value>]
// }
// # or response matcher single line syntax
// match [header <field> [<value>]] | [status <code...>]
// }
// encode [<matcher>] <formats...> {
// gzip [<level>]
// zstd
// minimum_length <length>
// # response matcher block
// match {
// status <code...>
// header <field> [<value>]
// }
// # or response matcher single line syntax
// match [header <field> [<value>]] | [status <code...>]
// }
//
// Specifying the formats on the first line will use those formats' defaults.
func (enc *Encode) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+27 -7
View File
@@ -20,9 +20,11 @@
package encode
import (
"bufio"
"fmt"
"io"
"math"
"net"
"net/http"
"sort"
"strconv"
@@ -117,14 +119,20 @@ func (enc *Encode) Validate() error {
return nil
}
func isEncodeAllowed(h http.Header) bool {
return !strings.Contains(h.Get("Cache-Control"), "no-transform")
}
func (enc *Encode) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
for _, encName := range AcceptedEncodings(r, enc.Prefer) {
if _, ok := enc.writerPools[encName]; !ok {
continue // encoding not offered
if isEncodeAllowed(r.Header) {
for _, encName := range AcceptedEncodings(r, enc.Prefer) {
if _, ok := enc.writerPools[encName]; !ok {
continue // encoding not offered
}
w = enc.openResponseWriter(encName, w)
defer w.(*responseWriter).Close()
break
}
w = enc.openResponseWriter(encName, w)
defer w.(*responseWriter).Close()
break
}
return next.ServeHTTP(w, r)
}
@@ -206,6 +214,18 @@ func (rw *responseWriter) Flush() {
rw.HTTPInterfaces.Flush()
}
// Hijack implements http.Hijacker. It will flush status code if set. We don't track actual hijacked
// status assuming http middlewares will track its status.
func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
if !rw.wroteHeader {
if rw.statusCode != 0 {
rw.HTTPInterfaces.WriteHeader(rw.statusCode)
}
rw.wroteHeader = true
}
return rw.HTTPInterfaces.Hijack()
}
// Write writes to the response. If the response qualifies,
// it is encoded using the encoder, which is initialized
// if not done so already.
@@ -281,7 +301,7 @@ func (rw *responseWriter) Close() error {
// init should be called before we write a response, if rw.buf has contents.
func (rw *responseWriter) init() {
if rw.Header().Get("Content-Encoding") == "" &&
if rw.Header().Get("Content-Encoding") == "" && isEncodeAllowed(rw.Header()) &&
rw.config.Match(rw) {
rw.w = rw.config.writerPools[rw.encodingName].Get().(Encoder)
+47
View File
@@ -261,3 +261,50 @@ func TestValidate(t *testing.T) {
}
}
func TestIsEncodeAllowed(t *testing.T) {
testCases := []struct {
name string
headers http.Header
expected bool
}{
{
name: "Without any headers",
headers: http.Header{},
expected: true,
},
{
name: "Without Cache-Control HTTP header",
headers: http.Header{
"Accept-Encoding": {"gzip"},
},
expected: true,
},
{
name: "Cache-Control HTTP header ending with no-transform directive",
headers: http.Header{
"Accept-Encoding": {"gzip"},
"Cache-Control": {"no-cache; no-transform"},
},
expected: false,
},
{
name: "With Cache-Control HTTP header no-transform as Cache-Extension value",
headers: http.Header{
"Accept-Encoding": {"gzip"},
"Cache-Control": {`no-store; no-cache; community="no-transform"`},
},
expected: false,
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
if result := isEncodeAllowed(test.headers); result != test.expected {
t.Errorf("The headers given to the isEncodeAllowed should return %t, %t given.",
result,
test.expected)
}
})
}
}
+9 -1
View File
@@ -152,12 +152,20 @@ func (fsrv *FileServer) loadDirectoryContents(ctx context.Context, dir fs.ReadDi
// browseApplyQueryParams applies query parameters to the listing.
// It mutates the listing and may set cookies.
func (fsrv *FileServer) browseApplyQueryParams(w http.ResponseWriter, r *http.Request, listing *browseTemplateContext) {
layoutParam := r.URL.Query().Get("layout")
sortParam := r.URL.Query().Get("sort")
orderParam := r.URL.Query().Get("order")
limitParam := r.URL.Query().Get("limit")
offsetParam := r.URL.Query().Get("offset")
// first figure out what to sort by
switch layoutParam {
case "list", "grid", "":
listing.Layout = layoutParam
default:
listing.Layout = "list"
}
// figure out what to sort by
switch sortParam {
case "":
sortParam = sortByNameDirFirst
File diff suppressed because it is too large Load Diff
@@ -134,6 +134,9 @@ type browseTemplateContext struct {
// Sorting order
Order string `json:"order,omitempty"`
// Display format (list or grid)
Layout string `json:"layout,omitempty"`
}
// Breadcrumbs returns l.Path where every element maps
@@ -229,6 +232,16 @@ type fileInfo struct {
IsSymlink bool `json:"is_symlink"`
}
// HasExt returns true if the filename has any of the given suffixes, case-insensitive.
func (fi fileInfo) HasExt(exts ...string) bool {
for _, ext := range exts {
if strings.HasSuffix(strings.ToLower(fi.Name), strings.ToLower(ext)) {
return true
}
}
return false
}
// HumanSize returns the size of the file as a
// human-readable string in IEC format (i.e.
// power of 2 or base 1024).
+14 -14
View File
@@ -16,7 +16,6 @@ package fileserver
import (
"encoding/json"
"flag"
"log"
"strconv"
"time"
@@ -27,13 +26,13 @@ import (
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
caddytpl "github.com/caddyserver/caddy/v2/modules/caddyhttp/templates"
"github.com/caddyserver/certmagic"
"github.com/spf13/cobra"
"go.uber.org/zap"
)
func init() {
caddycmd.RegisterCommand(caddycmd.Command{
Name: "file-server",
Func: cmdFileServer,
Usage: "[--domain <example.com>] [--root <path>] [--listen <addr>] [--browse] [--access-log]",
Short: "Spins up a production-ready file server",
Long: `
@@ -49,17 +48,16 @@ using this option.
If --browse is enabled, requests for folders without an index file will
respond with a file listing.`,
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("file-server", flag.ExitOnError)
fs.String("domain", "", "Domain name at which to serve the files")
fs.String("root", "", "The path to the root of the site")
fs.String("listen", "", "The address to which to bind the listener")
fs.Bool("browse", false, "Enable directory browsing")
fs.Bool("templates", false, "Enable template rendering")
fs.Bool("access-log", false, "Enable the access log")
fs.Bool("debug", false, "Enable verbose debug logs")
return fs
}(),
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().StringP("domain", "d", "", "Domain name at which to serve the files")
cmd.Flags().StringP("root", "r", "", "The path to the root of the site")
cmd.Flags().StringP("listen", "", "", "The address to which to bind the listener")
cmd.Flags().BoolP("browse", "b", false, "Enable directory browsing")
cmd.Flags().BoolP("templates", "t", false, "Enable template rendering")
cmd.Flags().BoolP("access-log", "", false, "Enable the access log")
cmd.Flags().BoolP("debug", "v", false, "Enable verbose debug logs")
cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdFileServer)
},
})
}
@@ -136,7 +134,9 @@ func cmdFileServer(fs caddycmd.Flags) (int, error) {
if debug {
cfg.Logging = &caddy.Logging{
Logs: map[string]*caddy.CustomLog{
"default": {Level: zap.DebugLevel.CapitalString()},
"default": {
BaseLog: caddy.BaseLog{Level: zap.DebugLevel.CapitalString()},
},
},
}
}
+17 -12
View File
@@ -163,6 +163,8 @@ func (m *MatchFile) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
//
// Example:
//
// expression file()
// expression file({http.request.uri.path}, '/index.php')
// expression file({'root': '/srv', 'try_files': [{http.request.uri.path}, '/index.php'], 'try_policy': 'first_exist', 'split_path': ['.php']})
func (MatchFile) CELLibrary(ctx caddy.Context) (cel.Library, error) {
requestType := cel.ObjectType("http.Request")
@@ -199,7 +201,7 @@ func (MatchFile) CELLibrary(ctx caddy.Context) (cel.Library, error) {
cel.Function("file", cel.Overload("file_request_map", []*cel.Type{requestType, caddyhttp.CELTypeJSON}, cel.BoolType)),
cel.Function("file_request_map",
cel.Overload("file_request_map", []*cel.Type{requestType, caddyhttp.CELTypeJSON}, cel.BoolType),
cel.SingletonBinaryImpl(caddyhttp.CELMatcherRuntimeFunction("file_request_map", matcherFactory))),
cel.SingletonBinaryBinding(caddyhttp.CELMatcherRuntimeFunction("file_request_map", matcherFactory))),
}
programOptions := []cel.ProgramOption{
@@ -212,18 +214,21 @@ func (MatchFile) CELLibrary(ctx caddy.Context) (cel.Library, error) {
func celFileMatcherMacroExpander() parser.MacroExpander {
return func(eh parser.ExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
if len(args) == 0 {
return nil, &common.Error{
Message: "matcher requires at least one argument",
}
return eh.GlobalCall("file",
eh.Ident("request"),
eh.NewMap(),
), nil
}
if len(args) == 1 {
arg := args[0]
if isCELStringLiteral(arg) || isCELCaddyPlaceholderCall(arg) {
return eh.GlobalCall("file",
eh.Ident("request"),
eh.NewMap(
eh.NewMapEntry(eh.LiteralString("try_files"), eh.NewList(arg)),
),
eh.NewMap(eh.NewMapEntry(
eh.LiteralString("try_files"),
eh.NewList(arg),
false,
)),
), nil
}
if isCELTryFilesLiteral(arg) {
@@ -245,11 +250,11 @@ func celFileMatcherMacroExpander() parser.MacroExpander {
}
return eh.GlobalCall("file",
eh.Ident("request"),
eh.NewMap(
eh.NewMapEntry(
eh.LiteralString("try_files"), eh.NewList(args...),
),
),
eh.NewMap(eh.NewMapEntry(
eh.LiteralString("try_files"),
eh.NewList(args...),
false,
)),
), nil
}
}
+14 -2
View File
@@ -62,6 +62,12 @@ func TestFileMatcher(t *testing.T) {
expectedType: "file",
matched: true,
},
{
path: "/foo.txt?a=b",
expectedPath: "/foo.txt",
expectedType: "file",
matched: true,
},
{
path: "/foodir",
expectedPath: "/foodir/",
@@ -211,6 +217,12 @@ func TestPHPFileMatcher(t *testing.T) {
expectedType: "file",
matched: false,
},
{
path: "/index.php?path={path}&{query}",
expectedPath: "/index.php",
expectedType: "file",
matched: true,
},
} {
m := &MatchFile{
fileSystem: osFS{},
@@ -280,8 +292,8 @@ var (
expression: &caddyhttp.MatchExpression{
Expr: `file()`,
},
urlTarget: "https://example.com/foo",
wantErr: true,
urlTarget: "https://example.com/foo.txt",
wantResult: true,
},
{
name: "file error bad try files (MatchFile)",
+24 -1
View File
@@ -26,6 +26,7 @@ import (
"os"
"path"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
@@ -232,11 +233,25 @@ func (fsrv *FileServer) Provision(ctx caddy.Context) error {
func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
if runtime.GOOS == "windows" {
// reject paths with Alternate Data Streams (ADS)
if strings.Contains(r.URL.Path, ":") {
return caddyhttp.Error(http.StatusBadRequest, fmt.Errorf("illegal ADS path"))
}
// reject paths with "8.3" short names
trimmedPath := strings.TrimRight(r.URL.Path, ". ") // Windows ignores trailing dots and spaces, sigh
if len(path.Base(trimmedPath)) <= 12 && strings.Contains(trimmedPath, "~") {
return caddyhttp.Error(http.StatusBadRequest, fmt.Errorf("illegal short name"))
}
// both of those could bypass file hiding or possibly leak information even if the file is not hidden
}
filesToHide := fsrv.transformHidePaths(repl)
root := repl.ReplaceAll(fsrv.Root, ".")
filename := caddyhttp.SanitizedPathJoin(root, r.URL.Path)
// remove any trailing `/` as it breaks fs.ValidPath() in the stdlib
filename := strings.TrimSuffix(caddyhttp.SanitizedPathJoin(root, r.URL.Path), "/")
fsrv.logger.Debug("sanitized path join",
zap.String("site_root", root),
@@ -396,6 +411,14 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
etag = calculateEtag(info)
}
// at this point, we're serving a file; Go std lib supports only
// GET and HEAD, which is sensible for a static file server - reject
// any other methods (see issue #5166)
if r.Method != http.MethodGet && r.Method != http.MethodHead {
w.Header().Add("Allow", "GET, HEAD")
return caddyhttp.Error(http.StatusMethodNotAllowed, nil)
}
// set the Etag - note that a conditional If-None-Match request is handled
// by http.ServeContent below, which checks against this Etag value
w.Header().Set("Etag", etag)
+16
View File
@@ -192,6 +192,19 @@ type RespHeaderOps struct {
// ApplyTo applies ops to hdr using repl.
func (ops HeaderOps) ApplyTo(hdr http.Header, repl *caddy.Replacer) {
// before manipulating headers in other ways, check if there
// is configuration to delete all headers, and do that first
// because if a header is to be added, we don't want to delete
// it also
for _, fieldName := range ops.Delete {
fieldName = repl.ReplaceKnown(fieldName, "")
if fieldName == "*" {
for existingField := range hdr {
delete(hdr, existingField)
}
}
}
// add
for fieldName, vals := range ops.Add {
fieldName = repl.ReplaceKnown(fieldName, "")
@@ -215,6 +228,9 @@ func (ops HeaderOps) ApplyTo(hdr http.Header, repl *caddy.Replacer) {
// delete
for _, fieldName := range ops.Delete {
fieldName = strings.ToLower(repl.ReplaceKnown(fieldName, ""))
if fieldName == "*" {
continue // handled above
}
switch {
case strings.HasPrefix(fieldName, "*") && strings.HasSuffix(fieldName, "*"):
for existingField := range hdr {
+102
View File
@@ -0,0 +1,102 @@
package caddyhttp
import (
"context"
"crypto/tls"
weakrand "math/rand"
"net"
"net/http"
"sync/atomic"
"time"
"golang.org/x/net/http2"
)
// http2Listener wraps the listener to solve the following problems:
// 1. server h2 natively without using h2c hack when listener handles tls connection but
// don't return *tls.Conn
// 2. graceful shutdown. the shutdown logic is copied from stdlib http.Server, it's an extra maintenance burden but
// whatever, the shutdown logic maybe extracted to be used with h2c graceful shutdown. http2.Server supports graceful shutdown
// sending GO_AWAY frame to connected clients, but doesn't track connection status. It requires explicit call of http2.ConfigureServer
type http2Listener struct {
cnt uint64
net.Listener
server *http.Server
h2server *http2.Server
}
type connectionStateConn interface {
net.Conn
ConnectionState() tls.ConnectionState
}
func (h *http2Listener) Accept() (net.Conn, error) {
for {
conn, err := h.Listener.Accept()
if err != nil {
return nil, err
}
if csc, ok := conn.(connectionStateConn); ok {
// *tls.Conn will return empty string because it's only populated after handshake is complete
if csc.ConnectionState().NegotiatedProtocol == http2.NextProtoTLS {
go h.serveHttp2(csc)
continue
}
}
return conn, nil
}
}
func (h *http2Listener) serveHttp2(csc connectionStateConn) {
atomic.AddUint64(&h.cnt, 1)
h.runHook(csc, http.StateNew)
defer func() {
csc.Close()
atomic.AddUint64(&h.cnt, ^uint64(0))
h.runHook(csc, http.StateClosed)
}()
h.h2server.ServeConn(csc, &http2.ServeConnOpts{
Context: h.server.ConnContext(context.Background(), csc),
BaseConfig: h.server,
Handler: h.server.Handler,
})
}
const shutdownPollIntervalMax = 500 * time.Millisecond
func (h *http2Listener) Shutdown(ctx context.Context) error {
pollIntervalBase := time.Millisecond
nextPollInterval := func() time.Duration {
// Add 10% jitter.
//nolint:gosec
interval := pollIntervalBase + time.Duration(weakrand.Intn(int(pollIntervalBase/10)))
// Double and clamp for next time.
pollIntervalBase *= 2
if pollIntervalBase > shutdownPollIntervalMax {
pollIntervalBase = shutdownPollIntervalMax
}
return interval
}
timer := time.NewTimer(nextPollInterval())
defer timer.Stop()
for {
if atomic.LoadUint64(&h.cnt) == 0 {
return nil
}
select {
case <-ctx.Done():
return ctx.Err()
case <-timer.C:
timer.Reset(nextPollInterval())
}
}
}
func (h *http2Listener) runHook(conn net.Conn, state http.ConnState) {
if h.server.ConnState != nil {
h.server.ConnState(conn, state)
}
}
+344
View File
@@ -0,0 +1,344 @@
// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package caddyhttp
import (
"errors"
"fmt"
"net"
"net/http"
"net/netip"
"reflect"
"strings"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types/ref"
"go.uber.org/zap"
)
// MatchRemoteIP matches requests by the remote IP address,
// i.e. the IP address of the direct connection to Caddy.
type MatchRemoteIP struct {
// The IPs or CIDR ranges to match.
Ranges []string `json:"ranges,omitempty"`
// If true, prefer the first IP in the request's X-Forwarded-For
// header, if present, rather than the immediate peer's IP, as
// the reference IP against which to match. Note that it is easy
// to spoof request headers. Default: false
// DEPRECATED: This is insecure, MatchClientIP should be used instead.
Forwarded bool `json:"forwarded,omitempty"`
// cidrs and zones vars should aligned always in the same
// length and indexes for matching later
cidrs []*netip.Prefix
zones []string
logger *zap.Logger
}
// MatchClientIP matches requests by the client IP address,
// i.e. the resolved address, considering trusted proxies.
type MatchClientIP struct {
// The IPs or CIDR ranges to match.
Ranges []string `json:"ranges,omitempty"`
// cidrs and zones vars should aligned always in the same
// length and indexes for matching later
cidrs []*netip.Prefix
zones []string
logger *zap.Logger
}
func init() {
caddy.RegisterModule(MatchRemoteIP{})
caddy.RegisterModule(MatchClientIP{})
}
// CaddyModule returns the Caddy module information.
func (MatchRemoteIP) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.matchers.remote_ip",
New: func() caddy.Module { return new(MatchRemoteIP) },
}
}
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (m *MatchRemoteIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
for d.NextArg() {
if d.Val() == "forwarded" {
if len(m.Ranges) > 0 {
return d.Err("if used, 'forwarded' must be first argument")
}
m.Forwarded = true
continue
}
if d.Val() == "private_ranges" {
m.Ranges = append(m.Ranges, PrivateRangesCIDR()...)
continue
}
m.Ranges = append(m.Ranges, d.Val())
}
if d.NextBlock(0) {
return d.Err("malformed remote_ip matcher: blocks are not supported")
}
}
return nil
}
// CELLibrary produces options that expose this matcher for use in CEL
// expression matchers.
//
// Example:
//
// expression remote_ip('forwarded', '192.168.0.0/16', '172.16.0.0/12', '10.0.0.0/8')
func (MatchRemoteIP) CELLibrary(ctx caddy.Context) (cel.Library, error) {
return CELMatcherImpl(
// name of the macro, this is the function name that users see when writing expressions.
"remote_ip",
// name of the function that the macro will be rewritten to call.
"remote_ip_match_request_list",
// internal data type of the MatchPath value.
[]*cel.Type{cel.ListType(cel.StringType)},
// function to convert a constant list of strings to a MatchPath instance.
func(data ref.Val) (RequestMatcher, error) {
refStringList := reflect.TypeOf([]string{})
strList, err := data.ConvertToNative(refStringList)
if err != nil {
return nil, err
}
m := MatchRemoteIP{}
for _, input := range strList.([]string) {
if input == "forwarded" {
if len(m.Ranges) > 0 {
return nil, errors.New("if used, 'forwarded' must be first argument")
}
m.Forwarded = true
continue
}
m.Ranges = append(m.Ranges, input)
}
err = m.Provision(ctx)
return m, err
},
)
}
// Provision parses m's IP ranges, either from IP or CIDR expressions.
func (m *MatchRemoteIP) Provision(ctx caddy.Context) error {
m.logger = ctx.Logger()
cidrs, zones, err := provisionCidrsZonesFromRanges(m.Ranges)
if err != nil {
return err
}
m.cidrs = cidrs
m.zones = zones
if m.Forwarded {
m.logger.Warn("remote_ip's forwarded mode is deprecated; use the 'client_ip' matcher instead")
}
return nil
}
// Match returns true if r matches m.
func (m MatchRemoteIP) Match(r *http.Request) bool {
address := r.RemoteAddr
if m.Forwarded {
if fwdFor := r.Header.Get("X-Forwarded-For"); fwdFor != "" {
address = strings.TrimSpace(strings.Split(fwdFor, ",")[0])
}
}
clientIP, zoneID, err := parseIPZoneFromString(address)
if err != nil {
m.logger.Error("getting remote IP", zap.Error(err))
return false
}
matches, zoneFilter := matchIPByCidrZones(clientIP, zoneID, m.cidrs, m.zones)
if !matches && !zoneFilter {
m.logger.Debug("zone ID from remote IP did not match", zap.String("zone", zoneID))
}
return matches
}
// CaddyModule returns the Caddy module information.
func (MatchClientIP) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.matchers.client_ip",
New: func() caddy.Module { return new(MatchClientIP) },
}
}
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (m *MatchClientIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
for d.NextArg() {
if d.Val() == "private_ranges" {
m.Ranges = append(m.Ranges, PrivateRangesCIDR()...)
continue
}
m.Ranges = append(m.Ranges, d.Val())
}
if d.NextBlock(0) {
return d.Err("malformed client_ip matcher: blocks are not supported")
}
}
return nil
}
// CELLibrary produces options that expose this matcher for use in CEL
// expression matchers.
//
// Example:
//
// expression client_ip('192.168.0.0/16', '172.16.0.0/12', '10.0.0.0/8')
func (MatchClientIP) CELLibrary(ctx caddy.Context) (cel.Library, error) {
return CELMatcherImpl(
// name of the macro, this is the function name that users see when writing expressions.
"client_ip",
// name of the function that the macro will be rewritten to call.
"client_ip_match_request_list",
// internal data type of the MatchPath value.
[]*cel.Type{cel.ListType(cel.StringType)},
// function to convert a constant list of strings to a MatchPath instance.
func(data ref.Val) (RequestMatcher, error) {
refStringList := reflect.TypeOf([]string{})
strList, err := data.ConvertToNative(refStringList)
if err != nil {
return nil, err
}
m := MatchClientIP{
Ranges: strList.([]string),
}
err = m.Provision(ctx)
return m, err
},
)
}
// Provision parses m's IP ranges, either from IP or CIDR expressions.
func (m *MatchClientIP) Provision(ctx caddy.Context) error {
m.logger = ctx.Logger()
cidrs, zones, err := provisionCidrsZonesFromRanges(m.Ranges)
if err != nil {
return err
}
m.cidrs = cidrs
m.zones = zones
return nil
}
// Match returns true if r matches m.
func (m MatchClientIP) Match(r *http.Request) bool {
address := GetVar(r.Context(), ClientIPVarKey).(string)
clientIP, zoneID, err := parseIPZoneFromString(address)
if err != nil {
m.logger.Error("getting client IP", zap.Error(err))
return false
}
matches, zoneFilter := matchIPByCidrZones(clientIP, zoneID, m.cidrs, m.zones)
if !matches && !zoneFilter {
m.logger.Debug("zone ID from client IP did not match", zap.String("zone", zoneID))
}
return matches
}
func provisionCidrsZonesFromRanges(ranges []string) ([]*netip.Prefix, []string, error) {
cidrs := []*netip.Prefix{}
zones := []string{}
for _, str := range ranges {
// Exclude the zone_id from the IP
if strings.Contains(str, "%") {
split := strings.Split(str, "%")
str = split[0]
// write zone identifiers in m.zones for matching later
zones = append(zones, split[1])
} else {
zones = append(zones, "")
}
if strings.Contains(str, "/") {
ipNet, err := netip.ParsePrefix(str)
if err != nil {
return nil, nil, fmt.Errorf("parsing CIDR expression '%s': %v", str, err)
}
cidrs = append(cidrs, &ipNet)
} else {
ipAddr, err := netip.ParseAddr(str)
if err != nil {
return nil, nil, fmt.Errorf("invalid IP address: '%s': %v", str, err)
}
ipNew := netip.PrefixFrom(ipAddr, ipAddr.BitLen())
cidrs = append(cidrs, &ipNew)
}
}
return cidrs, zones, nil
}
func parseIPZoneFromString(address string) (netip.Addr, string, error) {
ipStr, _, err := net.SplitHostPort(address)
if err != nil {
ipStr = address // OK; probably didn't have a port
}
// Some IPv6-Adresses can contain zone identifiers at the end,
// which are separated with "%"
zoneID := ""
if strings.Contains(ipStr, "%") {
split := strings.Split(ipStr, "%")
ipStr = split[0]
zoneID = split[1]
}
ipAddr, err := netip.ParseAddr(ipStr)
if err != nil {
return netip.IPv4Unspecified(), "", err
}
return ipAddr, zoneID, nil
}
func matchIPByCidrZones(clientIP netip.Addr, zoneID string, cidrs []*netip.Prefix, zones []string) (bool, bool) {
zoneFilter := true
for i, ipRange := range cidrs {
if ipRange.Contains(clientIP) {
// Check if there are zone filters assigned and if they match.
if zones[i] == "" || zoneID == zones[i] {
return true, false
}
zoneFilter = false
}
}
return false, zoneFilter
}
// Interface guards
var (
_ RequestMatcher = (*MatchRemoteIP)(nil)
_ caddy.Provisioner = (*MatchRemoteIP)(nil)
_ caddyfile.Unmarshaler = (*MatchRemoteIP)(nil)
_ CELLibraryProducer = (*MatchRemoteIP)(nil)
_ RequestMatcher = (*MatchClientIP)(nil)
_ caddy.Provisioner = (*MatchClientIP)(nil)
_ caddyfile.Unmarshaler = (*MatchClientIP)(nil)
_ CELLibraryProducer = (*MatchClientIP)(nil)
)
+142
View File
@@ -0,0 +1,142 @@
// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package caddyhttp
import (
"fmt"
"net/http"
"net/netip"
"strings"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
)
func init() {
caddy.RegisterModule(StaticIPRange{})
}
// IPRangeSource gets a list of IP ranges.
//
// The request is passed as an argument to allow plugin implementations
// to have more flexibility. But, a plugin MUST NOT modify the request.
// The caller will have read the `r.RemoteAddr` before getting IP ranges.
//
// This should be a very fast function -- instant if possible.
// The list of IP ranges should be sourced as soon as possible if loaded
// from an external source (i.e. initially loaded during Provisioning),
// so that it's ready to be used when requests start getting handled.
// A read lock should probably be used to get the cached value if the
// ranges can change at runtime (e.g. periodically refreshed).
// Using a `caddy.UsagePool` may be a good idea to avoid having refetch
// the values when a config reload occurs, which would waste time.
//
// If the list of IP ranges cannot be sourced, then provisioning SHOULD
// fail. Getting the IP ranges at runtime MUST NOT fail, because it would
// cancel incoming requests. If refreshing the list fails, then the
// previous list of IP ranges should continue to be returned so that the
// server can continue to operate normally.
type IPRangeSource interface {
GetIPRanges(*http.Request) []netip.Prefix
}
// StaticIPRange provides a static range of IP address prefixes (CIDRs).
type StaticIPRange struct {
// A static list of IP ranges (supports CIDR notation).
Ranges []string `json:"ranges,omitempty"`
// Holds the parsed CIDR ranges from Ranges.
ranges []netip.Prefix
}
// CaddyModule returns the Caddy module information.
func (StaticIPRange) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.ip_sources.static",
New: func() caddy.Module { return new(StaticIPRange) },
}
}
func (s *StaticIPRange) Provision(ctx caddy.Context) error {
for _, str := range s.Ranges {
prefix, err := CIDRExpressionToPrefix(str)
if err != nil {
return err
}
s.ranges = append(s.ranges, prefix)
}
return nil
}
func (s *StaticIPRange) GetIPRanges(_ *http.Request) []netip.Prefix {
return s.ranges
}
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (m *StaticIPRange) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
if !d.Next() {
return nil
}
for d.NextArg() {
if d.Val() == "private_ranges" {
m.Ranges = append(m.Ranges, PrivateRangesCIDR()...)
continue
}
m.Ranges = append(m.Ranges, d.Val())
}
return nil
}
// CIDRExpressionToPrefix takes a string which could be either a
// CIDR expression or a single IP address, and returns a netip.Prefix.
func CIDRExpressionToPrefix(expr string) (netip.Prefix, error) {
// Having a slash means it should be a CIDR expression
if strings.Contains(expr, "/") {
prefix, err := netip.ParsePrefix(expr)
if err != nil {
return netip.Prefix{}, fmt.Errorf("parsing CIDR expression: '%s': %v", expr, err)
}
return prefix, nil
}
// Otherwise it's likely a single IP address
parsed, err := netip.ParseAddr(expr)
if err != nil {
return netip.Prefix{}, fmt.Errorf("invalid IP address: '%s': %v", expr, err)
}
prefix := netip.PrefixFrom(parsed, parsed.BitLen())
return prefix, nil
}
// PrivateRangesCIDR returns a list of private CIDR range
// strings, which can be used as a configuration shortcut.
func PrivateRangesCIDR() []string {
return []string{
"192.168.0.0/16",
"172.16.0.0/12",
"10.0.0.0/8",
"127.0.0.1/8",
"fd00::/8",
"::1",
}
}
// Interface guards
var (
_ caddy.Provisioner = (*StaticIPRange)(nil)
_ caddyfile.Unmarshaler = (*StaticIPRange)(nil)
_ IPRangeSource = (*StaticIPRange)(nil)
)
+1
View File
@@ -40,6 +40,7 @@ type Handler struct {
Source string `json:"source,omitempty"`
// Destinations are the names of placeholders in which to store the outputs.
// Destination values should be wrapped in braces, for example, {my_placeholder}.
Destinations []string `json:"destinations,omitempty"`
// Mappings from source values (inputs) to destination values (outputs).
+1
View File
@@ -40,6 +40,7 @@ func (r LoggableHTTPRequest) MarshalLogObject(enc zapcore.ObjectEncoder) error {
enc.AddString("remote_ip", ip)
enc.AddString("remote_port", port)
enc.AddString("client_ip", GetVar(r.Context(), ClientIPVarKey).(string))
enc.AddString("proto", r.Proto)
enc.AddString("method", r.Method)
enc.AddString("host", r.Host)
+15 -185
View File
@@ -20,7 +20,6 @@ import (
"fmt"
"net"
"net/http"
"net/netip"
"net/textproto"
"net/url"
"path"
@@ -35,7 +34,6 @@ import (
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"go.uber.org/zap"
)
type (
@@ -123,6 +121,7 @@ type (
// keyed by the query keys, with an array of string values to match for that key.
// Query key matches are exact, but wildcards may be used for value matches. Both
// keys and values may be placeholders.
//
// An example of the structure to match `?key=value&topic=api&query=something` is:
//
// ```json
@@ -135,6 +134,13 @@ type (
//
// Invalid query strings, including those with bad escapings or illegal characters
// like semicolons, will fail to parse and thus fail to match.
//
// **NOTE:** Notice that query string values are arrays, not singular values. This is
// because repeated keys are valid in query strings, and each one may have a
// different value. This matcher will match for a key if any one of its configured
// values is assigned in the query string. Backend applications relying on query
// strings MUST take into consideration that query string values are arrays and can
// have multiple values.
MatchQuery url.Values
// MatchHeader matches requests by header fields. The key is the field
@@ -144,6 +150,13 @@ type (
// surrounding the value with the wildcard `*` character, respectively.
// If a list is null, the header must not exist. If the list is empty,
// the field must simply exist, regardless of its value.
//
// **NOTE:** Notice that header values are arrays, not singular values. This is
// because repeated fields are valid in headers, and each one may have a
// different value. This matcher will match for a field if any one of its configured
// values matches in the header. Backend applications relying on headers MUST take
// into consideration that header field values are arrays and can have multiple
// values.
MatchHeader http.Header
// MatchHeaderRE matches requests by a regular expression on header fields.
@@ -161,24 +174,6 @@ type (
// "http/2", "http/3", or minimum versions: "http/2+", etc.
MatchProtocol string
// MatchRemoteIP matches requests by client IP (or CIDR range).
MatchRemoteIP struct {
// The IPs or CIDR ranges to match.
Ranges []string `json:"ranges,omitempty"`
// If true, prefer the first IP in the request's X-Forwarded-For
// header, if present, rather than the immediate peer's IP, as
// the reference IP against which to match. Note that it is easy
// to spoof request headers. Default: false
Forwarded bool `json:"forwarded,omitempty"`
// cidrs and zones vars should aligned always in the same
// length and indexes for matching later
cidrs []*netip.Prefix
zones []string
logger *zap.Logger
}
// MatchNot matches requests by negating the results of its matcher
// sets. A single "not" matcher takes one or more matcher sets. Each
// matcher set is OR'ed; in other words, if any matcher set returns
@@ -214,7 +209,6 @@ func init() {
caddy.RegisterModule(MatchHeader{})
caddy.RegisterModule(MatchHeaderRE{})
caddy.RegisterModule(new(MatchProtocol))
caddy.RegisterModule(MatchRemoteIP{})
caddy.RegisterModule(MatchNot{})
}
@@ -1246,166 +1240,6 @@ func (m MatchNot) Match(r *http.Request) bool {
return true
}
// CaddyModule returns the Caddy module information.
func (MatchRemoteIP) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.matchers.remote_ip",
New: func() caddy.Module { return new(MatchRemoteIP) },
}
}
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (m *MatchRemoteIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
for d.NextArg() {
if d.Val() == "forwarded" {
if len(m.Ranges) > 0 {
return d.Err("if used, 'forwarded' must be first argument")
}
m.Forwarded = true
continue
}
if d.Val() == "private_ranges" {
m.Ranges = append(m.Ranges, []string{
"192.168.0.0/16",
"172.16.0.0/12",
"10.0.0.0/8",
"127.0.0.1/8",
"fd00::/8",
"::1",
}...)
continue
}
m.Ranges = append(m.Ranges, d.Val())
}
if d.NextBlock(0) {
return d.Err("malformed remote_ip matcher: blocks are not supported")
}
}
return nil
}
// CELLibrary produces options that expose this matcher for use in CEL
// expression matchers.
//
// Example:
//
// expression remote_ip('forwarded', '192.168.0.0/16', '172.16.0.0/12', '10.0.0.0/8')
func (MatchRemoteIP) CELLibrary(ctx caddy.Context) (cel.Library, error) {
return CELMatcherImpl(
// name of the macro, this is the function name that users see when writing expressions.
"remote_ip",
// name of the function that the macro will be rewritten to call.
"remote_ip_match_request_list",
// internal data type of the MatchPath value.
[]*cel.Type{cel.ListType(cel.StringType)},
// function to convert a constant list of strings to a MatchPath instance.
func(data ref.Val) (RequestMatcher, error) {
refStringList := reflect.TypeOf([]string{})
strList, err := data.ConvertToNative(refStringList)
if err != nil {
return nil, err
}
m := MatchRemoteIP{}
for _, input := range strList.([]string) {
if input == "forwarded" {
if len(m.Ranges) > 0 {
return nil, errors.New("if used, 'forwarded' must be first argument")
}
m.Forwarded = true
continue
}
m.Ranges = append(m.Ranges, input)
}
err = m.Provision(ctx)
return m, err
},
)
}
// Provision parses m's IP ranges, either from IP or CIDR expressions.
func (m *MatchRemoteIP) Provision(ctx caddy.Context) error {
m.logger = ctx.Logger()
for _, str := range m.Ranges {
// Exclude the zone_id from the IP
if strings.Contains(str, "%") {
split := strings.Split(str, "%")
str = split[0]
// write zone identifiers in m.zones for matching later
m.zones = append(m.zones, split[1])
} else {
m.zones = append(m.zones, "")
}
if strings.Contains(str, "/") {
ipNet, err := netip.ParsePrefix(str)
if err != nil {
return fmt.Errorf("parsing CIDR expression '%s': %v", str, err)
}
m.cidrs = append(m.cidrs, &ipNet)
} else {
ipAddr, err := netip.ParseAddr(str)
if err != nil {
return fmt.Errorf("invalid IP address: '%s': %v", str, err)
}
ipNew := netip.PrefixFrom(ipAddr, ipAddr.BitLen())
m.cidrs = append(m.cidrs, &ipNew)
}
}
return nil
}
func (m MatchRemoteIP) getClientIP(r *http.Request) (netip.Addr, string, error) {
remote := r.RemoteAddr
zoneID := ""
if m.Forwarded {
if fwdFor := r.Header.Get("X-Forwarded-For"); fwdFor != "" {
remote = strings.TrimSpace(strings.Split(fwdFor, ",")[0])
}
}
ipStr, _, err := net.SplitHostPort(remote)
if err != nil {
ipStr = remote // OK; probably didn't have a port
}
// Some IPv6-Adresses can contain zone identifiers at the end,
// which are separated with "%"
if strings.Contains(ipStr, "%") {
split := strings.Split(ipStr, "%")
ipStr = split[0]
zoneID = split[1]
}
ipAddr, err := netip.ParseAddr(ipStr)
if err != nil {
return netip.IPv4Unspecified(), "", err
}
return ipAddr, zoneID, nil
}
// Match returns true if r matches m.
func (m MatchRemoteIP) Match(r *http.Request) bool {
clientIP, zoneID, err := m.getClientIP(r)
if err != nil {
m.logger.Error("getting client IP", zap.Error(err))
return false
}
zoneFilter := true
for i, ipRange := range m.cidrs {
if ipRange.Contains(clientIP) {
// Check if there are zone filters assigned and if they match.
if m.zones[i] == "" || zoneID == m.zones[i] {
return true
}
zoneFilter = false
}
}
if !zoneFilter {
m.logger.Debug("zone ID from remote did not match", zap.String("zone", zoneID))
}
return false
}
// MatchRegexp is an embedable type for matching
// using regular expressions. It adds placeholders
// to the request's replacer.
@@ -1580,8 +1414,6 @@ var (
_ RequestMatcher = (*MatchHeaderRE)(nil)
_ caddy.Provisioner = (*MatchHeaderRE)(nil)
_ RequestMatcher = (*MatchProtocol)(nil)
_ RequestMatcher = (*MatchRemoteIP)(nil)
_ caddy.Provisioner = (*MatchRemoteIP)(nil)
_ RequestMatcher = (*MatchNot)(nil)
_ caddy.Provisioner = (*MatchNot)(nil)
_ caddy.Provisioner = (*MatchRegexp)(nil)
@@ -1594,7 +1426,6 @@ var (
_ caddyfile.Unmarshaler = (*MatchHeader)(nil)
_ caddyfile.Unmarshaler = (*MatchHeaderRE)(nil)
_ caddyfile.Unmarshaler = (*MatchProtocol)(nil)
_ caddyfile.Unmarshaler = (*MatchRemoteIP)(nil)
_ caddyfile.Unmarshaler = (*VarsMatcher)(nil)
_ caddyfile.Unmarshaler = (*MatchVarsRE)(nil)
@@ -1606,7 +1437,6 @@ var (
_ CELLibraryProducer = (*MatchHeader)(nil)
_ CELLibraryProducer = (*MatchHeaderRE)(nil)
_ CELLibraryProducer = (*MatchProtocol)(nil)
_ CELLibraryProducer = (*MatchRemoteIP)(nil)
// _ CELLibraryProducer = (*VarsMatcher)(nil)
// _ CELLibraryProducer = (*MatchVarsRE)(nil)
+1
View File
@@ -929,6 +929,7 @@ func TestVarREMatcher(t *testing.T) {
expect: true,
},
} {
i := i // capture range value
tc := tc // capture range value
t.Run(tc.desc, func(t *testing.T) {
t.Parallel()
@@ -0,0 +1,68 @@
// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package proxyprotocol
import (
"fmt"
"net"
"time"
"github.com/caddyserver/caddy/v2"
"github.com/mastercactapus/proxyprotocol"
)
// ListenerWrapper provides PROXY protocol support to Caddy by implementing
// the caddy.ListenerWrapper interface. It must be loaded before the `tls` listener.
//
// Credit goes to https://github.com/mastercactapus/caddy2-proxyprotocol for having
// initially implemented this as a plugin.
type ListenerWrapper struct {
// Timeout specifies an optional maximum time for
// the PROXY header to be received.
// If zero, timeout is disabled. Default is 5s.
Timeout caddy.Duration `json:"timeout,omitempty"`
// Allow is an optional list of CIDR ranges to
// allow/require PROXY headers from.
Allow []string `json:"allow,omitempty"`
rules []proxyprotocol.Rule
}
// Provision sets up the listener wrapper.
func (pp *ListenerWrapper) Provision(ctx caddy.Context) error {
rules := make([]proxyprotocol.Rule, 0, len(pp.Allow))
for _, s := range pp.Allow {
_, n, err := net.ParseCIDR(s)
if err != nil {
return fmt.Errorf("invalid subnet '%s': %w", s, err)
}
rules = append(rules, proxyprotocol.Rule{
Timeout: time.Duration(pp.Timeout),
Subnet: n,
})
}
pp.rules = rules
return nil
}
// WrapListener adds PROXY protocol support to the listener.
func (pp *ListenerWrapper) WrapListener(l net.Listener) net.Listener {
pl := proxyprotocol.NewListener(l, time.Duration(pp.Timeout))
pl.SetFilter(pp.rules)
return pl
}

Some files were not shown because too many files have changed in this diff Show More