Compare commits

...

163 Commits

Author SHA1 Message Date
a 3f1ff118f8 noot 2024-06-18 23:48:21 -05:00
a 4d40619aa4 Merge branch 'caddytest-2' of github.com:elee1766/caddy into caddytest-2 2024-06-18 23:45:57 -05:00
a 3c591ecac9 noot 2024-06-18 23:45:54 -05:00
a 73854014d9 Merge branch 'master' into caddytest-2 2024-06-18 22:48:21 -05:00
a c0d9a2383e noot 2024-06-18 22:36:02 -05:00
a 7bc7e1680e noot 2024-06-18 21:57:46 -05:00
a edf4168c8e noot 2024-06-18 21:18:38 -05:00
a 926fb82f6b noot 2024-06-18 21:18:07 -05:00
a 841fe2544d noot 2024-06-18 21:17:51 -05:00
a b19feec6dc noot 2024-06-18 21:01:15 -05:00
a 41a4320fd3 noot 2024-06-18 20:14:51 -05:00
a b491fc5d6c noot 2024-06-18 20:11:56 -05:00
a 01cb878087 noot 2024-06-18 20:08:38 -05:00
a b98c89fbb6 noot 2024-06-18 19:46:43 -05:00
a 2619271a5c noot 2024-06-18 19:44:05 -05:00
a 93a1853022 noot 2024-06-18 18:16:33 -05:00
Matthew Holt 99dcdf7e42 caddyhttp: Convert IDNs to ASCII when provisioning Host matcher 2024-06-18 14:44:05 -06:00
Jason Yuan fab6375a8b reverseproxy: add Max-Age option to sticky cookie (#6398)
* reverseproxy: add Max-Age option to sticky cookie

* Update selectionpolicies.go

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

* Update selectionpolicies.go

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

---------

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2024-06-15 07:50:31 -06:00
a aca4002fd8 caddyfile: Pass blocks to import for snippets (#6130)
* a

* a

* a

* a

* a

* a
2024-06-14 11:27:51 -06:00
Ririsoft 8e0d3e1ec5 logging: set file mode when the file already exist (#6391)
101d3e7 introduced a configuration option to set the log file mode.
This option was not taken into account if the file already exists,
making users having to delete their logs to have new logs created
with the right mode.
2024-06-12 15:17:46 -06:00
Omar Ramadan d85cc2ec10 logging: Customizable zap cores (#6381) 2024-06-10 09:03:24 -06:00
Will Norris 04fb9fe87f go.mod: update tscert package (#6384)
The latest tscert allows callers to provide a custom http.Transport for
calling Tailscale's local API.

Updates tailscale/caddy-tailscale#66
2024-06-10 07:28:30 -06:00
Ririsoft 0bc27e5fb1 logging: fix file mode configuration parsing (#6383)
Commit 101d3e7 introduced file mode setting,
but was missing a JSON Marshaller so that
CaddyFile can be converted to JSON safely.
2024-06-08 11:34:18 -06:00
Andreas Kohn 9be4f194e0 caddyhttp: Write header if needed in responseRecorder.WriteResponse (#6380) 2024-06-07 07:25:36 -06:00
Andreas Kohn a10117f8bd core: Split run into a public ProvisionContext and a private method (#6378)
* Split `run` into a public `BuildContext` and a private part

`BuildContext` can be used to set up a caddy context from a config, but not start any listeners
or active components: The returned context has the configured apps provisioned, but otherwise is
inert.

This is EXPERIMENTAL: Minimally it's missing documentation and the example for how this can be
used to run unit tests.

* Use the config from the context

The config passed into `BuildContext` can be nil, in which case `BuildContext` will just make one
up that works. In either case that will end up in the finished context.

* Rename `BuildContext` to `ProvisionContext` to better match the function

* Hide the `replaceAdminServer` parts

The admin server is a global thing, and in the envisioned use case for `ProvisionContext`
shouldn't actually exist. Hide this detail in a private `provisionContext` instead, and
only expose it publicly with `replaceAdminServer` set to `false`.

This should reduce foot-shooting potential further; in addition the documentation comment
now clearly spells out that the exact interface and implementation details of `ProvisionContext`
are experimental and subject to change.
2024-06-06 14:36:06 -06:00
Ririsoft 101d3e7407 logging: Customize log file permissions (#6314)
Adding a "mode" option to overwrite the default logfile permissions.
Default remains "0600" which is the one currently used by lumberjack.
2024-06-06 08:33:34 -06:00
Matthew Holt 3f1add6c9f events: Getters for event info (close #6377) 2024-06-06 07:11:28 -06:00
Mohammed Al Sahaf 5db2f81695 ci: add version key for .goreleaser.yml (#6376)
Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2024-06-06 11:33:19 +03:00
Mohammed Al Sahaf 243351b2b1 cmd: remove zealous check of Caddyfile auto-detection (#6370)
* cmd: remove zealous check of Caddyfile auto-detection

* add test case

* remove redundant check, add comment

* one more case
2024-06-05 08:57:15 -06:00
Matt Holt 198f4385d2 caddyhttp: Add test cases to corpus (#6374)
* caddyhttp: Add test case to corpus

* One more test case

* Clean up stray comment

* More tests
2024-06-04 14:23:55 -06:00
Andreas Kohn e7ecc7ede2 Make it possible to configure the DisableStorageCheck setting for certmagic (#6368)
See discussion about this setting in https://github.com/caddyserver/certmagic/issues/201
2024-06-04 07:00:15 -06:00
Mohammed Al Sahaf 7088605cc1 cmd: fix regression in auto-detect of Caddyfile (#6362)
* cmd: fix regression in auto-detect of Caddyfile

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* fix typo

Co-authored-by: Git'Fellow <12234510+solracsf@users.noreply.github.com>

* add tests

* address review comments

---------

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
Co-authored-by: Git'Fellow <12234510+solracsf@users.noreply.github.com>
2024-06-02 11:40:56 +00:00
Mohammed Al Sahaf 15faeacb60 cmd: fix auto-detetction of .caddyfile extension (#6356)
* cmd: fix auto-detetction of .caddyfile extension

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* move conditions around and add clarifying comment

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* reject ambiguous config file name

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

---------

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2024-06-02 03:49:38 +00:00
Will Norris f8a2c60297 caddyhttp: properly sanitize requests for root path (#6360)
SanitizePathJoin protects against directory traversal attacks by
checking for requests whose URL path look like they are trying to
request something other than a local file, and returns the root
directory in those cases.

The method is also careful to ensure that requests which contain a
trailing slash include a trailing slash in the returned value.  However,
for requests that contain only a slash (requests for the root path), the
IsLocal check returns early before the matching trailing slash is
re-added.

This change updates SanitizePathJoin to only perform the
filepath.IsLocal check if the cleaned request URL path is non-empty.

---

This change also updates the existing SanitizePathJoin tests to use
filepath.FromSlash rather than filepath.Join. This makes the expected
value a little easier to read, but also has the advantage of not being
processed by filepath.Clean like filepath.Join is. This means that the
exact expect value will be compared, not the result of first cleaning
it.

Fixes #6352
2024-06-02 03:40:59 +00:00
Matthew Holt 01308b4bae I'm so tired of typos 2024-06-01 20:43:35 -06:00
Matthew Holt b7280e6949 caddytls: Implement certmagic.RenewalInfoGetter
Fixes ARI errors reported here:
https://caddy.community/t/error-in-logs-with-updating-ari-after-upgrading-to-caddy-v2-8-1/24320
2024-06-01 18:02:49 -06:00
dependabot[bot] a63767d3f8 build(deps): bump golangci/golangci-lint-action from 5 to 6 (#6361)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 5 to 6.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-02 02:26:31 +03:00
Francis Lavoie 40c582ce82 caddyhttp: Fix merging consecutive client_ip or remote_ip matchers (#6350) 2024-05-30 07:32:17 -06:00
Anton Kovalenko a52917a37d core: MkdirAll appDataDir in InstanceID with 0o700 (#6340)
appDataDir components should be searchable (u+x) when they are
created, or else Caddy is unable to start with an empty HOME.
2024-05-30 10:38:09 +00:00
Ranveer Avhad e6f46c8d78 acmeserver: Add sign_with_root for Caddyfile (#6345)
* Added sign_with_root option available in the Caddyfile

* Added tests for sign_with_root to validate the adapted JSON config
2024-05-27 20:06:54 -04:00
Francis Lavoie f6d2c293e7 caddyfile: Reject global request matchers earlier (#6339) 2024-05-23 20:06:16 -06:00
Matthew Holt 2ce5c65269 core: Fix bug in AppIfConfigured (fix #6336) 2024-05-22 18:47:03 -06:00
a 61917c3443 fix a typo (#6333) 2024-05-21 18:41:41 -04:00
Francis Lavoie 224316eaec autohttps: Move log WARN to INFO, reduce confusion (#6185)
* autohttps: Move log WARN to INFO, reduce confusion

* Change implicit condition back to WARN

---------

Co-authored-by: Matthew Holt <mholt@users.noreply.github.com>
2024-05-20 13:14:39 -06:00
Matt Holt 5f6758dab5 reverseproxy: Support HTTP/3 transport to backend (#6312)
Closes #5086
2024-05-20 13:06:43 -06:00
Francis Lavoie a6a45ff6c5 context: AppIfConfigured returns error; consider not-yet-provisioned modules (#6292)
* context: Add new `AppStrict()` method to avoid instantiating empty apps

* Rename AppStrict -> AppIfConfigured

---------

Co-authored-by: Matthew Holt <mholt@users.noreply.github.com>
2024-05-20 11:14:58 -06:00
Matthew Holt 73e094e1dd Fix lint error about deprecated method in smallstep/certificates/authority 2024-05-20 10:56:25 -06:00
Matthew Holt d79c0f0dec go.mod: Upgrade dependencies 2024-05-20 10:35:27 -06:00
Will Norris db3e19b7b5 caddytls: fix permission requirement with AutomationPolicy (#6328)
Certificate automation has permission modules that are designed to
prevent inappropriate issuance of unbounded or wildcard certificates.
When an explicit cert manager is used, no additional permission should
be necessary. For example, this should be a valid caddyfile:

    https:// {
      tls {
        get_certificate tailscale
      }
      respond OK
    }

This is accomplished when provisioning an AutomationPolicy by tracking
whether there were explicit managers configured directly on the policy
(in the ManagersRaw field). Only when a number of potentially unsafe
conditions are present AND no explicit cert managers are configured is
an error returned.

The problem arises from the fact that ctx.LoadModule deletes the raw
bytes after loading in order to save memory. The first time an
AutomationPolicy is provisioned, the ManagersRaw field is populated, and
everything is fine.

An AutomationPolicy with no subjects is treated as a special "catch-all"
policy. App.createAutomationPolicies ensures that this catch-all policy
has an ACME issuer, and then calls its Provision method again because it
may have changed. This second time Provision is called, ManagesRaw is no
longer populated, and the permission check fails because it appears as
though the policy has no explicit managers.

Address this by storing a new boolean on AutomationPolicy recording
whether it had explicit cert managers configured on it.

Also fix an inverted boolean check on this value when setting
failClosed.

Updates #6060
Updates #6229
Updates #6327

Signed-off-by: Will Norris <will@tailscale.com>
2024-05-20 09:48:59 -06:00
Will Norris 1fc151faec caddytls: remove ClientHelloSNICtxKey (#6326) 2024-05-18 22:47:46 -04:00
Matt Holt 9ba999141b caddyhttp: Trace individual middleware handlers (#6313)
* caddyhttp: Trace individual middleware handlers

* Fix typo
2024-05-18 14:48:42 -06:00
deneb f98f449f05 templates: Add pathEscape template function and use it in file browser (#6278)
* use url.PathEscape in file-server browse template

- add `pathEscape` to c.tpl.Funcs, using `url.PathEscape`
- use `pathEscape` in browse.html in place of `replace`

* document `pathEscape`

* Remove unnecessary pipe of img src to `html`
2024-05-18 12:55:36 -06:00
Will Norris e66040a6f0 caddytls: set server name in context (#6324)
Set the requested server name in a context value for CertGetter
implementations to use. Pass ctx to tscert.GetCertificateWithContext.

Signed-off-by: Will Norris <will@tailscale.com>
2024-05-18 03:52:19 -06:00
Mohammed Al Sahaf 44860482d2 chore: downgrade minimum Go version in go.mod (#6318)
* chore: downgrade minimum Go version in go.mod

* Upgrade certmagic and zerossl

---------

Co-authored-by: Matthew Holt <mholt@users.noreply.github.com>
2024-05-15 19:28:34 +00:00
Mohammed Al Sahaf 4c90f1427f caddytest: normalize the JSON config (#6316)
* caddytest: normalize the JSON config
2024-05-14 07:50:14 +00:00
Kévin Dunglas fb63e2e40c caddyhttp: New experimental handler for intercepting responses (#6232)
* feat: add generic response interceptors

* fix: cs

* rename intercept

* add some docs

* @francislavoie review (first round)

* Update modules/caddyhttp/intercept/intercept.go

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

* shorthands: ir to resp

* mark exported symbols as experimental

---------

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2024-05-13 17:38:18 +00:00
Matthew Holt 583c585c81 httpcaddyfile: Set challenge ports when http_port or https_port are used 2024-05-11 21:39:56 -06:00
Aziz Rmadi 4356635d12 logging: Add support for additional logger filters other than hostname (#6082)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2024-05-11 13:31:44 +00:00
Matthew Holt 4af38e5ac8 caddyhttp: Log 4xx as INFO; 5xx as ERROR (close #6106) 2024-05-10 15:52:50 -06:00
Matthew Holt 399186abfc Second half of 6dce493
Not sure how it got unstaged
2024-05-10 15:51:28 -06:00
Matthew Holt 6dce4934f0 caddyhttp: Alter log message when request is unhandled (close #5182) 2024-05-10 15:49:34 -06:00
Francis Lavoie 874d0ce822 chore: Bump Go version in CI (#6310) 2024-05-10 14:56:18 +00:00
Matthew Holt abdf1ae15c go.mod: go 1.22.3
Seeing if this assists with some Go tooling logic
2024-05-10 08:32:44 -06:00
Viktor Szépe d7e3a1974b Fix typos (#6311)
* Fix typos

* Revert

* Revert to "htlm"

* fix indentations
2024-05-10 08:08:54 -06:00
WeidiDeng e60148ecc3 reverseproxy: Pointer to struct when loading modules; remove LazyCertPool (#6307)
* use pointer when loading modules

* change method to pointer type and remove LazyCertPool

* remove lazy pool test

* remove yet another lazy pool test
2024-05-08 19:13:37 -06:00
Matthew Penner 0b5720faa5 tracing: add trace_id var (http.vars.trace_id placeholder) (#6308) 2024-05-08 16:40:40 -06:00
Matthew Holt dd203ad41f go.mod: CertMagic v0.21.0 2024-05-07 10:17:10 -06:00
Ali Asgar b2b29dcd49 reverseproxy: Implement health_follow_redirects (#6302)
* added health_follow_redirect in active health checks

* chore: code format

* chore: refactore reversproxy healthcheck redirect variable name and description of the same

* chore: formatting

* changed reverse proxy health check status code range to be between 200-299

* chore: formatting

---------

Co-authored-by: aliasgar <joancena1268@mail.com>
2024-05-07 08:40:15 -06:00
Florian Apolloner c97292b255 caddypki: Allow use of root CA without a key. Fixes #6290 (#6298)
* Allow usage of root CA without a key. Fixes #6290

* Update modules/caddypki/crypto.go

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-05-07 03:38:26 +00:00
Matthew Holt b52271061d go.mod: Upgrade to quic-go v0.43.1 2024-05-06 20:15:43 -06:00
Mohammed Al Sahaf d05d715a00 reverseproxy: HTTP transport: fix PROXY protocol initialization (#6301) 2024-05-06 20:02:12 -06:00
Matthew Holt 8d7ac18402 caddytls: Ability to drop connections (close #6294) 2024-05-06 19:59:42 -06:00
dependabot[bot] 7e2510ef43 build(deps): bump golangci/golangci-lint-action from 4 to 5 (#6289)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 4 to 5.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 03:09:19 +03:00
Francis Lavoie feeb6af403 httpcaddyfile: Fix expression matcher shortcut in snippets (#6288) 2024-05-01 07:43:05 -04:00
Matt Holt d129ae6aec caddytls: Evict internal certs from cache based on issuer (#6266)
* caddytls: Evict internal certs from cache based on issuer

During a config reload, we would keep certs in the cache fi they were used  by the next config. If one config uses InternalIssuer and the other uses a public CA, this behavior is problematic / unintuitive, because there is a big difference between private/public CAs.

This change should ensure that internal issuers are considered when deciding whether to keep or evict from the cache during a reload, by making them distinct from each other and certs from public CAs.

* Make sure new TLS app manages configured certs

* Actually make it work
2024-04-30 16:15:54 -06:00
Mohammed Al Sahaf 87c7127c28 chore: add warn logs when using deprecated fields (#6276) 2024-04-27 15:51:00 -04:00
Matthew Holt 2fc620d38d caddyhttp: Fix linter warning about deprecation 2024-04-27 12:41:17 -06:00
Matthew Holt a46ff50a1c go.mod: Upgrade to quic-go v0.43.0 2024-04-27 12:01:30 -06:00
Matthew Holt cabb5d71c4 fileserver: Set "Vary: Accept-Encoding" header (see #5849) 2024-04-26 19:38:45 -06:00
Matthew Holt ba5811467a events: Add debug log 2024-04-26 18:59:08 -06:00
WeidiDeng 1b9042bcdd reverseproxy: handle buffered data during hijack (#6274) 2024-04-26 09:09:18 -06:00
Mohammed Al Sahaf 4d6370bf92 ci: remove android and plan9 from cross-build workflow (#6268) 2024-04-24 17:31:40 -04:00
Mohammed Al Sahaf c6eb186064 run golangci-lint run --fix --fast (#6270) 2024-04-24 15:17:23 -06:00
clauverjat 76c4cf5a56 caddytls: Option to configure certificate lifetime (#6253)
* Add option to configure certificate lifetime

* Bump CertMagic dep to latest master commit

* Apply suggestions and ran go mod tidy

* Update modules/caddytls/acmeissuer.go

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

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-04-24 14:35:14 -06:00
Francis Lavoie 797973944f replacer: Implement file.* global replacements (#5463)
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
Co-authored-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2024-04-24 16:26:18 -04:00
Matt Holt 6d97d8d87b caddyhttp: Address some Go 1.20 features (#6252)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2024-04-24 00:05:57 +00:00
Matthew Holt d404005339 Quell linter (false positive) 2024-04-23 11:55:37 -06:00
Aziz Rmadi 868af6a062 reverse_proxy: Add grace_period for SRV upstreams to Caddyfile (#6264) 2024-04-23 07:12:57 -06:00
Mohammed Al Sahaf d2668cdbb0 doc: add verifier in ClientAuthentication caddyfile marshaler doc (#6263) 2024-04-23 07:01:54 -06:00
Matthew Holt 6a02999054 caddytls: Add Caddyfile support for on-demand permission module (close #6260) 2024-04-22 15:47:09 -06:00
Matthew Holt 9f97df2275 reverseproxy: Remove long-deprecated buffering properties
They've been deprecated for over a year and we printed warnings during that time.
2024-04-22 15:34:14 -06:00
Matthew Holt d93e027e01 reverseproxy: Reuse buffered request body even if partially drained
Previous commit only works when the backends don't read any of the body first.
2024-04-22 15:22:50 -06:00
Matthew Holt 613d544a47 reverseproxy: Accept EOF when buffering
Before this change, a read of size (let's say) < 10, into a buffer of size 10, will return EOF because we're using CopyN to limit to the size of the buffer. That resulted in the body being read from later, which should only happen if it couldn't fit in the buffer.

With this change, the body is properly NOT set when it can all fit in the buffer.
2024-04-22 13:12:10 -06:00
Francis Lavoie 726a9a8fde logging: Fix default access logger (#6251)
* logging: Fix default access logger

* Simplify logic, remove retry without port, reject config with port, docs

* Nil check
2024-04-22 06:33:07 -06:00
Matthew Holt d00824f4a6 fileserver: Improve Vary handling (#5849) 2024-04-19 13:43:13 -06:00
Mohammed Al Sahaf 8f87c5d993 cmd: Only validate config is proper JSON if config slice has data (#6250)
* cmd: fix error when running without config

* ci: add smoke test
2024-04-18 15:40:12 -06:00
Mohammed Al Sahaf c6673ad4d8 staticresp: Use the evaluated response body for sniffing JSON content-type (#6249) 2024-04-18 20:31:00 +00:00
Matthew Holt 9ab09433de encode: Slight fix for the previous commit 2024-04-17 19:59:10 -06:00
Matthew Holt 3067074d9c encode: Improve Etag handling (fix #5849)
We also improve Last-Modified handling in the file server.
Both changes should be more compliant with RFC 9110.
2024-04-17 19:12:03 -06:00
Matthew Holt 3efda6fb3a httpcaddyfile: Skip automate loader if disable_certs is specified (fix #6148) 2024-04-17 12:26:03 -06:00
Francis Lavoie 9cd472c031 caddyfile: Populate regexp matcher names by default (#6145)
* caddyfile: Populate regexp matcher names by default

* Some lint cleanup that my VSCode complained about

* Pass down matcher name through expression matcher

* Compat with #6113: fix adapt test, set both styles in replacer
2024-04-17 12:19:14 -06:00
WeidiDeng e0daa39cd3 caddyhttp: record num. bytes read when response writer is hijacked (#6173)
* record the number of bytes read when response writer is hijacked

* record body size when not nil
2024-04-17 15:00:37 +00:00
Francis Lavoie 70953e873a caddyhttp: Support multiple logger names per host (#6088)
* caddyhttp: Support multiple logger names per host

* Lint

* Add adapt test

* Implement "string or array" parsing, keep original `logger_names`

* Rewrite adapter test to be more representative of the usecase
2024-04-16 22:26:18 +00:00
coderwander eafc875ea9 chore: fix some typos in comments (#6243) 2024-04-16 04:10:11 +00:00
dev-polymer 03e0a010d1 encode: Configurable compression level for zstd (#6140)
* Add zstd compression level support

* Refactored zstd levels to string arguments

fastest, default, better, best

* Add comment with list of all available levels

* Corrected data types for config

---------

Co-authored-by: Evgeny Blinov <e.a.blinov@gmail.com>
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-04-16 00:21:52 +00:00
Aziz Rmadi 3609a4af75 caddytls: Remove shim code supporting deprecated lego-dns (#6231)
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-04-15 21:26:56 +00:00
Mohammed Al Sahaf 26748d06b4 connection policy: add local_ip matcher (#6074)
* connection policy: add `local_ip`

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

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-04-15 21:13:24 +03:00
WeidiDeng b40cacf5ce reverseproxy: Wait for both ends of websocket to close (#6175) 2024-04-15 11:37:37 -06:00
Matt Holt 81413caea2 caddytls: Upgrade ACMEz to v2; support ZeroSSL API; various fixes (#6229)
* WIP: acmez v2, CertMagic, and ZeroSSL issuer upgrades

* caddytls: ZeroSSLIssuer now uses ZeroSSL API instead of ACME

* Fix go.mod

* caddytls: Fix automation related to managers (fix #6060)

* Fix typo (appease linter)

* Fix HTTP validation with ZeroSSL API
2024-04-13 21:31:43 -04:00
Matthew Holt dc9dd2e4b3 caddytls: Still provision permission module if ask is specified
Only needed for JSON configs, and only temporarily as the ask property is deprecated and will be removed.
2024-04-13 17:08:11 -06:00
Aziz Rmadi 567d96c624 fileserver: read etags from precomputed files (#6222) 2024-04-13 06:49:55 -04:00
Matthew Holt 5d8b45c9fb fileserver: Escape # and ? in img src (fix #6237) 2024-04-12 15:59:59 -06:00
Aziz Rmadi 0b381eb766 reverseproxy: Implement modular CA provider for TLS transport (#6065)
* added new modular ca providers to caddy tls HttpTransport

* reverse-proxy, httptransport: added tests and caddyfile support for ca module

---------

Co-authored-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2024-04-12 07:19:14 -06:00
Matthew Holt 83ef61de10 caddyhttp: Apply auto HTTPS redir to all interfaces (fix #6226) 2024-04-12 06:04:47 -06:00
Matthew Holt e1f4b83ffa cmd: Fix panic related to config filename (fix #5919) 2024-04-11 17:04:43 -06:00
Omar Hussein 185ed6fe7c cmd: Assume Caddyfile based on filename prefix and suffix (#5919)
This can be helpful if editors only consider file extensions for certain features.

* added special case support for caddyfile suffix, case insensitive

* Update cmd/main.go

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

* skip caddyfile adapter for registered file extensions

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-04-11 15:28:16 -06:00
Hayder 4a0492f3e1 admin: Make Etag a header, not a trailer (#6208)
* Making eTags a header not a trailer

* Checked the write

* Fixed typo

* Corrected comment

* Added sync Pool

* Changed control flow of buffer reset / putting and changed error code

* Switched from interface{} to any in bufferPool
2024-04-11 21:19:24 +00:00
Hugues Lismonde 654a3bb090 caddyhttp: remove duplicate strings.Count in path matcher (fixes #6233) (#6234) 2024-04-10 08:38:10 -06:00
danish-mehmood f4840cfeb8 caddyconfig: Use empty struct instead of bool in map (close #6224) (#6227) 2024-04-08 17:12:35 -06:00
Ed Pelc a4a64a6f6e gitignore: Add rule for caddyfile.go (#6225) 2024-04-07 02:30:00 +00:00
Hassan Ila 88d65967b5 chore: Fix broken links in README.md (#6223) 2024-04-05 23:48:43 -04:00
Francis Lavoie 1c4a807667 chore: Upgrade some dependencies (#6221) 2024-04-04 18:27:52 -04:00
kylosus 45132c5b24 caddyhttp: Add plaintext response to file_server browse (#6093)
* Added plaintext support to file_server browser

This commit is twofold: First it adds a new optional
field, `return_type`, to `browser` for setting the
default format of the returned index (html, json or plaintext).
This is used when the `Accept` header is set to `/*`.

Second, it adds a preliminary `text/plain`
support to the `file_server` browser that
returns a text representation of the file
system, when an `Accept: text/plain` header
is present, with the behavior discussed above.

* Added more details and better formatting to plaintext browser

* Replaced returnType conditions with a switch statement

* Simplify

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-04-01 18:12:40 +00:00
Hayder 1217449609 admin: Use xxhash for etag (#6207) 2024-03-30 07:24:50 -06:00
reallylowest e0bf179c1a modules: fix some typo in conments (#6206)
Signed-off-by: reallylowest <sunjinping@outlook.com>
2024-03-30 02:45:42 +00:00
Matthew Holt 7b48ce0e7e caddyhttp: Replace sensitive headers with REDACTED (close #5669) 2024-03-29 14:42:20 -06:00
WeidiDeng 924010cd3d caddyhttp: close quic connections when server closes (#6202)
* close quic connections when server closes

* fix lint

* add comment about CloseGracefully
2024-03-29 11:51:46 -06:00
Hayder 74949fb091 reverseproxy: Use xxhash instead of fnv32 for LB (#6203)
* Added Faster Non-cryptographic Hash Function for Load Balancing

* Ran golangci-lint

* Updated hash version and hash return type
2024-03-29 10:56:18 -06:00
Emily ddb1d2c2b1 caddyhttp: add http.request.local{,.host,.port} placeholder (#6182)
* caddyhttp: add `http.request.local{,.host,.port}` placeholder

This is the counterpart of `http.request.remote{,.host,.port}`.

`http.request.remote` operates on the remote client's address, while
`http.request.local` operates on the address the connection arrived on.

Take the following example:

- Caddy serving on `203.0.113.1:80`
- Client on `203.0.113.2`

`http.request.remote.host` would return `203.0.113.2` (client IP)

`http.request.local.host` would return `203.0.113.1` (server IP)
`http.request.local.port` would return `80` (server port)

I find this helpful for debugging setups with multiple servers and/or
multiple network paths (multiple IPs, AnyIP, Anycast).

Co-authored-by: networkException <git@nwex.de>

* caddyhttp: add unit test for `http.request.local{,.host,.port}`

* caddyhttp: add integration test for `http.request.local.port`

* caddyhttp: fix `http.request.local.host` placeholder handling with unix sockets

The implementation matches the one of `http.request.remote.host` now and
returns the unix socket path (just like `http.request.local` already did)
instead of an empty string.

---------

Co-authored-by: networkException <git@nwex.de>
2024-03-27 21:36:53 +00:00
Mohammed Al Sahaf 7f227b9d39 chore: upgrade deps (#6198) 2024-03-27 14:24:18 -04:00
sellskin 0dd0487eba chore: remove repetitive word (#6193)
Signed-off-by: sellskin <mydesk@yeah.net>
2024-03-25 09:05:45 -06:00
Aziz Rmadi db9d167354 Added a null check to avoid segfault on rewrite query ops (#6191) 2024-03-23 01:51:34 -04:00
Aziz Rmadi 29f57faa86 rewrite: uri query replace operation (#6165)
* Implemented query replace oeration

* Modified replace operation to use regexes in caddyfile

* Added more tests to uri query operations
2024-03-22 02:23:42 +00:00
Mohammed Al Sahaf 0c01547037 logging: support ms duration format and add docs (#6187) 2024-03-21 22:17:09 -04:00
Mohammed Al Sahaf e7336cc3bf replacer: use RWMutex to protect static provider (#6184) 2024-03-21 18:15:18 +00:00
Francis Lavoie 97a56d860a caddyhttp: Allow header replacement with empty string (#6163) 2024-03-21 17:29:32 +00:00
Francis Lavoie d13258423d vars: Make nil values act as empty string instead of "<nil>" (#6174) 2024-03-21 11:21:53 -06:00
Marten Seemann 32f7dd44ae chore: Update quic-go to v0.42.0 (#6176)
* update quic-go to v0.42.0

* use a rate limiter to control QUIC source address verification

* Lint

* remove deprecated ListenQUIC

* remove number of requests tracking

* increase the number of handshakes before source address verification is needed

* remove references to request counters

* remove deprecated listen*

---------

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
Co-authored-by: WeidiDeng <weidi_deng@icloud.com>
2024-03-21 10:56:10 -06:00
Francis Lavoie 63d597c09d caddyhttp: Accept XFF header values with ports, when parsing client IP (#6183) 2024-03-21 10:54:25 -06:00
Sam Ottenhoff e65b97f55b reverseproxy: configurable active health_passes and health_fails (#6154)
* reverseproxy: active health check allows configurable health_passes and health_fails

* Need to reset counters after recovery

* rename methods to be more clear that these are coming from active health checks

* do not export methods
2024-03-20 11:13:35 -06:00
Justin Angel a9768d2fde reverseproxy: Configurable forward proxy URL (#6114)
Co-authored-by: WeidiDeng <weidi_deng@icloud.com>
2024-03-18 04:07:25 +00:00
jbrown-stripe 52822a41cb caddyhttp: upgrade to cel v0.20.0 (#6161)
* upgrade to cel v0.20.0

* Attempt to address feedback and fix linter

* Let's try this

* Take that, you linter!

* Oh there's more

---------


Co-authored-by: Francis Lavoie <lavofr@gmail.com>
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
Co-authored-by: Tristan Swadell @TristonianJones
2024-03-13 21:32:42 -06:00
Francis Lavoie 5b5f8feaf7 chore: Bump Chroma to v2.13.0, includes new Caddyfile lexer (#6169) 2024-03-12 12:07:23 +03:00
WeidiDeng c93e30454f caddyhttp: suppress flushing if the response is being buffered (#6150)
* suppress flushing if the response is being buffered

* fix lint

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-03-11 20:03:20 +00:00
WeidiDeng 1bd598e90c chore: encode: use FlushError instead of Flush (#6168)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2024-03-10 23:04:35 -04:00
WeidiDeng e698ec5139 encode: write status immediately when status code is informational (#6164) 2024-03-10 10:49:49 -04:00
Steffen Busch c27425ef5d httpcaddyfile: Keep deprecated skip_log in directive order (#6153) 2024-03-07 14:34:01 -05:00
Francis Lavoie 258d906140 httpcaddyfile: Add RegisterDirectiveOrder function for plugin authors (#5865)
* httpcaddyfile: Add `RegisterDirectiveOrder` function for plugin authors

* Set up Positional enum

* Linter doesn't like a switch on an enum with default

* Update caddyconfig/httpcaddyfile/directives.go

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

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-03-06 19:41:45 +00:00
Aziz Rmadi 69290d232d rewrite: Implement uri query operations (#6120)
* Implemented basic uri query operations

* Added support for query operations block

* Applied Replacer on all query keys and values

* Implemented rename query key opration

* Rewrite struct: Changed QueryOperations field to Query and comments cleanup

* Cleaned up comments, changed the order of operations and added more tests

* Changed order of fields in queryOps struct to match the operations order
2024-03-06 10:08:46 -05:00
huajin tong 277472d081 fix struct names (#6151)
Signed-off-by: thirdkeyword <fliterdashen@gmail.com>
2024-03-06 13:53:03 +00:00
Francis Lavoie 5a4374bea0 fileserver: Preserve query during canonicalization redirect (#6109)
* fileserver: Preserve query during canonicalization redirect

* Clarify that only a path should be passed
2024-03-05 22:51:26 -07:00
Francis Lavoie 0d44e3ecba logging: Implement log_append handler (#6066)
* logging: Implement `extra_log` handler

* Rename to `log_append`

* Rename `skip_log` to `log_skip`

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-03-05 17:03:59 -07:00
Francis Lavoie 2a78c9c5e4 httpcaddyfile: Allow nameless regexp placeholder shorthand (#6113)
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-03-05 23:37:14 +00:00
Francis Lavoie 01d5568b20 logging: Implement append encoder, allow flatter filters config (#6069)
* logging: Implement `add` encoder

* Allow flatter config structure for `filter` & `add`

* Rename to append

* govulncheck was unhappy
2024-03-05 16:24:32 -07:00
Mohammed Al Sahaf 1f4a6fa7e7 ci: fix the integration test TestLeafCertLoaders (#6149) 2024-03-06 02:09:13 +03:00
Francis Lavoie 5ed8689629 vars: Allow overriding http.auth.user.id in replacer as a special case (#6108) 2024-03-05 22:25:38 +00:00
Aziz Rmadi 3ae07a73dc caddytls: clientauth: leaf verifier: make trusted leaf certs source pluggable (#6050)
* Made trusted leaf certificates pluggable into the tls.client_auth.leaf
module

* Added leaf loaders modules: file, folder, pem aand storage

* Cleaned implementation of leaf cert loader modules

* Added tests for leaf certs file and folder loaders

* cmd: fix the output of the `Usage` section (#6138)

* core: OnExit hooks (#6128)

* core: OnExit callbacks

* core: Process-global OnExit callbacks

* ci: bump golangci/golangci-lint-action from 3 to 4 (#6141)

Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3 to 4.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Added more leaf certificate loaders tests and cleaned up code

* Modified leaf cert loaders json field names and cleaned up storage loader comment

* Update modules/caddytls/leaffileloader.go

* Update LeafStorageLoader certificates field name

* Upgraded  protobuf version

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Mohammed Al Sahaf <msaa1990@gmail.com>
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-05 14:55:37 -07:00
Francis Lavoie e473ae6803 cmd: Adjust config load logs/errors (#6032)
* cmd: Adjust config load logs/errors

* Update cmd/main.go

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

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-03-05 19:26:30 +00:00
Matt Holt 72ce78d9af reverseproxy: SRV dynamic upstream failover (#5832)
* Implement grace period, but probably needs sync

* Update cached freshness value

* D'oh, actually use the grace period

* Fix freshness math
2024-03-05 12:08:31 -07:00
dependabot[bot] 8f8204708a ci: bump golangci/golangci-lint-action from 3 to 4 (#6141)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3 to 4.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-02 02:38:57 +03:00
Matt Holt 46c5db92da core: OnExit hooks (#6128)
* core: OnExit callbacks

* core: Process-global OnExit callbacks
2024-03-01 09:57:05 -07:00
Mohammed Al Sahaf de4959fe7b cmd: fix the output of the Usage section (#6138) 2024-03-01 19:00:29 +03:00
Mohammed Al Sahaf 03f703a00e caddytls: verifier: caddyfile: re-add Caddyfile support (#6127)
* caddytls: verifier: caddyfile: re-add Caddyfile support

* appease the linter

* caddytls: client_auth: verifier: change namespace to `tls.client_auth.verifier`
2024-02-26 00:13:48 +03:00
183 changed files with 8951 additions and 2864 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ Other menu items:
You can have a huge impact on the project by helping with its code. To contribute code to Caddy, first submit or comment in an issue to discuss your contribution, then open a [pull request](https://github.com/caddyserver/caddy/pulls) (PR). If you're new to our community, that's okay: **we gladly welcome pull requests from anyone, regardless of your native language or coding experience.** You can get familiar with Caddy's code base by using [code search at Sourcegraph](https://sourcegraph.com/github.com/caddyserver/caddy).
We hold contributions to a high standard for quality :bowtie:, so don't be surprised if we ask for revisions&mdash;even if it seems small or insignificant. Please don't take it personally. :blue_heart: If your change is on the right track, we can guide you to make it mergable.
We hold contributions to a high standard for quality :bowtie:, so don't be surprised if we ask for revisions&mdash;even if it seems small or insignificant. Please don't take it personally. :blue_heart: If your change is on the right track, we can guide you to make it mergeable.
Here are some of the expectations we have of contributors:
+8 -2
View File
@@ -33,7 +33,7 @@ jobs:
GO_SEMVER: '~1.21.0'
- go: '1.22'
GO_SEMVER: '~1.22.0'
GO_SEMVER: '~1.22.3'
# Set some variables per OS, usable via ${{ matrix.VAR }}
# OS_LABEL: the VM label from GitHub Actions (see https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories)
@@ -101,6 +101,12 @@ jobs:
run: |
go build -tags nobdger -trimpath -ldflags="-w -s" -v
- name: Smoke test Caddy
working-directory: ./cmd/caddy
run: |
./caddy start
./caddy stop
- name: Publish Build Artifact
uses: actions/upload-artifact@v4
with:
@@ -169,7 +175,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- uses: goreleaser/goreleaser-action@v5
- uses: goreleaser/goreleaser-action@v6
with:
version: latest
args: check
+1 -7
View File
@@ -17,14 +17,12 @@ jobs:
matrix:
goos:
- 'aix'
- 'android'
- 'linux'
- 'solaris'
- 'illumos'
- 'dragonfly'
- 'freebsd'
- 'openbsd'
- 'plan9'
- 'windows'
- 'darwin'
- 'netbsd'
@@ -35,7 +33,7 @@ jobs:
# Set the minimum Go patch version for the given Go minor
# Usable via ${{ matrix.GO_SEMVER }}
- go: '1.22'
GO_SEMVER: '~1.22.0'
GO_SEMVER: '~1.22.3'
runs-on: ubuntu-latest
continue-on-error: true
@@ -69,7 +67,3 @@ jobs:
working-directory: ./cmd/caddy
run: |
GOOS=$GOOS GOARCH=$GOARCH go build -tags nobadger -trimpath -o caddy-"$GOOS"-$GOARCH 2> /dev/null
if [ $? -ne 0 ]; then
echo "::warning ::$GOOS Build Failed"
exit 0
fi
+3 -6
View File
@@ -43,17 +43,14 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '~1.22.0'
go-version: '~1.22.3'
check-latest: true
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v6
with:
version: v1.55
# Workaround for https://github.com/golangci/golangci-lint-action/issues/135
skip-pkg-cache: true
# Windows times out frequently after about 5m50s if we don't set a longer timeout.
args: --timeout 10m
@@ -66,5 +63,5 @@ jobs:
- name: govulncheck
uses: golang/govulncheck-action@v1
with:
go-version-input: '~1.22.0'
go-version-input: '~1.22.3'
check-latest: true
+4 -4
View File
@@ -13,13 +13,13 @@ jobs:
os:
- ubuntu-latest
go:
- '1.21'
- '1.22'
include:
# Set the minimum Go patch version for the given Go minor
# Usable via ${{ matrix.GO_SEMVER }}
- go: '1.21'
GO_SEMVER: '~1.21.0'
- go: '1.22'
GO_SEMVER: '~1.22.3'
runs-on: ${{ matrix.os }}
# https://github.com/sigstore/cosign/issues/1258#issuecomment-1002251233
@@ -106,7 +106,7 @@ jobs:
run: syft version
# GoReleaser will take care of publishing those artifacts into the release
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5
uses: goreleaser/goreleaser-action@v6
with:
version: latest
args: release --clean --timeout 60m
+1
View File
@@ -3,6 +3,7 @@ _gitignore/
Caddyfile
Caddyfile.*
!caddyfile/
!caddyfile.go
# artifacts from pprof tooling
*.prof
+2
View File
@@ -1,3 +1,5 @@
version: 2
before:
hooks:
# The build is done in this particular way to build Caddy in a designated directory named in .gitignore.
+2 -2
View File
@@ -56,7 +56,7 @@
</p>
## [Features](https://caddyserver.com/v2)
## [Features](https://caddyserver.com/features)
- **Easy configuration** with the [Caddyfile](https://caddyserver.com/docs/caddyfile)
- **Powerful configuration** with its [native JSON config](https://caddyserver.com/docs/json/)
@@ -75,7 +75,7 @@
- **Runs anywhere** with **no external dependencies** (not even libc)
- Written in Go, a language with higher **memory safety guarantees** than other servers
- Actually **fun to use**
- So much more to [discover](https://caddyserver.com/v2)
- So much more to [discover](https://caddyserver.com/features)
## Install
+24 -10
View File
@@ -26,7 +26,6 @@ import (
"expvar"
"fmt"
"hash"
"hash/fnv"
"io"
"net"
"net/http"
@@ -41,13 +40,14 @@ import (
"time"
"github.com/caddyserver/certmagic"
"github.com/cespare/xxhash/v2"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func init() {
// The hard-coded default `DefaultAdminListen` can be overidden
// The hard-coded default `DefaultAdminListen` can be overridden
// 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
@@ -474,7 +474,6 @@ func manageIdentity(ctx Context, cfg *Config) error {
// import the caddytls package -- but it works
if cfg.Admin.Identity.IssuersRaw == nil {
cfg.Admin.Identity.IssuersRaw = []json.RawMessage{
json.RawMessage(`{"module": "zerossl"}`),
json.RawMessage(`{"module": "acme"}`),
}
}
@@ -946,7 +945,7 @@ func (h adminHandler) originAllowed(origin *url.URL) bool {
// etagHasher returns a the hasher we used on the config to both
// produce and verify ETags.
func etagHasher() hash.Hash32 { return fnv.New32a() }
func etagHasher() hash.Hash { return xxhash.New() }
// makeEtag returns an Etag header value (including quotes) for
// the given config path and hash of contents at that path.
@@ -954,17 +953,28 @@ func makeEtag(path string, hash hash.Hash) string {
return fmt.Sprintf(`"%s %x"`, path, hash.Sum(nil))
}
// This buffer pool is used to keep buffers for
// reading the config file during eTag header generation
var bufferPool = sync.Pool{
New: func() any {
return new(bytes.Buffer)
},
}
func handleConfig(w http.ResponseWriter, r *http.Request) error {
switch r.Method {
case http.MethodGet:
w.Header().Set("Content-Type", "application/json")
// Set the ETag as a trailer header.
// The alternative is to write the config to a buffer, and
// then hash that.
w.Header().Set("Trailer", "ETag")
hash := etagHasher()
configWriter := io.MultiWriter(w, hash)
// Read the config into a buffer instead of writing directly to
// the response writer, as we want to set the ETag as the header,
// not the trailer.
buf := bufferPool.Get().(*bytes.Buffer)
buf.Reset()
defer bufferPool.Put(buf)
configWriter := io.MultiWriter(buf, hash)
err := readConfig(r.URL.Path, configWriter)
if err != nil {
return APIError{HTTPStatus: http.StatusBadRequest, Err: err}
@@ -973,6 +983,10 @@ func handleConfig(w http.ResponseWriter, r *http.Request) error {
// we could consider setting up a sync.Pool for the summed
// hashes to reduce GC pressure.
w.Header().Set("Etag", makeEtag(r.URL.Path, hash))
_, err = w.Write(buf.Bytes())
if err != nil {
return APIError{HTTPStatus: http.StatusInternalServerError, Err: err}
}
return nil
+96 -45
View File
@@ -20,6 +20,7 @@ import (
"encoding/hex"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"io/fs"
@@ -397,6 +398,58 @@ func unsyncedDecodeAndRun(cfgJSON []byte, allowPersist bool) error {
// will want to use Run instead, which also
// updates the config's raw state.
func run(newCfg *Config, start bool) (Context, error) {
ctx, err := provisionContext(newCfg, start)
if err != nil {
return ctx, err
}
if !start {
return ctx, nil
}
// Provision any admin routers which may need to access
// some of the other apps at runtime
err = ctx.cfg.Admin.provisionAdminRouters(ctx)
if err != nil {
return ctx, err
}
// Start
err = func() error {
started := make([]string, 0, len(ctx.cfg.apps))
for name, a := range ctx.cfg.apps {
err := a.Start()
if err != nil {
// an app failed to start, so we need to stop
// all other apps that were already started
for _, otherAppName := range started {
err2 := ctx.cfg.apps[otherAppName].Stop()
if err2 != nil {
err = fmt.Errorf("%v; additionally, aborting app %s: %v",
err, otherAppName, err2)
}
}
return fmt.Errorf("%s app module: start: %v", name, err)
}
started = append(started, name)
}
return nil
}()
if err != nil {
return ctx, err
}
// now that the user's config is running, finish setting up anything else,
// such as remote admin endpoint, config loader, etc.
return ctx, finishSettingUp(ctx, ctx.cfg)
}
// provisionContext creates a new context from the given configuration and provisions
// storage and apps.
// If `newCfg` is nil a new empty configuration will be created.
// If `replaceAdminServer` is true any currently active admin server will be replaced
// with a new admin server based on the provided configuration.
func provisionContext(newCfg *Config, replaceAdminServer bool) (Context, error) {
// because we will need to roll back any state
// modifications if this function errors, we
// keep a single error value and scope all
@@ -444,7 +497,7 @@ func run(newCfg *Config, start bool) (Context, error) {
}
// start the admin endpoint (and stop any prior one)
if start {
if replaceAdminServer {
err = replaceLocalAdminServer(newCfg)
if err != nil {
return ctx, fmt.Errorf("starting caddy administration endpoint: %v", err)
@@ -491,49 +544,16 @@ func run(newCfg *Config, start bool) (Context, error) {
}
return nil
}()
if err != nil {
return ctx, err
}
return ctx, err
}
if !start {
return ctx, nil
}
// Provision any admin routers which may need to access
// some of the other apps at runtime
err = newCfg.Admin.provisionAdminRouters(ctx)
if err != nil {
return ctx, err
}
// Start
err = func() error {
started := make([]string, 0, len(newCfg.apps))
for name, a := range newCfg.apps {
err := a.Start()
if err != nil {
// an app failed to start, so we need to stop
// all other apps that were already started
for _, otherAppName := range started {
err2 := newCfg.apps[otherAppName].Stop()
if err2 != nil {
err = fmt.Errorf("%v; additionally, aborting app %s: %v",
err, otherAppName, err2)
}
}
return fmt.Errorf("%s app module: start: %v", name, err)
}
started = append(started, name)
}
return nil
}()
if err != nil {
return ctx, err
}
// now that the user's config is running, finish setting up anything else,
// such as remote admin endpoint, config loader, etc.
return ctx, finishSettingUp(ctx, newCfg)
// ProvisionContext creates a new context from the configuration and provisions storage
// and app modules.
// The function is intended for testing and advanced use cases only, typically `Run` should be
// use to ensure a fully functional caddy instance.
// EXPERIMENTAL: While this is public the interface and implementation details of this function may change.
func ProvisionContext(newCfg *Config) (Context, error) {
return provisionContext(newCfg, false)
}
// finishSettingUp should be run after all apps have successfully started.
@@ -715,6 +735,7 @@ func exitProcess(ctx context.Context, logger *zap.Logger) {
logger.Warn("exiting; byeee!! 👋")
exitCode := ExitCodeSuccess
lastContext := ActiveContext()
// stop all apps
if err := Stop(); err != nil {
@@ -736,6 +757,16 @@ func exitProcess(ctx context.Context, logger *zap.Logger) {
}
}
// execute any process-exit callbacks
for _, exitFunc := range lastContext.exitFuncs {
exitFunc(ctx)
}
exitFuncsMu.Lock()
for _, exitFunc := range exitFuncs {
exitFunc(ctx)
}
exitFuncsMu.Unlock()
// shut down admin endpoint(s) in goroutines so that
// if this function was called from an admin handler,
// it has a chance to return gracefully
@@ -748,7 +779,10 @@ func exitProcess(ctx context.Context, logger *zap.Logger) {
} else {
logger.Error("unclean shutdown")
}
os.Exit(exitCode)
// check if we are in test environment, and dont call exit if we are
if flag.Lookup("test.v") == nil && !strings.Contains(os.Args[0], ".test") {
os.Exit(exitCode)
}
}()
if remoteAdminServer != nil {
@@ -774,6 +808,23 @@ var exiting = new(int32) // accessed atomically
// EXPERIMENTAL API: subject to change or removal.
func Exiting() bool { return atomic.LoadInt32(exiting) == 1 }
// OnExit registers a callback to invoke during process exit.
// This registration is PROCESS-GLOBAL, meaning that each
// function should only be registered once forever, NOT once
// per config load (etc).
//
// EXPERIMENTAL API: subject to change or removal.
func OnExit(f func(context.Context)) {
exitFuncsMu.Lock()
exitFuncs = append(exitFuncs, f)
exitFuncsMu.Unlock()
}
var (
exitFuncs []func(context.Context)
exitFuncsMu sync.Mutex
)
// Duration can be an integer or a string. An integer is
// interpreted as nanoseconds. If a string, it is a Go
// time.Duration value such as `300ms`, `1.5h`, or `2h45m`;
@@ -841,7 +892,7 @@ func InstanceID() (uuid.UUID, error) {
if err != nil {
return uuid, err
}
err = os.MkdirAll(appDataDir, 0o600)
err = os.MkdirAll(appDataDir, 0o700)
if err != nil {
return uuid, err
}
+34
View File
@@ -30,6 +30,10 @@ type Dispenser struct {
tokens []Token
cursor int
nesting int
// A map of arbitrary context data that can be used
// to pass through some information to unmarshalers.
context map[string]any
}
// NewDispenser returns a Dispenser filled with the given tokens.
@@ -454,6 +458,34 @@ func (d *Dispenser) DeleteN(amount int) []Token {
return d.tokens
}
// SetContext sets a key-value pair in the context map.
func (d *Dispenser) SetContext(key string, value any) {
if d.context == nil {
d.context = make(map[string]any)
}
d.context[key] = value
}
// GetContext gets the value of a key in the context map.
func (d *Dispenser) GetContext(key string) any {
if d.context == nil {
return nil
}
return d.context[key]
}
// GetContextString gets the value of a key in the context map
// as a string, or an empty string if the key does not exist.
func (d *Dispenser) GetContextString(key string) string {
if d.context == nil {
return ""
}
if val, ok := d.context[key].(string); ok {
return val
}
return ""
}
// isNewLine determines whether the current token is on a different
// line (higher line number) than the previous token. It handles imported
// tokens correctly. If there isn't a previous token, it returns true.
@@ -485,3 +517,5 @@ func (d *Dispenser) isNextOnNewLine() bool {
next := d.tokens[d.cursor+1]
return isNextOnNewLine(curr, next)
}
const MatcherNameCtxKey = "matcher_name"
+1 -2
View File
@@ -17,9 +17,8 @@ package caddyfile
import (
"bytes"
"io"
"slices"
"unicode"
"golang.org/x/exp/slices"
)
// Format formats the input Caddyfile to a standard, nice-looking
+4 -4
View File
@@ -21,18 +21,18 @@ import (
type adjacency map[string][]string
type importGraph struct {
nodes map[string]bool
nodes map[string]struct{}
edges adjacency
}
func (i *importGraph) addNode(name string) {
if i.nodes == nil {
i.nodes = make(map[string]bool)
i.nodes = make(map[string]struct{})
}
if _, exists := i.nodes[name]; exists {
return
}
i.nodes[name] = true
i.nodes[name] = struct{}{}
}
func (i *importGraph) addNodes(names []string) {
@@ -66,7 +66,7 @@ func (i *importGraph) addEdge(from, to string) error {
}
if i.nodes == nil {
i.nodes = make(map[string]bool)
i.nodes = make(map[string]struct{})
}
if i.edges == nil {
i.edges = make(adjacency)
+15
View File
@@ -340,6 +340,8 @@ func (l *lexer) finalizeHeredoc(val []rune, marker string) ([]rune, error) {
return []rune(out), nil
}
// Quoted returns true if the token was enclosed in quotes
// (i.e. double quotes, backticks, or heredoc).
func (t Token) Quoted() bool {
return t.wasQuoted > 0
}
@@ -356,6 +358,19 @@ func (t Token) NumLineBreaks() int {
return lineBreaks
}
// Clone returns a deep copy of the token.
func (t Token) Clone() Token {
return Token{
File: t.File,
imports: append([]string{}, t.imports...),
Line: t.Line,
Text: t.Text,
wasQuoted: t.wasQuoted,
heredocMarker: t.heredocMarker,
snippetName: t.snippetName,
}
}
var heredocMarkerRegexp = regexp.MustCompile("^[A-Za-z0-9_-]+$")
// isNextOnNewLine tests whether t2 is on a different line from t1
+72 -5
View File
@@ -50,7 +50,7 @@ func Parse(filename string, input []byte) ([]ServerBlock, error) {
p := parser{
Dispenser: NewDispenser(tokens),
importGraph: importGraph{
nodes: make(map[string]bool),
nodes: make(map[string]struct{}),
edges: make(adjacency),
},
}
@@ -214,7 +214,12 @@ func (p *parser) addresses() error {
value := p.Val()
token := p.Token()
// special case: import directive replaces tokens during parse-time
// Reject request matchers if trying to define them globally
if strings.HasPrefix(value, "@") {
return p.Errf("request matchers may not be defined globally, they must be in a site block; found %s", value)
}
// Special case: import directive replaces tokens during parse-time
if value == "import" && p.isNewLine() {
err := p.doImport(0)
if err != nil {
@@ -359,9 +364,45 @@ func (p *parser) doImport(nesting int) error {
// set up a replacer for non-variadic args replacement
repl := makeArgsReplacer(args)
// grab all the tokens (if it exists) from within a block that follows the import
var blockTokens []Token
for currentNesting := p.Nesting(); p.NextBlock(currentNesting); {
blockTokens = append(blockTokens, p.Token())
}
// initialize with size 1
blockMapping := make(map[string][]Token, 1)
if len(blockTokens) > 0 {
// use such tokens to create a new dispenser, and then use it to parse each block
bd := NewDispenser(blockTokens)
for bd.Next() {
// see if we can grab a key
var currentMappingKey string
if bd.Val() == "{" {
return p.Err("anonymous blocks are not supported")
}
currentMappingKey = bd.Val()
currentMappingTokens := []Token{}
// read all args until end of line / {
if bd.NextArg() {
currentMappingTokens = append(currentMappingTokens, bd.Token())
for bd.NextArg() {
currentMappingTokens = append(currentMappingTokens, bd.Token())
}
// TODO(elee1766): we don't enter another mapping here because it's annoying to extract the { and } properly.
// maybe someone can do that in the future
} else {
// attempt to enter a block and add tokens to the currentMappingTokens
for mappingNesting := bd.Nesting(); bd.NextBlock(mappingNesting); {
currentMappingTokens = append(currentMappingTokens, bd.Token())
}
}
blockMapping[currentMappingKey] = currentMappingTokens
}
}
// splice out the import directive and its arguments
// (2 tokens, plus the length of args)
tokensBefore := p.tokens[:p.cursor-1-len(args)]
tokensBefore := p.tokens[:p.cursor-1-len(args)-len(blockTokens)]
tokensAfter := p.tokens[p.cursor+1:]
var importedTokens []Token
var nodes []string
@@ -395,7 +436,6 @@ func (p *parser) doImport(nesting int) error {
return p.Errf("Glob pattern may only contain one wildcard (*), but has others: %s", globPattern)
}
matches, err = filepath.Glob(globPattern)
if err != nil {
return p.Errf("Failed to use import pattern %s: %v", importPattern, err)
}
@@ -491,6 +531,33 @@ func (p *parser) doImport(nesting int) error {
maybeSnippet = false
}
}
// if it is {block}, we substitute with all tokens in the block
// if it is {blocks.*}, we substitute with the tokens in the mapping for the *
var skip bool
var tokensToAdd []Token
switch {
case token.Text == "{block}":
tokensToAdd = blockTokens
case strings.HasPrefix(token.Text, "{blocks.") && strings.HasSuffix(token.Text, "}"):
// {blocks.foo.bar} will be extracted to key `foo.bar`
blockKey := strings.TrimPrefix(strings.TrimSuffix(token.Text, "}"), "{blocks.")
val, ok := blockMapping[blockKey]
if ok {
tokensToAdd = val
}
default:
skip = true
}
if !skip {
if len(tokensToAdd) == 0 {
// if there is no content in the snippet block, don't do any replacement
// this allows snippets which contained {block}/{block.*} before this change to continue functioning as normal
tokensCopy = append(tokensCopy, token)
} else {
tokensCopy = append(tokensCopy, tokensToAdd...)
}
continue
}
if maybeSnippet {
tokensCopy = append(tokensCopy, token)
@@ -512,7 +579,7 @@ func (p *parser) doImport(nesting int) error {
// splice the imported tokens in the place of the import statement
// and rewind cursor so Next() will land on first imported token
p.tokens = append(tokensBefore, append(tokensCopy, tokensAfter...)...)
p.cursor -= len(args) + 1
p.cursor -= len(args) + len(blockTokens) + 1
return nil
}
+23
View File
@@ -857,6 +857,29 @@ func TestSnippetAcrossMultipleFiles(t *testing.T) {
}
}
func TestRejectsGlobalMatcher(t *testing.T) {
p := testParser(`
@rejected path /foo
(common) {
gzip foo
errors stderr
}
http://example.com {
import common
}
`)
_, err := p.parseAll()
if err == nil {
t.Fatal("Expected an error, but got nil")
}
expected := "request matchers may not be defined globally, they must be in a site block; found @rejected, at Testfile:2"
if err.Error() != expected {
t.Errorf("Expected error to be '%s' but got '%v'", expected, err)
}
}
func testParser(input string) parser {
return parser{Dispenser: NewTestDispenser(input)}
}
+65 -21
View File
@@ -24,7 +24,7 @@ import (
"time"
"github.com/caddyserver/certmagic"
"github.com/mholt/acmez/acme"
"github.com/mholt/acmez/v2/acme"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2"
@@ -49,7 +49,9 @@ func init() {
RegisterDirective("handle_errors", parseHandleErrors)
RegisterHandlerDirective("invoke", parseInvoke)
RegisterDirective("log", parseLog)
RegisterHandlerDirective("skip_log", parseSkipLog)
RegisterHandlerDirective("skip_log", parseLogSkip)
RegisterHandlerDirective("log_skip", parseLogSkip)
RegisterHandlerDirective("log_name", parseLogName)
}
// parseBind parses the bind directive. Syntax:
@@ -68,8 +70,7 @@ func parseBind(h Helper) ([]ConfigValue, error) {
// curves <curves...>
// client_auth {
// mode [request|require|verify_if_given|require_and_verify]
// trusted_ca_cert <base64_der>
// trusted_ca_cert_file <filename>
// trust_pool <module_name> [...]
// trusted_leaf_cert <base64_der>
// trusted_leaf_cert_file <filename>
// }
@@ -106,7 +107,6 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
var onDemand bool
var reusePrivateKeys bool
// file certificate loader
firstLine := h.RemainingArgs()
switch len(firstLine) {
case 0:
@@ -116,13 +116,13 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
} else if !strings.Contains(firstLine[0], "@") {
return nil, h.Err("single argument must either be 'internal' or an email address")
} else {
if acmeIssuer == nil {
acmeIssuer = new(caddytls.ACMEIssuer)
acmeIssuer = &caddytls.ACMEIssuer{
Email: firstLine[0],
}
acmeIssuer.Email = firstLine[0]
}
case 2:
// file certificate loader
certFilename := firstLine[0]
keyFilename := firstLine[1]
@@ -487,19 +487,24 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
case acmeIssuer != nil:
// implicit ACME issuers (from various subdirectives) - use defaults; there might be more than one
defaultIssuers := caddytls.DefaultIssuers()
defaultIssuers := caddytls.DefaultIssuers(acmeIssuer.Email)
// if a CA endpoint was set, override multiple implicit issuers since it's a specific one
// if an ACME CA endpoint was set, the user expects to use that specific one,
// not any others that may be defaults, so replace all defaults with that ACME CA
if acmeIssuer.CA != "" {
defaultIssuers = []certmagic.Issuer{acmeIssuer}
}
for _, issuer := range defaultIssuers {
switch iss := issuer.(type) {
case *caddytls.ACMEIssuer:
issuer = acmeIssuer
case *caddytls.ZeroSSLIssuer:
iss.ACMEIssuer = acmeIssuer
// apply settings from the implicitly-configured ACMEIssuer to any
// default ACMEIssuers, but preserve each default issuer's CA endpoint,
// because, for example, if you configure the DNS challenge, it should
// apply to any of the default ACMEIssuers, but you don't want to trample
// out their unique CA endpoints
if iss, ok := issuer.(*caddytls.ACMEIssuer); ok && iss != nil {
acmeCopy := *acmeIssuer
acmeCopy.CA = iss.CA
issuer = &acmeCopy
}
configVals = append(configVals, ConfigValue{
Class: "tls.cert_issuer",
@@ -844,6 +849,7 @@ func parseInvoke(h Helper) (caddyhttp.MiddlewareHandler, error) {
// log <logger_name> {
// hostnames <hostnames...>
// output <writer_module> ...
// core <core_module> ...
// format <encoder_module> ...
// level <level>
// }
@@ -910,7 +916,7 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
// this is useful for setting up loggers per subdomain in a site block
// with a wildcard domain
customHostnames := []string{}
noHostname := false
for h.NextBlock(0) {
switch h.Val() {
case "hostnames":
@@ -955,6 +961,22 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
}
cl.WriterRaw = caddyconfig.JSONModuleObject(wo, "output", moduleName, h.warnings)
case "core":
if !h.NextArg() {
return nil, h.ArgErr()
}
moduleName := h.Val()
moduleID := "caddy.logging.cores." + moduleName
unm, err := caddyfile.UnmarshalModule(h.Dispenser, moduleID)
if err != nil {
return nil, err
}
core, ok := unm.(zapcore.Core)
if !ok {
return nil, h.Errf("module %s (%T) is not a zapcore.Core", moduleID, unm)
}
cl.CoreRaw = caddyconfig.JSONModuleObject(core, "module", moduleName, h.warnings)
case "format":
if !h.NextArg() {
return nil, h.ArgErr()
@@ -996,6 +1018,12 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
cl.Exclude = append(cl.Exclude, h.Val())
}
case "no_hostname":
if h.NextArg() {
return nil, h.ArgErr()
}
noHostname = true
default:
return nil, h.Errf("unrecognized subdirective: %s", h.Val())
}
@@ -1003,7 +1031,7 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
var val namedCustomLog
val.hostnames = customHostnames
val.noHostname = noHostname
isEmptyConfig := reflect.DeepEqual(cl, new(caddy.CustomLog))
// Skip handling of empty logging configs
@@ -1038,13 +1066,29 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
return configValues, nil
}
// parseSkipLog parses the skip_log directive. Syntax:
// parseLogSkip parses the log_skip directive. Syntax:
//
// skip_log [<matcher>]
func parseSkipLog(h Helper) (caddyhttp.MiddlewareHandler, error) {
// log_skip [<matcher>]
func parseLogSkip(h Helper) (caddyhttp.MiddlewareHandler, error) {
h.Next() // consume directive name
// "skip_log" is deprecated, replaced by "log_skip"
if h.Val() == "skip_log" {
caddy.Log().Named("config.adapter.caddyfile").Warn("the 'skip_log' directive is deprecated, please use 'log_skip' instead!")
}
if h.NextArg() {
return nil, h.ArgErr()
}
return caddyhttp.VarsMiddleware{"skip_log": true}, nil
return caddyhttp.VarsMiddleware{"log_skip": true}, nil
}
// parseLogName parses the log_name directive. Syntax:
//
// log_name <names...>
func parseLogName(h Helper) (caddyhttp.MiddlewareHandler, error) {
h.Next() // consume directive name
return caddyhttp.VarsMiddleware{
caddyhttp.AccessLoggerNameVarKey: h.RemainingArgs(),
}, nil
}
+4 -2
View File
@@ -25,11 +25,12 @@ func TestLogDirectiveSyntax(t *testing.T) {
{
input: `:8080 {
log {
core mock
output file foo.log
}
}
`,
output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.log0"]},"log0":{"writer":{"filename":"foo.log","output":"file"},"include":["http.log.access.log0"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"log0"}}}}}}`,
output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.log0"]},"log0":{"writer":{"filename":"foo.log","output":"file"},"core":{"module":"mock"},"include":["http.log.access.log0"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"log0"}}}}}}`,
expectError: false,
},
{
@@ -53,11 +54,12 @@ func TestLogDirectiveSyntax(t *testing.T) {
{
input: `:8080 {
log name-override {
core mock
output file foo.log
}
}
`,
output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.name-override"]},"name-override":{"writer":{"filename":"foo.log","output":"file"},"include":["http.log.access.name-override"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"name-override"}}}}}}`,
output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.name-override"]},"name-override":{"writer":{"filename":"foo.log","output":"file"},"core":{"module":"mock"},"include":["http.log.access.name-override"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"name-override"}}}}}}`,
expectError: false,
},
} {
+87 -9
View File
@@ -27,23 +27,33 @@ import (
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
// directiveOrder specifies the order
// to apply directives in HTTP routes.
// defaultDirectiveOrder specifies the default order
// to apply directives in HTTP routes. This must only
// consist of directives that are included in Caddy's
// standard distribution.
//
// The root directive goes first in case rewrites or
// redirects depend on existence of files, i.e. the
// file matcher, which must know the root first.
// e.g. The 'root' directive goes near the start in
// case rewrites or redirects depend on existence of
// files, i.e. the file matcher, which must know the
// root first.
//
// The header directive goes second so that headers
// can be manipulated before doing redirects.
var directiveOrder = []string{
// e.g. The 'header' directive goes before 'redir' so
// that headers can be manipulated before doing redirects.
//
// e.g. The 'respond' directive is near the end because it
// writes a response and terminates the middleware chain.
var defaultDirectiveOrder = []string{
"tracing",
// set variables that may be used by other directives
"map",
"vars",
"fs",
"root",
"skip_log",
"log_append",
"skip_log", // TODO: deprecated, renamed to log_skip
"log_skip",
"log_name",
"header",
"copy_response_headers", // only in reverse_proxy's handle_response
@@ -64,6 +74,7 @@ var directiveOrder = []string{
"request_header",
"encode",
"push",
"intercept",
"templates",
// special routing & dispatching directives
@@ -84,6 +95,11 @@ var directiveOrder = []string{
"acme_server",
}
// directiveOrder specifies the order to apply directives
// in HTTP routes, after being modified by either the
// plugins or by the user via the "order" global option.
var directiveOrder = defaultDirectiveOrder
// directiveIsOrdered returns true if dir is
// a known, ordered (sorted) directive.
func directiveIsOrdered(dir string) bool {
@@ -130,6 +146,58 @@ func RegisterHandlerDirective(dir string, setupFunc UnmarshalHandlerFunc) {
})
}
// RegisterDirectiveOrder registers the default order for a
// directive from a plugin.
//
// This is useful when a plugin has a well-understood place
// it should run in the middleware pipeline, and it allows
// users to avoid having to define the order themselves.
//
// The directive dir may be placed in the position relative
// to ('before' or 'after') a directive included in Caddy's
// standard distribution. It cannot be relative to another
// plugin's directive.
//
// EXPERIMENTAL: This API may change or be removed.
func RegisterDirectiveOrder(dir string, position Positional, standardDir string) {
// check if directive was already ordered
if directiveIsOrdered(dir) {
panic("directive '" + dir + "' already ordered")
}
if position != Before && position != After {
panic("the 2nd argument must be either 'before' or 'after', got '" + position + "'")
}
// check if directive exists in standard distribution, since
// we can't allow plugins to depend on one another; we can't
// guarantee the order that plugins are loaded in.
foundStandardDir := false
for _, d := range defaultDirectiveOrder {
if d == standardDir {
foundStandardDir = true
}
}
if !foundStandardDir {
panic("the 3rd argument '" + standardDir + "' must be a directive that exists in the standard distribution of Caddy")
}
// insert directive into proper position
newOrder := directiveOrder
for i, d := range newOrder {
if d != standardDir {
continue
}
if position == Before {
newOrder = append(newOrder[:i], append([]string{dir}, newOrder[i:]...)...)
} else if position == After {
newOrder = append(newOrder[:i+1], append([]string{dir}, newOrder[i+1:]...)...)
}
break
}
directiveOrder = newOrder
}
// RegisterGlobalOption registers a unique global option opt with
// an associated unmarshaling (setup) function. When the global
// option opt is encountered in a Caddyfile, setupFunc will be
@@ -554,6 +622,16 @@ func (sb serverBlock) isAllHTTP() bool {
return true
}
// Positional are the supported modes for ordering directives.
type Positional string
const (
Before Positional = "before"
After Positional = "after"
First Positional = "first"
Last Positional = "last"
)
type (
// UnmarshalFunc is a function which can unmarshal Caddyfile
// tokens into zero or more config values using a Helper type.
+45 -20
View File
@@ -19,12 +19,12 @@ import (
"fmt"
"net"
"reflect"
"slices"
"sort"
"strconv"
"strings"
"go.uber.org/zap"
"golang.org/x/exp/slices"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig"
@@ -797,6 +797,15 @@ func (st *ServerType) serversFromPairings(
sblockLogHosts := sblock.hostsFromKeys(true)
for _, cval := range sblock.pile["custom_log"] {
ncl := cval.Value.(namedCustomLog)
// if `no_hostname` is set, then this logger will not
// be associated with any of the site block's hostnames,
// and only be usable via the `log_name` directive
// or the `access_logger_names` variable
if ncl.noHostname {
continue
}
if sblock.hasHostCatchAllKey() && len(ncl.hostnames) == 0 {
// all requests for hosts not able to be listed should use
// this log because it's a catch-all-hosts server block
@@ -805,22 +814,22 @@ func (st *ServerType) serversFromPairings(
// if the logger overrides the hostnames, map that to the logger name
for _, h := range ncl.hostnames {
if srv.Logs.LoggerNames == nil {
srv.Logs.LoggerNames = make(map[string]string)
srv.Logs.LoggerNames = make(map[string]caddyhttp.StringArray)
}
srv.Logs.LoggerNames[h] = ncl.name
srv.Logs.LoggerNames[h] = append(srv.Logs.LoggerNames[h], ncl.name)
}
} else {
// otherwise, map each host to the logger name
for _, h := range sblockLogHosts {
if srv.Logs.LoggerNames == nil {
srv.Logs.LoggerNames = make(map[string]string)
}
// strip the port from the host, if any
host, _, err := net.SplitHostPort(h)
if err != nil {
host = h
}
srv.Logs.LoggerNames[host] = ncl.name
if srv.Logs.LoggerNames == nil {
srv.Logs.LoggerNames = make(map[string]caddyhttp.StringArray)
}
srv.Logs.LoggerNames[host] = append(srv.Logs.LoggerNames[host], ncl.name)
}
}
}
@@ -1282,19 +1291,24 @@ func matcherSetFromMatcherToken(
if tkn.Text == "*" {
// match all requests == no matchers, so nothing to do
return nil, true, nil
} else if strings.HasPrefix(tkn.Text, "/") {
// convenient way to specify a single path match
}
// convenient way to specify a single path match
if strings.HasPrefix(tkn.Text, "/") {
return caddy.ModuleMap{
"path": caddyconfig.JSON(caddyhttp.MatchPath{tkn.Text}, warnings),
}, true, nil
} else if strings.HasPrefix(tkn.Text, matcherPrefix) {
// pre-defined matcher
}
// pre-defined matcher
if strings.HasPrefix(tkn.Text, matcherPrefix) {
m, ok := matcherDefs[tkn.Text]
if !ok {
return nil, false, fmt.Errorf("unrecognized matcher name: %+v", tkn.Text)
}
return m, true, nil
}
return nil, false, nil
}
@@ -1397,6 +1411,14 @@ func parseMatcherDefinitions(d *caddyfile.Dispenser, matchers map[string]caddy.M
// given a matcher name and the tokens following it, parse
// the tokens as a matcher module and record it
makeMatcher := func(matcherName string, tokens []caddyfile.Token) error {
// create a new dispenser from the tokens
dispenser := caddyfile.NewDispenser(tokens)
// set the matcher name (without @) in the dispenser context so
// that matcher modules can access it to use it as their name
// (e.g. regexp matchers which use the name for capture groups)
dispenser.SetContext(caddyfile.MatcherNameCtxKey, definitionName[1:])
mod, err := caddy.GetModule("http.matchers." + matcherName)
if err != nil {
return fmt.Errorf("getting matcher module '%s': %v", matcherName, err)
@@ -1405,7 +1427,7 @@ func parseMatcherDefinitions(d *caddyfile.Dispenser, matchers map[string]caddy.M
if !ok {
return fmt.Errorf("matcher module '%s' is not a Caddyfile unmarshaler", matcherName)
}
err = unm.UnmarshalCaddyfile(caddyfile.NewDispenser(tokens))
err = unm.UnmarshalCaddyfile(dispenser)
if err != nil {
return err
}
@@ -1422,11 +1444,13 @@ func parseMatcherDefinitions(d *caddyfile.Dispenser, matchers map[string]caddy.M
if d.NextArg() {
if d.Token().Quoted() {
// since it was missing the matcher name, we insert a token
// in front of the expression token itself
err := makeMatcher("expression", []caddyfile.Token{
{Text: "expression", File: d.File(), Line: d.Line()},
d.Token(),
})
// in front of the expression token itself; we use Clone() to
// make the new token to keep the same the import location as
// the next token, if this is within a snippet or imported file.
// see https://github.com/caddyserver/caddy/issues/6287
expressionToken := d.Token().Clone()
expressionToken.Text = "expression"
err := makeMatcher("expression", []caddyfile.Token{expressionToken, d.Token()})
if err != nil {
return err
}
@@ -1583,9 +1607,10 @@ func (c counter) nextGroup() string {
}
type namedCustomLog struct {
name string
hostnames []string
log *caddy.CustomLog
name string
hostnames []string
log *caddy.CustomLog
noHostname bool
}
// sbAddrAssociation is a mapping from a list of
+36 -10
View File
@@ -18,7 +18,7 @@ import (
"strconv"
"github.com/caddyserver/certmagic"
"github.com/mholt/acmez/acme"
"github.com/mholt/acmez/v2/acme"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig"
@@ -54,6 +54,7 @@ func init() {
RegisterGlobalOption("auto_https", parseOptAutoHTTPS)
RegisterGlobalOption("servers", parseServerOptions)
RegisterGlobalOption("ocsp_stapling", parseOCSPStaplingOptions)
RegisterGlobalOption("cert_lifetime", parseOptDuration)
RegisterGlobalOption("log", parseLogOptions)
RegisterGlobalOption("preferred_chains", parseOptPreferredChains)
RegisterGlobalOption("persist_config", parseOptPersistConfig)
@@ -107,7 +108,7 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
if !d.Next() {
return nil, d.ArgErr()
}
pos := d.Val()
pos := Positional(d.Val())
newOrder := directiveOrder
@@ -121,22 +122,22 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
// act on the positional
switch pos {
case "first":
case First:
newOrder = append([]string{dirName}, newOrder...)
if d.NextArg() {
return nil, d.ArgErr()
}
directiveOrder = newOrder
return newOrder, nil
case "last":
case Last:
newOrder = append(newOrder, dirName)
if d.NextArg() {
return nil, d.ArgErr()
}
directiveOrder = newOrder
return newOrder, nil
case "before":
case "after":
case Before:
case After:
default:
return nil, d.Errf("unknown positional '%s'", pos)
}
@@ -153,9 +154,9 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
// insert directive into proper position
for i, d := range newOrder {
if d == otherDir {
if pos == "before" {
if pos == Before {
newOrder = append(newOrder[:i], append([]string{dirName}, newOrder[i:]...)...)
} else if pos == "after" {
} else if pos == After {
newOrder = append(newOrder[:i+1], append([]string{dirName}, newOrder[i+1:]...)...)
}
break
@@ -212,9 +213,9 @@ func parseOptACMEDNS(d *caddyfile.Dispenser, _ any) (any, error) {
if err != nil {
return nil, err
}
prov, ok := unm.(certmagic.ACMEDNSProvider)
prov, ok := unm.(certmagic.DNSProvider)
if !ok {
return nil, d.Errf("module %s (%T) is not a certmagic.ACMEDNSProvider", modID, unm)
return nil, d.Errf("module %s (%T) is not a certmagic.DNSProvider", modID, unm)
}
return prov, nil
}
@@ -345,9 +346,34 @@ func parseOptOnDemand(d *caddyfile.Dispenser, _ any) (any, error) {
if ond == nil {
ond = new(caddytls.OnDemandConfig)
}
if ond.PermissionRaw != nil {
return nil, d.Err("on-demand TLS permission module (or 'ask') already specified")
}
perm := caddytls.PermissionByHTTP{Endpoint: d.Val()}
ond.PermissionRaw = caddyconfig.JSONModuleObject(perm, "module", "http", nil)
case "permission":
if !d.NextArg() {
return nil, d.ArgErr()
}
if ond == nil {
ond = new(caddytls.OnDemandConfig)
}
if ond.PermissionRaw != nil {
return nil, d.Err("on-demand TLS permission module (or 'ask') already specified")
}
modName := d.Val()
modID := "tls.permission." + modName
unm, err := caddyfile.UnmarshalModule(d, modID)
if err != nil {
return nil, err
}
perm, ok := unm.(caddytls.OnDemandPermission)
if !ok {
return nil, d.Errf("module %s (%T) is not an on-demand TLS permission module", modID, unm)
}
ond.PermissionRaw = caddyconfig.JSONModuleObject(perm, "module", modName, nil)
case "interval":
if !d.NextArg() {
return nil, d.ArgErr()
+14 -34
View File
@@ -50,6 +50,7 @@ type serverOptions struct {
ClientIPHeaders []string
ShouldLogCredentials bool
Metrics *caddyhttp.Metrics
Trace bool // TODO: EXPERIMENTAL
}
func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
@@ -246,39 +247,11 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
}
serverOpts.Metrics = new(caddyhttp.Metrics)
// TODO: DEPRECATED. (August 2022)
case "protocol":
caddy.Log().Named("caddyfile").Warn("DEPRECATED: protocol sub-option will be removed soon")
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "allow_h2c":
caddy.Log().Named("caddyfile").Warn("DEPRECATED: allow_h2c will be removed soon; use protocols option instead")
if d.NextArg() {
return nil, d.ArgErr()
}
if sliceContains(serverOpts.Protocols, "h2c") {
return nil, d.Errf("protocol h2c already specified")
}
serverOpts.Protocols = append(serverOpts.Protocols, "h2c")
case "strict_sni_host":
caddy.Log().Named("caddyfile").Warn("DEPRECATED: protocol > strict_sni_host in this position will be removed soon; move up to the servers block instead")
if d.NextArg() && d.Val() != "insecure_off" && d.Val() != "on" {
return nil, d.Errf("strict_sni_host only supports 'on' or 'insecure_off', got '%s'", d.Val())
}
boolVal := true
if d.Val() == "insecure_off" {
boolVal = false
}
serverOpts.StrictSNIHost = &boolVal
default:
return nil, d.Errf("unrecognized protocol option '%s'", d.Val())
}
case "trace":
if d.NextArg() {
return nil, d.ArgErr()
}
serverOpts.Trace = true
default:
return nil, d.Errf("unrecognized servers option '%s'", d.Val())
@@ -291,7 +264,7 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
func applyServerOptions(
servers map[string]*caddyhttp.Server,
options map[string]any,
warnings *[]caddyconfig.Warning,
_ *[]caddyconfig.Warning,
) error {
serverOpts, ok := options["servers"].([]serverOptions)
if !ok {
@@ -351,10 +324,17 @@ func applyServerOptions(
server.Metrics = opts.Metrics
if opts.ShouldLogCredentials {
if server.Logs == nil {
server.Logs = &caddyhttp.ServerLogConfig{}
server.Logs = new(caddyhttp.ServerLogConfig)
}
server.Logs.ShouldLogCredentials = opts.ShouldLogCredentials
}
if opts.Trace {
// TODO: THIS IS EXPERIMENTAL (MAY 2024)
if server.Logs == nil {
server.Logs = new(caddyhttp.ServerLogConfig)
}
server.Logs.Trace = opts.Trace
}
if opts.Name != "" {
nameReplacements[key] = opts.Name
+2 -1
View File
@@ -33,9 +33,10 @@ func NewShorthandReplacer() ShorthandReplacer {
{regexp.MustCompile(`{path\.([\w-]*)}`), "{http.request.uri.path.$1}"},
{regexp.MustCompile(`{file\.([\w-]*)}`), "{http.request.uri.path.file.$1}"},
{regexp.MustCompile(`{query\.([\w-]*)}`), "{http.request.uri.query.$1}"},
{regexp.MustCompile(`{re\.([\w-]*)\.([\w-]*)}`), "{http.regexp.$1.$2}"},
{regexp.MustCompile(`{re\.([\w-\.]*)}`), "{http.regexp.$1}"},
{regexp.MustCompile(`{vars\.([\w-]*)}`), "{http.vars.$1}"},
{regexp.MustCompile(`{rp\.([\w-\.]*)}`), "{http.reverse_proxy.$1}"},
{regexp.MustCompile(`{resp\.([\w-\.]*)}`), "{http.intercept.$1}"},
{regexp.MustCompile(`{err\.([\w-\.]*)}`), "{http.error.$1}"},
{regexp.MustCompile(`{file_match\.([\w-]*)}`), "{http.matchers.file.$1}"},
}
+51 -11
View File
@@ -24,7 +24,7 @@ import (
"strings"
"github.com/caddyserver/certmagic"
"github.com/mholt/acmez/acme"
"github.com/mholt/acmez/v2/acme"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig"
@@ -224,7 +224,7 @@ func (st ServerType) buildTLSApp(
var internal, external []string
for _, s := range ap.SubjectsRaw {
// do not create Issuers for Tailscale domains; they will be given a Manager instead
if strings.HasSuffix(strings.ToLower(s), ".ts.net") {
if isTailscaleDomain(s) {
continue
}
if !certmagic.SubjectQualifiesForCert(s) {
@@ -344,7 +344,7 @@ func (st ServerType) buildTLSApp(
internalAP := &caddytls.AutomationPolicy{
IssuersRaw: []json.RawMessage{json.RawMessage(`{"module":"internal"}`)},
}
if autoHTTPS != "off" {
if autoHTTPS != "off" && autoHTTPS != "disable_certs" {
for h := range httpsHostsSharedWithHostlessKey {
al = append(al, h)
if !certmagic.SubjectQualifiesForPublicCert(h) {
@@ -378,15 +378,12 @@ func (st ServerType) buildTLSApp(
if len(ap.Issuers) == 0 && automationPolicyHasAllPublicNames(ap) {
// for public names, create default issuers which will later be filled in with configured global defaults
// (internal names will implicitly use the internal issuer at auto-https time)
ap.Issuers = caddytls.DefaultIssuers()
emailStr, _ := globalEmail.(string)
ap.Issuers = caddytls.DefaultIssuers(emailStr)
// if a specific endpoint is configured, can't use multiple default issuers
if globalACMECA != nil {
if strings.Contains(globalACMECA.(string), "zerossl") {
ap.Issuers = []certmagic.Issuer{&caddytls.ZeroSSLIssuer{ACMEIssuer: new(caddytls.ACMEIssuer)}}
} else {
ap.Issuers = []certmagic.Issuer{new(caddytls.ACMEIssuer)}
}
ap.Issuers = []certmagic.Issuer{new(caddytls.ACMEIssuer)}
}
}
}
@@ -459,6 +456,8 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]any) e
globalACMEDNS := options["acme_dns"]
globalACMEEAB := options["acme_eab"]
globalPreferredChains := options["preferred_chains"]
globalCertLifetime := options["cert_lifetime"]
globalHTTPPort, globalHTTPSPort := options["http_port"], options["https_port"]
if globalEmail != nil && acmeIssuer.Email == "" {
acmeIssuer.Email = globalEmail.(string)
@@ -482,6 +481,27 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]any) e
if globalPreferredChains != nil && acmeIssuer.PreferredChains == nil {
acmeIssuer.PreferredChains = globalPreferredChains.(*caddytls.ChainPreference)
}
if globalHTTPPort != nil && (acmeIssuer.Challenges == nil || acmeIssuer.Challenges.HTTP == nil || acmeIssuer.Challenges.HTTP.AlternatePort == 0) {
if acmeIssuer.Challenges == nil {
acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
}
if acmeIssuer.Challenges.HTTP == nil {
acmeIssuer.Challenges.HTTP = new(caddytls.HTTPChallengeConfig)
}
acmeIssuer.Challenges.HTTP.AlternatePort = globalHTTPPort.(int)
}
if globalHTTPSPort != nil && (acmeIssuer.Challenges == nil || acmeIssuer.Challenges.TLSALPN == nil || acmeIssuer.Challenges.TLSALPN.AlternatePort == 0) {
if acmeIssuer.Challenges == nil {
acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
}
if acmeIssuer.Challenges.TLSALPN == nil {
acmeIssuer.Challenges.TLSALPN = new(caddytls.TLSALPNChallengeConfig)
}
acmeIssuer.Challenges.TLSALPN.AlternatePort = globalHTTPSPort.(int)
}
if globalCertLifetime != nil && acmeIssuer.CertificateLifetime == 0 {
acmeIssuer.CertificateLifetime = globalCertLifetime.(caddy.Duration)
}
return nil
}
@@ -490,7 +510,11 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]any) e
// for any other automation policies. A nil policy (and no error) will be
// returned if there are no default/global options. However, if always is
// true, a non-nil value will always be returned (unless there is an error).
func newBaseAutomationPolicy(options map[string]any, warnings []caddyconfig.Warning, always bool) (*caddytls.AutomationPolicy, error) {
func newBaseAutomationPolicy(
options map[string]any,
_ []caddyconfig.Warning,
always bool,
) (*caddytls.AutomationPolicy, error) {
issuers, hasIssuers := options["cert_issuer"]
_, hasLocalCerts := options["local_certs"]
keyType, hasKeyType := options["key_type"]
@@ -666,17 +690,33 @@ func automationPolicyShadows(i int, aps []*caddytls.AutomationPolicy) int {
// subjectQualifiesForPublicCert is like certmagic.SubjectQualifiesForPublicCert() except
// that this allows domains with multiple wildcard levels like '*.*.example.com' to qualify
// if the automation policy has OnDemand enabled (i.e. this function is more lenient).
//
// IP subjects are considered as non-qualifying for public certs. Technically, there are
// now public ACME CAs as well as non-ACME CAs that issue IP certificates. But this function
// is used solely for implicit automation (defaults), where it gets really complicated to
// keep track of which issuers support IP certificates in which circumstances. Currently,
// issuers that support IP certificates are very few, and all require some sort of config
// from the user anyway (such as an account credential). Since we cannot implicitly and
// automatically get public IP certs without configuration from the user, we treat IPs as
// not qualifying for public certificates. Users should expressly configure an issuer
// that supports IP certs for that purpose.
func subjectQualifiesForPublicCert(ap *caddytls.AutomationPolicy, subj string) bool {
return !certmagic.SubjectIsIP(subj) &&
!certmagic.SubjectIsInternal(subj) &&
(strings.Count(subj, "*.") < 2 || ap.OnDemand)
}
// automationPolicyHasAllPublicNames returns true if all the names on the policy
// do NOT qualify for public certs OR are tailscale domains.
func automationPolicyHasAllPublicNames(ap *caddytls.AutomationPolicy) bool {
for _, subj := range ap.SubjectsRaw {
if !subjectQualifiesForPublicCert(ap, subj) {
if !subjectQualifiesForPublicCert(ap, subj) || isTailscaleDomain(subj) {
return false
}
}
return true
}
func isTailscaleDomain(name string) bool {
return strings.HasSuffix(strings.ToLower(name), ".ts.net")
}
+5 -8
View File
@@ -181,19 +181,16 @@ func (hl HTTPLoader) makeClient(ctx caddy.Context) (*http.Client, error) {
if err != nil {
return nil, fmt.Errorf("getting server identity credentials: %v", err)
}
if tlsConfig == nil {
tlsConfig = new(tls.Config)
}
tlsConfig.Certificates = certs
// See https://github.com/securego/gosec/issues/1054#issuecomment-2072235199
//nolint:gosec
tlsConfig = &tls.Config{Certificates: certs}
} else if hl.TLS.ClientCertificateFile != "" && hl.TLS.ClientCertificateKeyFile != "" {
cert, err := tls.LoadX509KeyPair(hl.TLS.ClientCertificateFile, hl.TLS.ClientCertificateKeyFile)
if err != nil {
return nil, err
}
if tlsConfig == nil {
tlsConfig = new(tls.Config)
}
tlsConfig.Certificates = []tls.Certificate{cert}
//nolint:gosec
tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert}}
}
// trusted server certs
+165 -391
View File
@@ -1,42 +1,31 @@
package caddytest
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"io/fs"
"log"
"net"
"net/http"
"net/http/cookiejar"
"os"
"path"
"reflect"
"regexp"
"runtime"
"strconv"
"strings"
"testing"
"sync/atomic"
"time"
"github.com/aryann/difflib"
caddycmd "github.com/caddyserver/caddy/v2/cmd"
"github.com/caddyserver/caddy/v2/caddyconfig"
// plug in Caddy modules here
_ "github.com/caddyserver/caddy/v2/modules/standard"
)
// Defaults store any configuration required to make the tests run
type Defaults struct {
// Port we expect caddy to listening on
AdminPort int
// Certificates we expect to be loaded before attempting to run the tests
Certifcates []string
Certificates []string
// TestRequestTimeout is the time to wait for a http request to
TestRequestTimeout time.Duration
// LoadRequestTimeout is the time to wait for the config to be loaded against the caddy server
@@ -45,29 +34,31 @@ type Defaults struct {
// Default testing values
var Default = Defaults{
AdminPort: 2999, // different from what a real server also running on a developer's machine might be
Certifcates: []string{"/caddy.localhost.crt", "/caddy.localhost.key"},
Certificates: []string{"/caddy.localhost.crt", "/caddy.localhost.key"},
TestRequestTimeout: 5 * time.Second,
LoadRequestTimeout: 5 * time.Second,
}
var (
matchKey = regexp.MustCompile(`(/[\w\d\.]+\.key)`)
matchCert = regexp.MustCompile(`(/[\w\d\.]+\.crt)`)
)
// Tester represents an instance of a test client.
type Tester struct {
Client *http.Client
configLoaded bool
t testing.TB
Client *http.Client
adminPort int
portOne int
portTwo int
started atomic.Bool
configLoaded bool
configFileName string
envFileName string
}
// NewTester will create a new testing client with an attached cookie jar
func NewTester(t testing.TB) *Tester {
func NewTester() (*Tester, error) {
jar, err := cookiejar.New(nil)
if err != nil {
t.Fatalf("failed to create cookiejar: %s", err)
return nil, fmt.Errorf("failed to create cookiejar: %w", err)
}
return &Tester{
@@ -77,8 +68,7 @@ func NewTester(t testing.TB) *Tester {
Timeout: Default.TestRequestTimeout,
},
configLoaded: false,
t: t,
}
}, nil
}
type configLoadError struct {
@@ -92,58 +82,90 @@ func timeElapsed(start time.Time, name string) {
log.Printf("%s took %s", name, elapsed)
}
// InitServer this will configure the server with a configurion of a specific
// type. The configType must be either "json" or the adapter type.
func (tc *Tester) InitServer(rawConfig string, configType string) {
if err := tc.initServer(rawConfig, configType); err != nil {
tc.t.Logf("failed to load config: %s", err)
tc.t.Fail()
// launch caddy will start the server
func (tc *Tester) LaunchCaddy() error {
if !tc.started.CompareAndSwap(false, true) {
return fmt.Errorf("already launched caddy with this tester")
}
if err := tc.ensureConfigRunning(rawConfig, configType); err != nil {
tc.t.Logf("failed ensuring config is running: %s", err)
tc.t.Fail()
if err := tc.startServer(); err != nil {
return fmt.Errorf("failed to start server: %w", err)
}
return nil
}
// InitServer this will configure the server with a configurion of a specific
// type. The configType must be either "json" or the adapter type.
func (tc *Tester) initServer(rawConfig string, configType string) error {
if testing.Short() {
tc.t.SkipNow()
return nil
}
err := validateTestPrerequisites(tc.t)
if err != nil {
tc.t.Skipf("skipping tests as failed integration prerequisites. %s", err)
return nil
}
tc.t.Cleanup(func() {
if tc.t.Failed() && tc.configLoaded {
res, err := http.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
if err != nil {
tc.t.Log("unable to read the current config")
return
}
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
var out bytes.Buffer
_ = json.Indent(&out, body, "", " ")
tc.t.Logf("----------- failed with config -----------\n%s", out.String())
func (tc *Tester) CleanupCaddy() error {
// now shutdown the server, since the test is done.
defer func() {
// try to remove pthe tmp config file we created
if tc.configFileName != "" {
os.Remove(tc.configFileName)
}
})
if tc.envFileName != "" {
os.Remove(tc.envFileName)
}
}()
resp, err := http.Post(fmt.Sprintf("http://localhost:%d/stop", tc.adminPort), "", nil)
if err != nil {
return fmt.Errorf("couldn't stop caddytest server: %w", err)
}
resp.Body.Close()
for retries := 0; retries < 10; retries++ {
if tc.isCaddyAdminRunning() != nil {
return nil
}
time.Sleep(100 * time.Millisecond)
}
rawConfig = prependCaddyFilePath(rawConfig)
return fmt.Errorf("timed out waiting for caddytest server to stop")
}
func (tc *Tester) AdminPort() int {
return tc.adminPort
}
func (tc *Tester) PortOne() int {
return tc.portOne
}
func (tc *Tester) PortTwo() int {
return tc.portTwo
}
func (tc *Tester) ReplaceTestingPlaceholders(x string) string {
x = strings.ReplaceAll(x, "{$TESTING_CADDY_ADMIN_BIND}", fmt.Sprintf("localhost:%d", tc.adminPort))
x = strings.ReplaceAll(x, "{$TESTING_CADDY_ADMIN_PORT}", fmt.Sprintf("%d", tc.adminPort))
x = strings.ReplaceAll(x, "{$TESTING_CADDY_PORT_ONE}", fmt.Sprintf("%d", tc.portOne))
x = strings.ReplaceAll(x, "{$TESTING_CADDY_PORT_TWO}", fmt.Sprintf("%d", tc.portTwo))
return x
}
// LoadConfig loads the config to the tester server and also ensures that the config was loaded
// it should not be run
func (tc *Tester) LoadConfig(rawConfig string, configType string) error {
if tc.adminPort == 0 {
return fmt.Errorf("load config called where startServer didnt succeed")
}
rawConfig = tc.ReplaceTestingPlaceholders(rawConfig)
// replace special testing placeholders so we can have our admin api be on a random port
// normalize JSON config
if configType == "json" {
var conf any
if err := json.Unmarshal([]byte(rawConfig), &conf); err != nil {
return err
}
c, err := json.Marshal(conf)
if err != nil {
return err
}
rawConfig = string(c)
}
client := &http.Client{
Timeout: Default.LoadRequestTimeout,
}
start := time.Now()
req, err := http.NewRequest("POST", fmt.Sprintf("http://localhost:%d/load", Default.AdminPort), strings.NewReader(rawConfig))
req, err := http.NewRequest("POST", fmt.Sprintf("http://localhost:%d/load", tc.adminPort), strings.NewReader(rawConfig))
if err != nil {
tc.t.Errorf("failed to create request. %s", err)
return err
return fmt.Errorf("failed to create request. %w", err)
}
if configType == "json" {
@@ -154,16 +176,14 @@ func (tc *Tester) initServer(rawConfig string, configType string) error {
res, err := client.Do(req)
if err != nil {
tc.t.Errorf("unable to contact caddy server. %s", err)
return err
return fmt.Errorf("unable to contact caddy server. %w", err)
}
timeElapsed(start, "caddytest: config load time")
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
tc.t.Errorf("unable to read response. %s", err)
return err
return fmt.Errorf("unable to read response. %w", err)
}
if res.StatusCode != 200 {
@@ -171,133 +191,115 @@ func (tc *Tester) initServer(rawConfig string, configType string) error {
}
tc.configLoaded = true
// if the config is not loaded at this point, it is a bug in caddy's config.Load
// the contract for config.Load states that the config must be loaded before it returns, and that it will
// error if the config fails to apply
return nil
}
func (tc *Tester) ensureConfigRunning(rawConfig string, configType string) error {
expectedBytes := []byte(prependCaddyFilePath(rawConfig))
if configType != "json" {
adapter := caddyconfig.GetAdapter(configType)
if adapter == nil {
return fmt.Errorf("adapter of config type is missing: %s", configType)
}
expectedBytes, _, _ = adapter.Adapt([]byte(rawConfig), nil)
}
var expected any
err := json.Unmarshal(expectedBytes, &expected)
if err != nil {
return err
}
func (tc *Tester) GetCurrentConfig(receiver any) error {
client := &http.Client{
Timeout: Default.LoadRequestTimeout,
}
fetchConfig := func(client *http.Client) any {
resp, err := client.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
if err != nil {
return nil
}
defer resp.Body.Close()
actualBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil
}
var actual any
err = json.Unmarshal(actualBytes, &actual)
if err != nil {
return nil
}
return actual
resp, err := client.Get(fmt.Sprintf("http://localhost:%d/config/", tc.adminPort))
if err != nil {
return err
}
for retries := 10; retries > 0; retries-- {
if reflect.DeepEqual(expected, fetchConfig(client)) {
return nil
}
time.Sleep(1 * time.Second)
defer resp.Body.Close()
actualBytes, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
tc.t.Errorf("POSTed configuration isn't active")
return errors.New("EnsureConfigRunning: POSTed configuration isn't active")
err = json.Unmarshal(actualBytes, receiver)
if err != nil {
return err
}
return nil
}
const initConfig = `{
admin localhost:2999
}
`
// validateTestPrerequisites ensures the certificates are available in the
// designated path and Caddy sub-process is running.
func validateTestPrerequisites(t testing.TB) error {
// check certificates are found
for _, certName := range Default.Certifcates {
if _, err := os.Stat(getIntegrationDir() + certName); errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("caddy integration test certificates (%s) not found", certName)
}
func getFreePort() (int, error) {
lr, err := net.Listen("tcp", "localhost:0")
if err != nil {
return 0, err
}
port := strings.Split(lr.Addr().String(), ":")
if len(port) < 2 {
return 0, fmt.Errorf("no port available")
}
i, err := strconv.Atoi(port[1])
if err != nil {
return 0, err
}
err = lr.Close()
if err != nil {
return 0, fmt.Errorf("failed to close listener: %w", err)
}
return i, nil
}
if isCaddyAdminRunning() != nil {
// setup the init config file, and set the cleanup afterwards
// launches caddy, and then ensures the Caddy sub-process is running.
func (tc *Tester) startServer() error {
if tc.isCaddyAdminRunning() == nil {
return fmt.Errorf("caddy test admin port still in use")
}
a, err := getFreePort()
if err != nil {
return fmt.Errorf("could not find a open port to listen on: %w", err)
}
tc.adminPort = a
tc.portOne, err = getFreePort()
if err != nil {
return fmt.Errorf("could not find a open portOne: %w", err)
}
tc.portTwo, err = getFreePort()
if err != nil {
return fmt.Errorf("could not find a open portOne: %w", err)
}
// 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())
})
tc.configFileName = f.Name()
initConfig := fmt.Sprintf(`{
admin localhost:%d
}`, a)
if _, err := f.WriteString(initConfig); err != nil {
return err
}
}
// start inprocess caddy server
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(1 * time.Second)
}
// start inprocess caddy server
go func() {
_ = caddycmd.MainForTesting("run", "--config", tc.configFileName, "--adapter", "caddyfile")
}()
// wait for caddy admin api to start. it should happen quickly.
for retries := 10; retries > 0 && tc.isCaddyAdminRunning() != nil; retries-- {
time.Sleep(100 * time.Millisecond)
}
// one more time to return the error
return isCaddyAdminRunning()
return tc.isCaddyAdminRunning()
}
func isCaddyAdminRunning() error {
func (tc *Tester) isCaddyAdminRunning() error {
// assert that caddy is running
client := &http.Client{
Timeout: Default.LoadRequestTimeout,
}
resp, err := client.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
resp, err := client.Get(fmt.Sprintf("http://localhost:%d/config/", tc.adminPort))
if err != nil {
return fmt.Errorf("caddy integration test caddy server not running. Expected to be listening on localhost:%d", Default.AdminPort)
return fmt.Errorf("caddy integration test caddy server not running. Expected to be listening on localhost:%d", tc.adminPort)
}
resp.Body.Close()
return nil
}
func getIntegrationDir() string {
_, filename, _, ok := runtime.Caller(1)
if !ok {
panic("unable to determine the current file path")
}
return path.Dir(filename)
}
// use the convention to replace /[certificatename].[crt|key] with the full path
// this helps reduce the noise in test configurations and also allow this
// to run in any path
func prependCaddyFilePath(rawConfig string) string {
r := matchKey.ReplaceAllString(rawConfig, getIntegrationDir()+"$1")
r = matchCert.ReplaceAllString(r, getIntegrationDir()+"$1")
return r
}
// CreateTestingTransport creates a testing transport that forces call dialing connections to happen locally
func CreateTestingTransport() *http.Transport {
dialer := net.Dialer{
@@ -324,231 +326,3 @@ func CreateTestingTransport() *http.Transport {
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec
}
}
// AssertLoadError will load a config and expect an error
func AssertLoadError(t *testing.T, rawConfig string, configType string, expectedError string) {
tc := NewTester(t)
err := tc.initServer(rawConfig, configType)
if !strings.Contains(err.Error(), expectedError) {
t.Errorf("expected error \"%s\" but got \"%s\"", expectedError, err.Error())
}
}
// AssertRedirect makes a request and asserts the redirection happens
func (tc *Tester) AssertRedirect(requestURI string, expectedToLocation string, expectedStatusCode int) *http.Response {
redirectPolicyFunc := func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
// using the existing client, we override the check redirect policy for this test
old := tc.Client.CheckRedirect
tc.Client.CheckRedirect = redirectPolicyFunc
defer func() { tc.Client.CheckRedirect = old }()
resp, err := tc.Client.Get(requestURI)
if err != nil {
tc.t.Errorf("failed to call server %s", err)
return nil
}
if expectedStatusCode != resp.StatusCode {
tc.t.Errorf("requesting \"%s\" expected status code: %d but got %d", requestURI, expectedStatusCode, resp.StatusCode)
}
loc, err := resp.Location()
if err != nil {
tc.t.Errorf("requesting \"%s\" expected location: \"%s\" but got error: %s", requestURI, expectedToLocation, err)
}
if loc == nil && expectedToLocation != "" {
tc.t.Errorf("requesting \"%s\" expected a Location header, but didn't get one", requestURI)
}
if loc != nil {
if expectedToLocation != loc.String() {
tc.t.Errorf("requesting \"%s\" expected location: \"%s\" but got \"%s\"", requestURI, expectedToLocation, loc.String())
}
}
return resp
}
// CompareAdapt adapts a config and then compares it against an expected result
func CompareAdapt(t testing.TB, filename, rawConfig string, adapterName string, expectedResponse string) bool {
cfgAdapter := caddyconfig.GetAdapter(adapterName)
if cfgAdapter == nil {
t.Logf("unrecognized config adapter '%s'", adapterName)
return false
}
options := make(map[string]any)
result, warnings, err := cfgAdapter.Adapt([]byte(rawConfig), options)
if err != nil {
t.Logf("adapting config using %s adapter: %v", adapterName, err)
return false
}
// prettify results to keep tests human-manageable
var prettyBuf bytes.Buffer
err = json.Indent(&prettyBuf, result, "", "\t")
if err != nil {
return false
}
result = prettyBuf.Bytes()
if len(warnings) > 0 {
for _, w := range warnings {
t.Logf("warning: %s:%d: %s: %s", filename, w.Line, w.Directive, w.Message)
}
}
diff := difflib.Diff(
strings.Split(expectedResponse, "\n"),
strings.Split(string(result), "\n"))
// scan for failure
failed := false
for _, d := range diff {
if d.Delta != difflib.Common {
failed = true
break
}
}
if failed {
for _, d := range diff {
switch d.Delta {
case difflib.Common:
fmt.Printf(" %s\n", d.Payload)
case difflib.LeftOnly:
fmt.Printf(" - %s\n", d.Payload)
case difflib.RightOnly:
fmt.Printf(" + %s\n", d.Payload)
}
}
return false
}
return true
}
// AssertAdapt adapts a config and then tests it against an expected result
func AssertAdapt(t testing.TB, rawConfig string, adapterName string, expectedResponse string) {
ok := CompareAdapt(t, "Caddyfile", rawConfig, adapterName, expectedResponse)
if !ok {
t.Fail()
}
}
// Generic request functions
func applyHeaders(t testing.TB, req *http.Request, requestHeaders []string) {
requestContentType := ""
for _, requestHeader := range requestHeaders {
arr := strings.SplitAfterN(requestHeader, ":", 2)
k := strings.TrimRight(arr[0], ":")
v := strings.TrimSpace(arr[1])
if k == "Content-Type" {
requestContentType = v
}
t.Logf("Request header: %s => %s", k, v)
req.Header.Set(k, v)
}
if requestContentType == "" {
t.Logf("Content-Type header not provided")
}
}
// AssertResponseCode will execute the request and verify the status code, returns a response for additional assertions
func (tc *Tester) AssertResponseCode(req *http.Request, expectedStatusCode int) *http.Response {
resp, err := tc.Client.Do(req)
if err != nil {
tc.t.Fatalf("failed to call server %s", err)
}
if expectedStatusCode != resp.StatusCode {
tc.t.Errorf("requesting \"%s\" expected status code: %d but got %d", req.URL.RequestURI(), expectedStatusCode, resp.StatusCode)
}
return resp
}
// AssertResponse request a URI and assert the status code and the body contains a string
func (tc *Tester) AssertResponse(req *http.Request, expectedStatusCode int, expectedBody string) (*http.Response, string) {
resp := tc.AssertResponseCode(req, expectedStatusCode)
defer resp.Body.Close()
bytes, err := io.ReadAll(resp.Body)
if err != nil {
tc.t.Fatalf("unable to read the response body %s", err)
}
body := string(bytes)
if body != expectedBody {
tc.t.Errorf("requesting \"%s\" expected response body \"%s\" but got \"%s\"", req.RequestURI, expectedBody, body)
}
return resp, body
}
// Verb specific test functions
// AssertGetResponse GET a URI and expect a statusCode and body text
func (tc *Tester) AssertGetResponse(requestURI string, expectedStatusCode int, expectedBody string) (*http.Response, string) {
req, err := http.NewRequest("GET", requestURI, nil)
if err != nil {
tc.t.Fatalf("unable to create request %s", err)
}
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
}
// AssertDeleteResponse request a URI and expect a statusCode and body text
func (tc *Tester) AssertDeleteResponse(requestURI string, expectedStatusCode int, expectedBody string) (*http.Response, string) {
req, err := http.NewRequest("DELETE", requestURI, nil)
if err != nil {
tc.t.Fatalf("unable to create request %s", err)
}
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
}
// AssertPostResponseBody POST to a URI and assert the response code and body
func (tc *Tester) AssertPostResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
req, err := http.NewRequest("POST", requestURI, requestBody)
if err != nil {
tc.t.Errorf("failed to create request %s", err)
return nil, ""
}
applyHeaders(tc.t, req, requestHeaders)
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
}
// AssertPutResponseBody PUT to a URI and assert the response code and body
func (tc *Tester) AssertPutResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
req, err := http.NewRequest("PUT", requestURI, requestBody)
if err != nil {
tc.t.Errorf("failed to create request %s", err)
return nil, ""
}
applyHeaders(tc.t, req, requestHeaders)
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
}
// AssertPatchResponseBody PATCH to a URI and assert the response code and body
func (tc *Tester) AssertPatchResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
req, err := http.NewRequest("PATCH", requestURI, requestBody)
if err != nil {
tc.t.Errorf("failed to create request %s", err)
return nil, ""
}
applyHeaders(tc.t, req, requestHeaders)
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
}
+116
View File
@@ -0,0 +1,116 @@
package caddytest
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"strings"
"testing"
"github.com/aryann/difflib"
"github.com/stretchr/testify/require"
"github.com/caddyserver/caddy/v2/caddyconfig"
)
// AssertLoadError will load a config and expect an error
func AssertLoadError(t *testing.T, rawConfig string, configType string, expectedError string) {
tc, err := NewTester()
require.NoError(t, err)
err = tc.LaunchCaddy()
require.NoError(t, err)
err = tc.LoadConfig(rawConfig, configType)
if !strings.Contains(err.Error(), expectedError) {
t.Errorf("expected error \"%s\" but got \"%s\"", expectedError, err.Error())
}
_ = tc.CleanupCaddy()
}
// CompareAdapt adapts a config and then compares it against an expected result
func CompareAdapt(t testing.TB, filename, rawConfig string, adapterName string, expectedResponse string) bool {
cfgAdapter := caddyconfig.GetAdapter(adapterName)
if cfgAdapter == nil {
t.Logf("unrecognized config adapter '%s'", adapterName)
return false
}
options := make(map[string]any)
result, warnings, err := cfgAdapter.Adapt([]byte(rawConfig), options)
if err != nil {
t.Logf("adapting config using %s adapter: %v", adapterName, err)
return false
}
// prettify results to keep tests human-manageable
var prettyBuf bytes.Buffer
err = json.Indent(&prettyBuf, result, "", "\t")
if err != nil {
return false
}
result = prettyBuf.Bytes()
if len(warnings) > 0 {
for _, w := range warnings {
t.Logf("warning: %s:%d: %s: %s", filename, w.Line, w.Directive, w.Message)
}
}
diff := difflib.Diff(
strings.Split(expectedResponse, "\n"),
strings.Split(string(result), "\n"))
// scan for failure
failed := false
for _, d := range diff {
if d.Delta != difflib.Common {
failed = true
break
}
}
if failed {
for _, d := range diff {
switch d.Delta {
case difflib.Common:
fmt.Printf(" %s\n", d.Payload)
case difflib.LeftOnly:
fmt.Printf(" - %s\n", d.Payload)
case difflib.RightOnly:
fmt.Printf(" + %s\n", d.Payload)
}
}
return false
}
return true
}
// AssertAdapt adapts a config and then tests it against an expected result
func AssertAdapt(t testing.TB, rawConfig string, adapterName string, expectedResponse string) {
ok := CompareAdapt(t, "Caddyfile", rawConfig, adapterName, expectedResponse)
if !ok {
t.Fail()
}
}
// Generic request functions
func applyHeaders(t testing.TB, req *http.Request, requestHeaders []string) {
requestContentType := ""
for _, requestHeader := range requestHeaders {
arr := strings.SplitAfterN(requestHeader, ":", 2)
k := strings.TrimRight(arr[0], ":")
v := strings.TrimSpace(arr[1])
if k == "Content-Type" {
requestContentType = v
}
t.Logf("Request header: %s => %s", k, v)
req.Header.Set(k, v)
}
if requestContentType == "" {
t.Logf("Content-Type header not provided")
}
}
+100 -3
View File
@@ -1,20 +1,22 @@
package caddytest
import (
"fmt"
"net/http"
"strings"
"testing"
)
func TestReplaceCertificatePaths(t *testing.T) {
rawConfig := `a.caddy.localhost:9443 {
rawConfig := `a.caddy.localhost:9443{
tls /caddy.localhost.crt /caddy.localhost.key {
}
redir / https://b.caddy.localhost:9443/version 301
respond /version 200 {
body "hello from a.caddy.localhost"
}
}
}`
r := prependCaddyFilePath(rawConfig)
@@ -31,3 +33,98 @@ func TestReplaceCertificatePaths(t *testing.T) {
t.Error("expected redirect uri to be unchanged")
}
}
func TestLoadUnorderedJSON(t *testing.T) {
harness := StartHarness(t)
harness.LoadConfig(`
{
"logging": {
"logs": {
"default": {
"level": "DEBUG",
"writer": {
"output": "stdout"
}
},
"sStdOutLogs": {
"level": "DEBUG",
"writer": {
"output": "stdout"
},
"include": [
"http.*",
"admin.*"
]
},
"sFileLogs": {
"level": "DEBUG",
"writer": {
"output": "stdout"
},
"include": [
"http.*",
"admin.*"
]
}
}
},
"admin": {
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"pki": {
"certificate_authorities" : {
"local" : {
"install_trust": false
}
}
},
"http": {
"http_port": {$TESTING_CADDY_PORT_ONE},
"https_port": {$TESTING_CADDY_PORT_TWO},
"servers": {
"s_server": {
"listen": [
":{$TESTING_CADDY_PORT_ONE}",
":{$TESTING_CADDY_PORT_TWO}"
],
"routes": [
{
"handle": [
{
"handler": "static_response",
"body": "Hello"
}
]
},
{
"match": [
{
"host": [
"localhost",
"127.0.0.1"
]
}
]
}
],
"logs": {
"default_logger_name": "sStdOutLogs",
"logger_names": {
"localhost": "sStdOutLogs",
"127.0.0.1": "sFileLogs"
}
}
}
}
}
}
}
`, "json")
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), nil)
if err != nil {
t.Fail()
return
}
harness.AssertResponseCode(req, 200)
}
+19 -24
View File
@@ -13,8 +13,8 @@ import (
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddytest"
"github.com/mholt/acmez"
"github.com/mholt/acmez/acme"
"github.com/mholt/acmez/v2"
"github.com/mholt/acmez/v2/acme"
smallstepacme "github.com/smallstep/certificates/acme"
"go.uber.org/zap"
)
@@ -24,19 +24,13 @@ const acmeChallengePort = 9081
// Test the basic functionality of Caddy's ACME server
func TestACMEServerWithDefaults(t *testing.T) {
ctx := context.Background()
logger, err := zap.NewDevelopment()
if err != nil {
t.Error(err)
return
}
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
local_certs
}
acme.localhost {
@@ -44,10 +38,11 @@ func TestACMEServerWithDefaults(t *testing.T) {
}
`, "caddyfile")
logger := caddy.Log().Named("acmeserver")
client := acmez.Client{
Client: &acme.Client{
Directory: "https://acme.localhost:9443/acme/local/directory",
HTTPClient: tester.Client,
Directory: fmt.Sprintf("https://acme.localhost:%d/acme/local/directory", harness.Tester().PortTwo()),
HTTPClient: harness.Client(),
Logger: logger,
},
ChallengeSolvers: map[string]acmez.Solver{
@@ -77,7 +72,7 @@ func TestACMEServerWithDefaults(t *testing.T) {
return
}
certs, err := client.ObtainCertificate(ctx, account, certPrivateKey, []string{"localhost"})
certs, err := client.ObtainCertificateForSANs(ctx, account, certPrivateKey, []string{"localhost"})
if err != nil {
t.Errorf("obtaining certificate: %v", err)
return
@@ -97,13 +92,13 @@ func TestACMEServerWithMismatchedChallenges(t *testing.T) {
ctx := context.Background()
logger := caddy.Log().Named("acmez")
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
local_certs
}
acme.localhost {
@@ -115,8 +110,8 @@ func TestACMEServerWithMismatchedChallenges(t *testing.T) {
client := acmez.Client{
Client: &acme.Client{
Directory: "https://acme.localhost:9443/acme/local/directory",
HTTPClient: tester.Client,
Directory: fmt.Sprintf("https://acme.localhost:%d/acme/local/directory", harness.Tester().PortTwo()),
HTTPClient: harness.Client(),
Logger: logger,
},
ChallengeSolvers: map[string]acmez.Solver{
@@ -146,7 +141,7 @@ func TestACMEServerWithMismatchedChallenges(t *testing.T) {
return
}
certs, err := client.ObtainCertificate(ctx, account, certPrivateKey, []string{"localhost"})
certs, err := client.ObtainCertificateForSANs(ctx, account, certPrivateKey, []string{"localhost"})
if len(certs) > 0 {
t.Errorf("expected '0' certificates, but received '%d'", len(certs))
}
+34 -46
View File
@@ -5,50 +5,51 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"strings"
"testing"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddytest"
"github.com/mholt/acmez"
"github.com/mholt/acmez/acme"
"go.uber.org/zap"
"github.com/mholt/acmez/v2"
"github.com/mholt/acmez/v2/acme"
)
func TestACMEServerDirectory(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
skip_install_trust
local_certs
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
pki {
ca local {
name "Caddy Local Authority"
}
}
}
acme.localhost:9443 {
acme.localhost:{$TESTING_CADDY_PORT_TWO} {
acme_server
}
`, "caddyfile")
tester.AssertGetResponse(
"https://acme.localhost:9443/acme/local/directory",
harness.AssertGetResponse(
fmt.Sprintf("https://acme.localhost:%d/acme/local/directory", harness.Tester().PortTwo()),
200,
`{"newNonce":"https://acme.localhost:9443/acme/local/new-nonce","newAccount":"https://acme.localhost:9443/acme/local/new-account","newOrder":"https://acme.localhost:9443/acme/local/new-order","revokeCert":"https://acme.localhost:9443/acme/local/revoke-cert","keyChange":"https://acme.localhost:9443/acme/local/key-change"}
`)
fmt.Sprintf(`{"newNonce":"https://acme.localhost:%[1]d/acme/local/new-nonce","newAccount":"https://acme.localhost:%[1]d/acme/local/new-account","newOrder":"https://acme.localhost:%[1]d/acme/local/new-order","revokeCert":"https://acme.localhost:%[1]d/acme/local/revoke-cert","keyChange":"https://acme.localhost:%[1]d/acme/local/key-change"}
`, harness.Tester().PortTwo()))
}
func TestACMEServerAllowPolicy(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
skip_install_trust
local_certs
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
pki {
ca local {
name "Caddy Local Authority"
@@ -66,16 +67,12 @@ func TestACMEServerAllowPolicy(t *testing.T) {
`, "caddyfile")
ctx := context.Background()
logger, err := zap.NewDevelopment()
if err != nil {
t.Error(err)
return
}
logger := caddy.Log().Named("acmez")
client := acmez.Client{
Client: &acme.Client{
Directory: "https://acme.localhost:9443/acme/local/directory",
HTTPClient: tester.Client,
Directory: fmt.Sprintf("https://acme.localhost:%d/acme/local/directory", harness.Tester().PortTwo()),
HTTPClient: harness.Client(),
Logger: logger,
},
ChallengeSolvers: map[string]acmez.Solver{
@@ -105,12 +102,7 @@ func TestACMEServerAllowPolicy(t *testing.T) {
return
}
{
certs, err := client.ObtainCertificate(
ctx,
account,
certPrivateKey,
[]string{"localhost"},
)
certs, err := client.ObtainCertificateForSANs(ctx, account, certPrivateKey, []string{"localhost"})
if err != nil {
t.Errorf("obtaining certificate for allowed domain: %v", err)
return
@@ -126,7 +118,7 @@ func TestACMEServerAllowPolicy(t *testing.T) {
}
}
{
_, err := client.ObtainCertificate(ctx, account, certPrivateKey, []string{"not-matching.localhost"})
_, err := client.ObtainCertificateForSANs(ctx, account, certPrivateKey, []string{"not-matching.localhost"})
if err == nil {
t.Errorf("obtaining certificate for 'not-matching.localhost' domain")
} else if err != nil && !strings.Contains(err.Error(), "urn:ietf:params:acme:error:rejectedIdentifier") {
@@ -136,14 +128,14 @@ func TestACMEServerAllowPolicy(t *testing.T) {
}
func TestACMEServerDenyPolicy(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
skip_install_trust
local_certs
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
pki {
ca local {
name "Caddy Local Authority"
@@ -160,16 +152,12 @@ func TestACMEServerDenyPolicy(t *testing.T) {
`, "caddyfile")
ctx := context.Background()
logger, err := zap.NewDevelopment()
if err != nil {
t.Error(err)
return
}
logger := caddy.Log().Named("acmez")
client := acmez.Client{
Client: &acme.Client{
Directory: "https://acme.localhost:9443/acme/local/directory",
HTTPClient: tester.Client,
Directory: fmt.Sprintf("https://acme.localhost:%d/acme/local/directory", harness.Tester().PortTwo()),
HTTPClient: harness.Client(),
Logger: logger,
},
ChallengeSolvers: map[string]acmez.Solver{
@@ -199,10 +187,10 @@ func TestACMEServerDenyPolicy(t *testing.T) {
return
}
{
_, err := client.ObtainCertificate(ctx, account, certPrivateKey, []string{"deny.localhost"})
_, err := client.ObtainCertificateForSANs(ctx, account, certPrivateKey, []string{"deny.localhost"})
if err == nil {
t.Errorf("obtaining certificate for 'deny.localhost' domain")
} else if err != nil && !strings.Contains(err.Error(), "urn:ietf:params:acme:error:rejectedIdentifier") {
} else if !strings.Contains(err.Error(), "urn:ietf:params:acme:error:rejectedIdentifier") {
t.Logf("unexpected error: %v", err)
}
}
+47 -46
View File
@@ -1,6 +1,7 @@
package integration
import (
"fmt"
"net/http"
"testing"
@@ -8,69 +9,69 @@ import (
)
func TestAutoHTTPtoHTTPSRedirectsImplicitPort(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
admin localhost:2999
admin {$TESTING_CADDY_ADMIN_BIND}
skip_install_trust
http_port 9080
https_port 9443
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
}
localhost
respond "Yahaha! You found me!"
`, "caddyfile")
tester.AssertRedirect("http://localhost:9080/", "https://localhost/", http.StatusPermanentRedirect)
harness.AssertRedirect(fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), "https://localhost/", http.StatusPermanentRedirect)
}
func TestAutoHTTPtoHTTPSRedirectsExplicitPortSameAsHTTPSPort(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
}
localhost:9443
localhost:{$TESTING_CADDY_PORT_TWO}
respond "Yahaha! You found me!"
`, "caddyfile")
tester.AssertRedirect("http://localhost:9080/", "https://localhost/", http.StatusPermanentRedirect)
harness.AssertRedirect(fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), "https://localhost/", http.StatusPermanentRedirect)
}
func TestAutoHTTPtoHTTPSRedirectsExplicitPortDifferentFromHTTPSPort(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
}
localhost:1234
respond "Yahaha! You found me!"
`, "caddyfile")
tester.AssertRedirect("http://localhost:9080/", "https://localhost:1234/", http.StatusPermanentRedirect)
harness.AssertRedirect(fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), "https://localhost:1234/", http.StatusPermanentRedirect)
}
func TestAutoHTTPRedirectsWithHTTPListenerFirstInAddresses(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"http": {
"http_port": 9080,
"https_port": 9443,
"http_port": {$TESTING_CADDY_PORT_ONE},
"https_port": {$TESTING_CADDY_PORT_TWO},
"servers": {
"ingress_server": {
"listen": [
":9080",
":9443"
":{$TESTING_CADDY_PORT_ONE}",
":{$TESTING_CADDY_PORT_TWO}"
],
"routes": [
{
@@ -94,52 +95,52 @@ func TestAutoHTTPRedirectsWithHTTPListenerFirstInAddresses(t *testing.T) {
}
}
`, "json")
tester.AssertRedirect("http://localhost:9080/", "https://localhost/", http.StatusPermanentRedirect)
harness.AssertRedirect(fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), "https://localhost/", http.StatusPermanentRedirect)
}
func TestAutoHTTPRedirectsInsertedBeforeUserDefinedCatchAll(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
local_certs
}
http://:9080 {
http://:{$TESTING_CADDY_PORT_ONE} {
respond "Foo"
}
http://baz.localhost:9080 {
http://baz.localhost:{$TESTING_CADDY_PORT_ONE} {
respond "Baz"
}
bar.localhost {
respond "Bar"
}
`, "caddyfile")
tester.AssertRedirect("http://bar.localhost:9080/", "https://bar.localhost/", http.StatusPermanentRedirect)
tester.AssertGetResponse("http://foo.localhost:9080/", 200, "Foo")
tester.AssertGetResponse("http://baz.localhost:9080/", 200, "Baz")
harness.AssertRedirect(fmt.Sprintf("http://bar.localhost:%d/", harness.Tester().PortOne()), "https://bar.localhost/", http.StatusPermanentRedirect)
harness.AssertGetResponse(fmt.Sprintf("http://foo.localhost:%d/", harness.Tester().PortOne()), 200, "Foo")
harness.AssertGetResponse(fmt.Sprintf("http://baz.localhost:%d/", harness.Tester().PortOne()), 200, "Baz")
}
func TestAutoHTTPRedirectsInsertedBeforeUserDefinedCatchAllWithNoExplicitHTTPSite(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
local_certs
}
http://:9080 {
http://:{$TESTING_CADDY_PORT_ONE} {
respond "Foo"
}
bar.localhost {
respond "Bar"
}
`, "caddyfile")
tester.AssertRedirect("http://bar.localhost:9080/", "https://bar.localhost/", http.StatusPermanentRedirect)
tester.AssertGetResponse("http://foo.localhost:9080/", 200, "Foo")
tester.AssertGetResponse("http://baz.localhost:9080/", 200, "Foo")
harness.AssertRedirect(fmt.Sprintf("http://bar.localhost:%d/", harness.Tester().PortOne()), "https://bar.localhost/", http.StatusPermanentRedirect)
harness.AssertGetResponse(fmt.Sprintf("http://foo.localhost:%d/", harness.Tester().PortOne()), 200, "Foo")
harness.AssertGetResponse(fmt.Sprintf("http://baz.localhost:%d/", harness.Tester().PortOne()), 200, "Foo")
}
@@ -0,0 +1,67 @@
{
pki {
ca internal {
name "Internal"
root_cn "Internal Root Cert"
intermediate_cn "Internal Intermediate Cert"
}
}
}
acme.example.com {
acme_server {
ca internal
sign_with_root
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"acme.example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"ca": "internal",
"handler": "acme_server",
"sign_with_root": true
}
]
}
]
}
],
"terminal": true
}
]
}
}
},
"pki": {
"certificate_authorities": {
"internal": {
"name": "Internal",
"root_common_name": "Internal Root Cert",
"intermediate_common_name": "Internal Intermediate Cert"
}
}
}
}
}
@@ -1,3 +1,7 @@
(snippet) {
@g `{http.error.status_code} == 404`
}
example.com
@a expression {http.error.status_code} == 400
@@ -14,6 +18,12 @@ abort @d
@e expression `{http.error.status_code} == 404`
abort @e
@f `{http.error.status_code} == 404`
abort @f
import snippet
abort @g
----------
{
"apps": {
@@ -84,7 +94,10 @@ abort @e
],
"match": [
{
"expression": "{http.error.status_code} == 403"
"expression": {
"expr": "{http.error.status_code} == 403",
"name": "d"
}
}
]
},
@@ -97,7 +110,42 @@ abort @e
],
"match": [
{
"expression": "{http.error.status_code} == 404"
"expression": {
"expr": "{http.error.status_code} == 404",
"name": "e"
}
}
]
},
{
"handle": [
{
"abort": true,
"handler": "static_response"
}
],
"match": [
{
"expression": {
"expr": "{http.error.status_code} == 404",
"name": "f"
}
}
]
},
{
"handle": [
{
"abort": true,
"handler": "static_response"
}
],
"match": [
{
"expression": {
"expr": "{http.error.status_code} == 404",
"name": "g"
}
}
]
}
@@ -0,0 +1,40 @@
:8080 {
root * ./
file_server {
etag_file_extensions .b3sum .sha256
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":8080"
],
"routes": [
{
"handle": [
{
"handler": "vars",
"root": "./"
},
{
"etag_file_extensions": [
".b3sum",
".sha256"
],
"handler": "file_server",
"hide": [
"./Caddyfile"
]
}
]
}
]
}
}
}
}
}
@@ -63,6 +63,14 @@
"issuers": [
{
"ca": "https://example.com",
"challenges": {
"http": {
"alternate_port": 8080
},
"tls-alpn": {
"alternate_port": 8443
}
},
"email": "test@example.com",
"external_account": {
"key_id": "4K2scIVbBpNd-78scadB2g",
@@ -40,12 +40,6 @@ example.com
"preferred_chains": {
"smallest": true
}
},
{
"module": "zerossl",
"preferred_chains": {
"smallest": true
}
}
]
}
@@ -72,8 +72,12 @@ b.example.com {
],
"logs": {
"logger_names": {
"a.example.com": "log0",
"b.example.com": "log1"
"a.example.com": [
"log0"
],
"b.example.com": [
"log1"
]
}
}
}
@@ -0,0 +1,58 @@
(snippet) {
header {
{block}
}
}
example.com {
import snippet {
foo bar
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Foo": [
"bar"
]
}
}
}
]
}
]
}
],
"terminal": true
}
]
}
}
}
}
}
@@ -0,0 +1,56 @@
(snippet) {
{block}
}
example.com {
import snippet {
header foo bar
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Foo": [
"bar"
]
}
}
}
]
}
]
}
],
"terminal": true
}
]
}
}
}
}
}
@@ -0,0 +1,76 @@
(snippet) {
header {
{blocks.foo}
}
header {
{blocks.bar}
}
}
example.com {
import snippet {
foo {
foo a
}
bar {
bar b
}
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Foo": [
"a"
]
}
}
},
{
"handler": "headers",
"response": {
"set": {
"Bar": [
"b"
]
}
}
}
]
}
]
}
],
"terminal": true
}
]
}
}
}
}
}
@@ -0,0 +1,82 @@
(snippet) {
header {
{blocks.bar}
}
import sub_snippet {
bar {
{blocks.foo}
}
}
}
(sub_snippet) {
header {
{blocks.bar}
}
}
example.com {
import snippet {
foo {
foo a
}
bar {
bar b
}
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Bar": [
"b"
]
}
}
},
{
"handler": "headers",
"response": {
"set": {
"Foo": [
"a"
]
}
}
}
]
}
]
}
],
"terminal": true
}
]
}
}
}
}
}
@@ -0,0 +1,230 @@
localhost
respond "To intercept"
intercept {
@500 status 500
replace_status @500 400
@all status 2xx 3xx 4xx 5xx
replace_status @all {http.error.status_code}
replace_status {http.error.status_code}
@accel header X-Accel-Redirect *
handle_response @accel {
respond "Header X-Accel-Redirect!"
}
@another {
header X-Another *
}
handle_response @another {
respond "Header X-Another!"
}
@401 status 401
handle_response @401 {
respond "Status 401!"
}
handle_response {
respond "Any! This should be last in the JSON!"
}
@403 {
status 403
}
handle_response @403 {
respond "Status 403!"
}
@multi {
status 401 403
status 404
header Foo *
header Bar *
}
handle_response @multi {
respond "Headers Foo, Bar AND statuses 401, 403 and 404!"
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handle_response": [
{
"match": {
"status_code": [
500
]
},
"status_code": 400
},
{
"match": {
"status_code": [
2,
3,
4,
5
]
},
"status_code": "{http.error.status_code}"
},
{
"match": {
"headers": {
"X-Accel-Redirect": [
"*"
]
}
},
"routes": [
{
"handle": [
{
"body": "Header X-Accel-Redirect!",
"handler": "static_response"
}
]
}
]
},
{
"match": {
"headers": {
"X-Another": [
"*"
]
}
},
"routes": [
{
"handle": [
{
"body": "Header X-Another!",
"handler": "static_response"
}
]
}
]
},
{
"match": {
"status_code": [
401
]
},
"routes": [
{
"handle": [
{
"body": "Status 401!",
"handler": "static_response"
}
]
}
]
},
{
"match": {
"status_code": [
403
]
},
"routes": [
{
"handle": [
{
"body": "Status 403!",
"handler": "static_response"
}
]
}
]
},
{
"match": {
"headers": {
"Bar": [
"*"
],
"Foo": [
"*"
]
},
"status_code": [
401,
403,
404
]
},
"routes": [
{
"handle": [
{
"body": "Headers Foo, Bar AND statuses 401, 403 and 404!",
"handler": "static_response"
}
]
}
]
},
{
"status_code": "{http.error.status_code}"
},
{
"routes": [
{
"handle": [
{
"body": "Any! This should be last in the JSON!",
"handler": "static_response"
}
]
}
]
}
],
"handler": "intercept"
},
{
"body": "To intercept",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
}
]
}
}
}
}
}
@@ -0,0 +1,71 @@
:80 {
log
vars foo foo
log_append const bar
log_append vars foo
log_append placeholder {path}
log_append /only-for-this-path secret value
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":80"
],
"routes": [
{
"handle": [
{
"foo": "foo",
"handler": "vars"
}
]
},
{
"match": [
{
"path": [
"/only-for-this-path"
]
}
],
"handle": [
{
"handler": "log_append",
"key": "secret",
"value": "value"
}
]
},
{
"handle": [
{
"handler": "log_append",
"key": "const",
"value": "bar"
},
{
"handler": "log_append",
"key": "vars",
"value": "foo"
},
{
"handler": "log_append",
"key": "placeholder",
"value": "{http.request.uri.path}"
}
]
}
],
"logs": {}
}
}
}
}
}
@@ -0,0 +1,63 @@
{
log {
format append {
wrap json
fields {
wrap "foo"
}
env {env.EXAMPLE}
int 1
float 1.1
bool true
string "string"
}
}
}
:80 {
respond "Hello, World!"
}
----------
{
"logging": {
"logs": {
"default": {
"encoder": {
"fields": {
"bool": true,
"env": "{env.EXAMPLE}",
"float": 1.1,
"int": 1,
"string": "string",
"wrap": "foo"
},
"format": "append",
"wrap": {
"format": "json"
}
}
}
}
},
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":80"
],
"routes": [
{
"handle": [
{
"body": "Hello, World!",
"handler": "static_response"
}
]
}
]
}
}
}
}
}
@@ -1,7 +1,7 @@
http://localhost:2020 {
log
skip_log /first-hidden*
skip_log /second-hidden*
log_skip /first-hidden*
log_skip /second-hidden*
respond 200
}
@@ -34,7 +34,7 @@ http://localhost:2020 {
"handle": [
{
"handler": "vars",
"skip_log": true
"log_skip": true
}
],
"match": [
@@ -49,7 +49,7 @@ http://localhost:2020 {
"handle": [
{
"handler": "vars",
"skip_log": true
"log_skip": true
}
],
"match": [
@@ -99,7 +99,9 @@ http://localhost:2020 {
},
"logs": {
"logger_names": {
"localhost": ""
"localhost": [
""
]
},
"skip_unmapped_hosts": true
}
@@ -0,0 +1,151 @@
localhost {
log {
output file ./caddy.access.log
}
log health_check_log {
output file ./caddy.access.health.log
no_hostname
}
log general_log {
output file ./caddy.access.general.log
no_hostname
}
@healthCheck `header_regexp('User-Agent', '^some-regexp$') || path('/healthz*')`
handle @healthCheck {
log_name health_check_log general_log
respond "Healthy"
}
handle {
respond "Hello World"
}
}
----------
{
"logging": {
"logs": {
"default": {
"exclude": [
"http.log.access.general_log",
"http.log.access.health_check_log",
"http.log.access.log0"
]
},
"general_log": {
"writer": {
"filename": "./caddy.access.general.log",
"output": "file"
},
"include": [
"http.log.access.general_log"
]
},
"health_check_log": {
"writer": {
"filename": "./caddy.access.health.log",
"output": "file"
},
"include": [
"http.log.access.health_check_log"
]
},
"log0": {
"writer": {
"filename": "./caddy.access.log",
"output": "file"
},
"include": [
"http.log.access.log0"
]
}
}
},
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"group": "group2",
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"access_logger_names": [
"health_check_log",
"general_log"
],
"handler": "vars"
},
{
"body": "Healthy",
"handler": "static_response"
}
]
}
]
}
],
"match": [
{
"expression": {
"expr": "header_regexp('User-Agent', '^some-regexp$') || path('/healthz*')",
"name": "healthCheck"
}
}
]
},
{
"group": "group2",
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "Hello World",
"handler": "static_response"
}
]
}
]
}
]
}
]
}
],
"terminal": true
}
],
"logs": {
"logger_names": {
"localhost": [
"log0"
]
}
}
}
}
}
}
}
@@ -4,27 +4,31 @@ log {
output stdout
format filter {
wrap console
# long form, with "fields" wrapper
fields {
uri query {
replace foo REDACTED
delete bar
hash baz
}
request>headers>Authorization replace REDACTED
request>headers>Server delete
request>headers>Cookie cookie {
replace foo REDACTED
delete bar
hash baz
}
request>remote_ip ip_mask {
ipv4 24
ipv6 32
}
request>client_ip ip_mask 16 32
request>headers>Regexp regexp secret REDACTED
request>headers>Hash hash
}
# short form, flatter structure
request>headers>Authorization replace REDACTED
request>headers>Server delete
request>headers>Cookie cookie {
replace foo REDACTED
delete bar
hash baz
}
request>remote_ip ip_mask {
ipv4 24
ipv6 32
}
request>client_ip ip_mask 16 32
request>headers>Regexp regexp secret REDACTED
request>headers>Hash hash
}
}
----------
@@ -0,0 +1,117 @@
(log-both) {
log {args[0]}-json {
hostnames {args[0]}
output file /var/log/{args[0]}.log
format json
}
log {args[0]}-console {
hostnames {args[0]}
output file /var/log/{args[0]}.json
format console
}
}
*.example.com {
# Subdomains log to multiple files at once, with
# different output files and formats.
import log-both foo.example.com
import log-both bar.example.com
}
----------
{
"logging": {
"logs": {
"bar.example.com-console": {
"writer": {
"filename": "/var/log/bar.example.com.json",
"output": "file"
},
"encoder": {
"format": "console"
},
"include": [
"http.log.access.bar.example.com-console"
]
},
"bar.example.com-json": {
"writer": {
"filename": "/var/log/bar.example.com.log",
"output": "file"
},
"encoder": {
"format": "json"
},
"include": [
"http.log.access.bar.example.com-json"
]
},
"default": {
"exclude": [
"http.log.access.bar.example.com-console",
"http.log.access.bar.example.com-json",
"http.log.access.foo.example.com-console",
"http.log.access.foo.example.com-json"
]
},
"foo.example.com-console": {
"writer": {
"filename": "/var/log/foo.example.com.json",
"output": "file"
},
"encoder": {
"format": "console"
},
"include": [
"http.log.access.foo.example.com-console"
]
},
"foo.example.com-json": {
"writer": {
"filename": "/var/log/foo.example.com.log",
"output": "file"
},
"encoder": {
"format": "json"
},
"include": [
"http.log.access.foo.example.com-json"
]
}
}
},
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"*.example.com"
]
}
],
"terminal": true
}
],
"logs": {
"logger_names": {
"bar.example.com": [
"bar.example.com-json",
"bar.example.com-console"
],
"foo.example.com": [
"foo.example.com-json",
"foo.example.com-console"
]
}
}
}
}
}
}
}
@@ -75,9 +75,15 @@ example.com:8443 {
],
"logs": {
"logger_names": {
"bar.example.com": "log0",
"baz.example.com": "log1",
"foo.example.com": "log0"
"bar.example.com": [
"log0"
],
"baz.example.com": [
"log1"
],
"foo.example.com": [
"log0"
]
}
}
},
@@ -99,7 +105,9 @@ example.com:8443 {
],
"logs": {
"logger_names": {
"example.com": "log2"
"example.com": [
"log2"
]
}
}
}
@@ -76,7 +76,9 @@ http://localhost:8881 {
},
"logs": {
"logger_names": {
"localhost": "foo"
"localhost": [
"foo"
]
}
}
}
@@ -81,7 +81,9 @@ http://localhost:8881 {
},
"logs": {
"logger_names": {
"localhost": "foo"
"localhost": [
"foo"
]
}
}
}
@@ -63,7 +63,9 @@ example.com {
],
"logs": {
"logger_names": {
"one.example.com": ""
"one.example.com": [
""
]
},
"skip_hosts": [
"example.com",
@@ -46,6 +46,18 @@
@matcher12 client_ip private_ranges
respond @matcher12 "client_ip matcher with private ranges"
@matcher13 {
remote_ip 1.1.1.1
remote_ip 2.2.2.2
}
respond @matcher13 "remote_ip merged"
@matcher14 {
client_ip 1.1.1.1
client_ip 2.2.2.2
}
respond @matcher14 "client_ip merged"
}
----------
{
@@ -146,6 +158,7 @@
{
"vars_regexp": {
"{http.request.uri}": {
"name": "matcher6",
"pattern": "\\.([a-f0-9]{6})\\.(css|js)$"
}
}
@@ -161,7 +174,10 @@
{
"match": [
{
"expression": "path('/foo*') \u0026\u0026 method('GET')"
"expression": {
"expr": "path('/foo*') \u0026\u0026 method('GET')",
"name": "matcher7"
}
}
],
"handle": [
@@ -275,6 +291,42 @@
"handler": "static_response"
}
]
},
{
"match": [
{
"remote_ip": {
"ranges": [
"1.1.1.1",
"2.2.2.2"
]
}
}
],
"handle": [
{
"body": "remote_ip merged",
"handler": "static_response"
}
]
},
{
"match": [
{
"client_ip": {
"ranges": [
"1.1.1.1",
"2.2.2.2"
]
}
}
],
"handle": [
{
"body": "client_ip merged",
"handler": "static_response"
}
]
}
]
}
@@ -0,0 +1,38 @@
:8884 {
reverse_proxy {
dynamic srv {
name foo
refresh 5m
grace_period 5s
}
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":8884"
],
"routes": [
{
"handle": [
{
"dynamic_upstreams": {
"grace_period": 5000000000,
"name": "foo",
"refresh": 300000000000,
"source": "srv"
},
"handler": "reverse_proxy"
}
]
}
]
}
}
}
}
}
@@ -0,0 +1,47 @@
:8884
reverse_proxy 127.0.0.1:65535 {
transport http {
tls_trust_pool file {
pem_file ../caddy.ca.cer
}
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":8884"
],
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"transport": {
"protocol": "http",
"tls": {
"ca": {
"pem_files": [
"../caddy.ca.cer"
],
"provider": "file"
}
}
},
"upstreams": [
{
"dial": "127.0.0.1:65535"
}
]
}
]
}
]
}
}
}
}
}
@@ -0,0 +1,47 @@
:8884
reverse_proxy 127.0.0.1:65535 {
transport http {
tls_trust_pool inline {
trust_der MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ==
}
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":8884"
],
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"transport": {
"protocol": "http",
"tls": {
"ca": {
"provider": "inline",
"trusted_ca_certs": [
"MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ=="
]
}
}
},
"upstreams": [
{
"dial": "127.0.0.1:65535"
}
]
}
]
}
]
}
}
}
}
}
@@ -1,5 +1,9 @@
localhost:80
respond * "{header.content-type} {labels.0} {query.p} {path.0} {re.name.0}"
@match path_regexp ^/foo(.*)$
respond @match "{re.1}"
----------
{
"apps": {
@@ -22,6 +26,22 @@ respond * "{header.content-type} {labels.0} {query.p} {path.0} {re.name.0}"
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "{http.regexp.1}",
"handler": "static_response"
}
],
"match": [
{
"path_regexp": {
"name": "match",
"pattern": "^/foo(.*)$"
}
}
]
},
{
"handle": [
{
@@ -70,8 +70,9 @@ c.example.com {
"module": "acme"
},
{
"ca": "https://acme.zerossl.com/v2/DV90",
"email": "abc@example.com",
"module": "zerossl"
"module": "acme"
}
]
},
@@ -131,8 +131,9 @@ abc.de {
"module": "acme"
},
{
"ca": "https://acme.zerossl.com/v2/DV90",
"email": "my.email@example.com",
"module": "zerossl"
"module": "acme"
}
]
}
@@ -86,8 +86,9 @@ http://localhost:8081 {
"module": "acme"
},
{
"ca": "https://acme.zerossl.com/v2/DV90",
"email": "abc@example.com",
"module": "zerossl"
"module": "acme"
}
]
}
@@ -54,8 +54,9 @@ example.com {
"module": "acme"
},
{
"ca": "https://acme.zerossl.com/v2/DV90",
"email": "foo@bar",
"module": "zerossl"
"module": "acme"
}
]
}
@@ -0,0 +1,75 @@
localhost
respond "hello from localhost"
tls {
client_auth {
mode request
trusted_ca_cert_file ../caddy.ca.cer
verifier dummy
}
}
----------
{
"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_connection_policies": [
{
"match": {
"sni": [
"localhost"
]
},
"client_authentication": {
"ca": {
"provider": "inline",
"trusted_ca_certs": [
"MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ=="
]
},
"verifiers": [
{
"verifier": "dummy"
}
],
"mode": "request"
}
},
{}
]
}
}
}
}
}
@@ -58,14 +58,6 @@ tls {
}
},
"module": "acme"
},
{
"challenges": {
"dns": {
"ttl": 310000000000
}
},
"module": "zerossl"
}
]
}
@@ -5,7 +5,7 @@ tls {
issuer acme {
dns_ttl 5m10s
}
issuer zerossl {
issuer zerossl api_key {
dns_ttl 10m20s
}
}
@@ -65,10 +65,9 @@ tls {
"module": "acme"
},
{
"challenges": {
"dns": {
"ttl": 620000000000
}
"api_key": "api_key",
"cname_validation": {
"ttl": 620000000000
},
"module": "zerossl"
}
@@ -6,7 +6,7 @@ tls {
propagation_delay 5m10s
propagation_timeout 10m20s
}
issuer zerossl {
issuer zerossl api_key {
propagation_delay 5m30s
propagation_timeout -1
}
@@ -68,11 +68,10 @@ tls {
"module": "acme"
},
{
"challenges": {
"dns": {
"propagation_delay": 330000000000,
"propagation_timeout": -1
}
"api_key": "api_key",
"cname_validation": {
"propagation_delay": 330000000000,
"propagation_timeout": -1
},
"module": "zerossl"
}
@@ -60,15 +60,6 @@ tls {
}
},
"module": "acme"
},
{
"challenges": {
"dns": {
"propagation_delay": 310000000000,
"propagation_timeout": 620000000000
}
},
"module": "zerossl"
}
]
}
@@ -0,0 +1,106 @@
:9080
uri query +foo bar
uri query -baz
uri query taz test
uri query key=value example
uri query changethis>changed
uri query {
findme value replacement
+foo1 baz
}
respond "{query}"
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":9080"
],
"routes": [
{
"handle": [
{
"handler": "rewrite",
"query": {
"add": [
{
"key": "foo",
"val": "bar"
}
]
}
},
{
"handler": "rewrite",
"query": {
"delete": [
"baz"
]
}
},
{
"handler": "rewrite",
"query": {
"set": [
{
"key": "taz",
"val": "test"
}
]
}
},
{
"handler": "rewrite",
"query": {
"set": [
{
"key": "key=value",
"val": "example"
}
]
}
},
{
"handler": "rewrite",
"query": {
"rename": [
{
"key": "changethis",
"val": "changed"
}
]
}
},
{
"handler": "rewrite",
"query": {
"add": [
{
"key": "foo1",
"val": "baz"
}
],
"replace": [
{
"key": "findme",
"replace": "replacement",
"search_regexp": "value"
}
]
}
},
{
"body": "{http.request.uri.query}",
"handler": "static_response"
}
]
}
]
}
}
}
}
}
@@ -10,6 +10,8 @@ import (
"testing"
"github.com/caddyserver/caddy/v2/caddytest"
_ "github.com/caddyserver/caddy/v2/internal/testmocks"
)
func TestCaddyfileAdaptToJSON(t *testing.T) {
+295 -78
View File
@@ -1,6 +1,7 @@
package integration
import (
"fmt"
"net/http"
"net/url"
"testing"
@@ -10,62 +11,63 @@ import (
func TestRespond(t *testing.T) {
// arrange
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
localhost:9080 {
localhost:{$TESTING_CADDY_PORT_ONE} {
respond /version 200 {
body "hello from localhost"
}
}
}
`, "caddyfile")
// act and assert
tester.AssertGetResponse("http://localhost:9080/version", 200, "hello from localhost")
harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne()), 200, "hello from localhost")
}
func TestRedirect(t *testing.T) {
// arrange
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
localhost:9080 {
redir / http://localhost:9080/hello 301
localhost:{$TESTING_CADDY_PORT_ONE} {
redir / http://localhost:{$TESTING_CADDY_PORT_ONE}/hello 301
respond /hello 200 {
body "hello from localhost"
}
}
}
`, "caddyfile")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
// act and assert
tester.AssertRedirect("http://localhost:9080/", "http://localhost:9080/hello", 301)
harness.AssertRedirect(target, target+"hello", 301)
// follow redirect
tester.AssertGetResponse("http://localhost:9080/", 200, "hello from localhost")
harness.AssertGetResponse(target, 200, "hello from localhost")
}
func TestDuplicateHosts(t *testing.T) {
// act and assert
caddytest.AssertLoadError(t,
`
localhost:9080 {
localhost:{$TESTING_CADDY_PORT_ONE} {
}
localhost:9080 {
localhost:{$TESTING_CADDY_PORT_ONE} {
}
`,
"caddyfile",
@@ -80,18 +82,18 @@ func TestReadCookie(t *testing.T) {
}
// arrange
tester := caddytest.NewTester(t)
tester.Client.Jar.SetCookies(localhost, []*http.Cookie{&cookie})
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.Client().Jar.SetCookies(localhost, []*http.Cookie{&cookie})
harness.LoadConfig(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
localhost:9080 {
localhost:{$TESTING_CADDY_PORT_ONE} {
templates {
root testdata
}
@@ -102,21 +104,22 @@ func TestReadCookie(t *testing.T) {
`, "caddyfile")
// act and assert
tester.AssertGetResponse("http://localhost:9080/cookie.html", 200, "<h2>Cookie.ClientName caddytest</h2>")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"cookie.html", 200, "<h2>Cookie.ClientName caddytest</h2>")
}
func TestReplIndex(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
localhost:9080 {
localhost:{$TESTING_CADDY_PORT_ONE} {
templates {
root testdata
}
@@ -128,7 +131,8 @@ func TestReplIndex(t *testing.T) {
`, "caddyfile")
// act and assert
tester.AssertGetResponse("http://localhost:9080/", 200, "")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target, 200, "")
}
func TestInvalidPrefix(t *testing.T) {
@@ -481,49 +485,259 @@ func TestValidPrefix(t *testing.T) {
}
func TestUriReplace(t *testing.T) {
tester := caddytest.NewTester(t)
harness := caddytest.StartHarness(t)
tester.InitServer(`
harness.LoadConfig(`
{
admin localhost:2999
http_port 9080
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
:9080
:{$TESTING_CADDY_PORT_ONE}
uri replace "\}" %7D
uri replace "\{" %7B
respond "{query}"`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/endpoint?test={%20content%20}", 200, "test=%7B%20content%20%7D")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"endpoint?test={%20content%20}", 200, "test=%7B%20content%20%7D")
}
func TestUriOps(t *testing.T) {
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
:{$TESTING_CADDY_PORT_ONE}
uri query +foo bar
uri query -baz
uri query taz test
uri query key=value example
uri query changethis>changed
respond "{query}"`, "caddyfile")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"endpoint?foo=bar0&baz=buz&taz=nottest&changethis=val", 200, "changed=val&foo=bar0&foo=bar&key%3Dvalue=example&taz=test")
}
// Tests the `http.request.local.port` placeholder.
// We don't test the very similar `http.request.local.host` placeholder,
// because depending on the host the test is running on, localhost might
// refer to 127.0.0.1 or ::1.
// TODO: Test each http version separately (especially http/3)
func TestHttpRequestLocalPortPlaceholder(t *testing.T) {
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
:{$TESTING_CADDY_PORT_ONE}
respond "{http.request.local.port}"`, "caddyfile")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target, 200, fmt.Sprintf("%d", harness.Tester().PortOne()))
}
func TestSetThenAddQueryParams(t *testing.T) {
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
:{$TESTING_CADDY_PORT_ONE}
uri query foo bar
uri query +foo baz
respond "{query}"`, "caddyfile")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"endpoint", 200, "foo=bar&foo=baz")
}
func TestSetThenDeleteParams(t *testing.T) {
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
:{$TESTING_CADDY_PORT_ONE}
uri query bar foo{query.foo}
uri query -foo
respond "{query}"`, "caddyfile")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"endpoint?foo=bar", 200, "bar=foobar")
}
func TestRenameAndOtherOps(t *testing.T) {
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
:{$TESTING_CADDY_PORT_ONE}
uri query foo>bar
uri query bar taz
uri query +bar baz
respond "{query}"`, "caddyfile")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"endpoint?foo=bar", 200, "bar=taz&bar=baz")
}
func TestReplaceOps(t *testing.T) {
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
:{$TESTING_CADDY_PORT_ONE}
uri query foo bar baz
respond "{query}"`, "caddyfile")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"endpoint?foo=bar", 200, "foo=baz")
}
func TestReplaceWithReplacementPlaceholder(t *testing.T) {
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
:{$TESTING_CADDY_PORT_ONE}
uri query foo bar {query.placeholder}
respond "{query}"`, "caddyfile")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"endpoint?placeholder=baz&foo=bar", 200, "foo=baz&placeholder=baz")
}
func TestReplaceWithKeyPlaceholder(t *testing.T) {
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
:{$TESTING_CADDY_PORT_ONE}
uri query {query.placeholder} bar baz
respond "{query}"`, "caddyfile")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"endpoint?placeholder=foo&foo=bar", 200, "foo=baz&placeholder=foo")
}
func TestPartialReplacement(t *testing.T) {
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
:{$TESTING_CADDY_PORT_ONE}
uri query foo ar az
respond "{query}"`, "caddyfile")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"endpoint?foo=bar", 200, "foo=baz")
}
func TestNonExistingSearch(t *testing.T) {
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
:{$TESTING_CADDY_PORT_ONE}
uri query foo var baz
respond "{query}"`, "caddyfile")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"endpoint?foo=bar", 200, "foo=bar")
}
func TestReplaceAllOps(t *testing.T) {
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
:{$TESTING_CADDY_PORT_ONE}
uri query * bar baz
respond "{query}"`, "caddyfile")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"endpoint?foo=bar&baz=bar", 200, "baz=baz&foo=baz")
}
func TestUriOpsBlock(t *testing.T) {
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
:{$TESTING_CADDY_PORT_ONE}
uri query {
+foo bar
-baz
taz test
}
respond "{query}"`, "caddyfile")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"endpoint?foo=bar0&baz=buz&taz=nottest", 200, "foo=bar0&foo=bar&taz=test")
}
func TestHandleErrorSimpleCodes(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`{
admin localhost:2999
http_port 9080
harness := caddytest.StartHarness(t)
harness.LoadConfig(`{
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
localhost:9080 {
localhost:{$TESTING_CADDY_PORT_ONE} {
root * /srv
error /private* "Unauthorized" 410
error /hidden* "Not found" 404
handle_errors 404 410 {
respond "404 or 410 error"
}
}`, "caddyfile")
// act and assert
tester.AssertGetResponse("http://localhost:9080/private", 410, "404 or 410 error")
tester.AssertGetResponse("http://localhost:9080/hidden", 404, "404 or 410 error")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"private", 410, "404 or 410 error")
harness.AssertGetResponse(target+"hidden", 404, "404 or 410 error")
}
func TestHandleErrorRange(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`{
admin localhost:2999
http_port 9080
harness := caddytest.StartHarness(t)
harness.LoadConfig(`{
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
localhost:9080 {
localhost:{$TESTING_CADDY_PORT_ONE} {
root * /srv
error /private* "Unauthorized" 410
error /hidden* "Not found" 404
@@ -533,17 +747,18 @@ func TestHandleErrorRange(t *testing.T) {
}
}`, "caddyfile")
// act and assert
tester.AssertGetResponse("http://localhost:9080/private", 410, "Error in the [400 .. 499] range")
tester.AssertGetResponse("http://localhost:9080/hidden", 404, "Error in the [400 .. 499] range")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"private", 410, "Error in the [400 .. 499] range")
harness.AssertGetResponse(target+"hidden", 404, "Error in the [400 .. 499] range")
}
func TestHandleErrorSort(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`{
admin localhost:2999
http_port 9080
harness := caddytest.StartHarness(t)
harness.LoadConfig(`{
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
localhost:9080 {
localhost:{$TESTING_CADDY_PORT_ONE} {
root * /srv
error /private* "Unauthorized" 410
error /hidden* "Not found" 404
@@ -557,17 +772,18 @@ func TestHandleErrorSort(t *testing.T) {
}
}`, "caddyfile")
// act and assert
tester.AssertGetResponse("http://localhost:9080/internalerr", 500, "Fallback route: code outside the [400..499] range")
tester.AssertGetResponse("http://localhost:9080/hidden", 404, "Error in the [400 .. 499] range")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"internalerr", 500, "Fallback route: code outside the [400..499] range")
harness.AssertGetResponse(target+"hidden", 404, "Error in the [400 .. 499] range")
}
func TestHandleErrorRangeAndCodes(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`{
admin localhost:2999
http_port 9080
harness := caddytest.StartHarness(t)
harness.LoadConfig(`{
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
}
localhost:9080 {
localhost:{$TESTING_CADDY_PORT_ONE} {
root * /srv
error /private* "Unauthorized" 410
error /threehundred* "Moved Permanently" 301
@@ -581,9 +797,10 @@ func TestHandleErrorRangeAndCodes(t *testing.T) {
}
}`, "caddyfile")
// act and assert
tester.AssertGetResponse("http://localhost:9080/internalerr", 500, "Error code is equal to 500 or in the [300..399] range")
tester.AssertGetResponse("http://localhost:9080/threehundred", 301, "Error code is equal to 500 or in the [300..399] range")
tester.AssertGetResponse("http://localhost:9080/private", 410, "Error in the [400 .. 499] range")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target+"internalerr", 500, "Error code is equal to 500 or in the [300..399] range")
harness.AssertGetResponse(target+"threehundred", 301, "Error code is equal to 500 or in the [300..399] range")
harness.AssertGetResponse(target+"private", 410, "Error in the [400 .. 499] range")
}
func TestInvalidSiteAddressesAsDirectives(t *testing.T) {
+37 -8
View File
@@ -1,6 +1,8 @@
package integration
import (
"bytes"
"fmt"
"net/http"
"testing"
@@ -8,24 +10,51 @@ import (
)
func TestBrowse(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
http://localhost:9080 {
http://localhost:{$TESTING_CADDY_PORT_ONE} {
file_server browse
}
`, "caddyfile")
req, err := http.NewRequest(http.MethodGet, "http://localhost:9080/", nil)
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), nil)
if err != nil {
t.Fail()
return
}
tester.AssertResponseCode(req, 200)
harness.AssertResponseCode(req, 200)
}
func TestRespondWithJSON(t *testing.T) {
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
skip_install_trust
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
localhost {
respond {http.request.body}
}
`, "caddyfile")
res, _ := harness.AssertPostResponseBody(fmt.Sprintf("https://localhost:%d/", harness.Tester().PortTwo()),
nil,
bytes.NewBufferString(`{
"greeting": "Hello, world!"
}`), 200, `{
"greeting": "Hello, world!"
}`)
if res.Header.Get("Content-Type") != "application/json" {
t.Errorf("expected Content-Type to be application/json, but was %s", res.Header.Get("Content-Type"))
}
}
+35
View File
@@ -0,0 +1,35 @@
package integration
import (
"fmt"
"testing"
"github.com/caddyserver/caddy/v2/caddytest"
)
func TestIntercept(t *testing.T) {
harness := caddytest.StartHarness(t)
harness.LoadConfig(`{
skip_install_trust
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
localhost:{$TESTING_CADDY_PORT_ONE} {
respond /intercept "I'm a teapot" 408
respond /no-intercept "I'm not a teapot"
intercept {
@teapot status 408
handle_response @teapot {
respond /intercept "I'm a combined coffee/tea pot that is temporarily out of coffee" 503
}
}
}
`, "caddyfile")
harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/intercept", harness.Tester().PortOne()), 503, "I'm a combined coffee/tea pot that is temporarily out of coffee")
harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/no-intercept", harness.Tester().PortOne()), 200, "I'm not a teapot")
}
@@ -0,0 +1,70 @@
package integration
import (
"testing"
"github.com/caddyserver/caddy/v2/caddytest"
)
func TestLeafCertLoaders(t *testing.T) {
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
"admin": {
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"http": {
"http_port": {$TESTING_CADDY_PORT_ONE},
"https_port": {$TESTING_CADDY_PORT_TWO},
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
":{$TESTING_CADDY_PORT_TWO}"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"terminal": true
}
],
"tls_connection_policies": [
{
"client_authentication": {
"verifiers": [
{
"verifier": "leaf",
"leaf_certs_loaders": [
{
"loader": "file",
"files": ["../leafcert.pem"]
},
{
"loader": "folder",
"folders": ["../"]
},
{
"loader": "storage"
},
{
"loader": "pem"
}
]
}
]
}
}
]
}
}
}
}
}`, "json")
}
+13 -13
View File
@@ -12,7 +12,7 @@ import (
"github.com/caddyserver/caddy/v2/caddytest"
)
func setupListenerWrapperTest(t *testing.T, handlerFunc http.HandlerFunc) *caddytest.Tester {
func setupListenerWrapperTest(t *testing.T, handlerFunc http.HandlerFunc) *caddytest.TestHarness {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to listen: %s", err)
@@ -28,15 +28,15 @@ func setupListenerWrapperTest(t *testing.T, handlerFunc http.HandlerFunc) *caddy
_ = srv.Close()
_ = l.Close()
})
tester := caddytest.NewTester(t)
tester.InitServer(fmt.Sprintf(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(fmt.Sprintf(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
local_certs
servers :9443 {
servers :{$TESTING_CADDY_PORT_TWO} {
listener_wrappers {
http_redirect
tls
@@ -47,7 +47,7 @@ func setupListenerWrapperTest(t *testing.T, handlerFunc http.HandlerFunc) *caddy
reverse_proxy %s
}
`, l.Addr().String()), "caddyfile")
return tester
return harness
}
func TestHTTPRedirectWrapperWithLargeUpload(t *testing.T) {
@@ -56,7 +56,7 @@ func TestHTTPRedirectWrapperWithLargeUpload(t *testing.T) {
body := make([]byte, uploadSize)
rand.New(rand.NewSource(0)).Read(body)
tester := setupListenerWrapperTest(t, func(writer http.ResponseWriter, request *http.Request) {
harness := setupListenerWrapperTest(t, func(writer http.ResponseWriter, request *http.Request) {
buf := new(bytes.Buffer)
_, err := buf.ReadFrom(request.Body)
if err != nil {
@@ -69,7 +69,7 @@ func TestHTTPRedirectWrapperWithLargeUpload(t *testing.T) {
writer.WriteHeader(http.StatusNoContent)
})
resp, err := tester.Client.Post("https://localhost:9443", "application/octet-stream", bytes.NewReader(body))
resp, err := harness.Client().Post(fmt.Sprintf("https://localhost:%d", harness.Tester().PortTwo()), "application/octet-stream", bytes.NewReader(body))
if err != nil {
t.Fatalf("failed to post: %s", err)
}
@@ -80,14 +80,14 @@ func TestHTTPRedirectWrapperWithLargeUpload(t *testing.T) {
}
func TestLargeHttpRequest(t *testing.T) {
tester := setupListenerWrapperTest(t, func(writer http.ResponseWriter, request *http.Request) {
harness := setupListenerWrapperTest(t, func(writer http.ResponseWriter, request *http.Request) {
t.Fatal("not supposed to handle a request")
})
// We never read the body in any way, set an extra long header instead.
req, _ := http.NewRequest("POST", "http://localhost:9443", nil)
req, _ := http.NewRequest("POST", fmt.Sprintf("http://localhost:%d", harness.Tester().PortTwo()), nil)
req.Header.Set("Long-Header", strings.Repeat("X", 1024*1024))
_, err := tester.Client.Do(req)
_, err := harness.Client().Do(req)
if err == nil {
t.Fatal("not supposed to succeed")
}
+31 -30
View File
@@ -2,6 +2,7 @@ package integration
import (
"bytes"
"fmt"
"testing"
"github.com/caddyserver/caddy/v2/caddytest"
@@ -9,16 +10,16 @@ import (
func TestMap(t *testing.T) {
// arrange
tester := caddytest.NewTester(t)
tester.InitServer(`{
harness := caddytest.StartHarness(t)
harness.LoadConfig(`{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
localhost:9080 {
localhost:{$TESTING_CADDY_PORT_ONE} {
map {http.request.method} {dest-1} {dest-2} {
default unknown1 unknown2
@@ -28,50 +29,50 @@ func TestMap(t *testing.T) {
respond /version 200 {
body "hello from localhost {dest-1} {dest-2}"
}
}
}
`, "caddyfile")
// act and assert
tester.AssertGetResponse("http://localhost:9080/version", 200, "hello from localhost GET-called unknown2")
tester.AssertPostResponseBody("http://localhost:9080/version", []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost post-called foobar")
harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne()), 200, "hello from localhost GET-called unknown2")
harness.AssertPostResponseBody(fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne()), []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost post-called foobar")
}
func TestMapRespondWithDefault(t *testing.T) {
// arrange
tester := caddytest.NewTester(t)
tester.InitServer(`{
harness := caddytest.StartHarness(t)
harness.LoadConfig(`{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
}
localhost:9080 {
localhost:{$TESTING_CADDY_PORT_ONE} {
map {http.request.method} {dest-name} {
default unknown
GET get-called
}
respond /version 200 {
body "hello from localhost {dest-name}"
}
}
}
`, "caddyfile")
// act and assert
tester.AssertGetResponse("http://localhost:9080/version", 200, "hello from localhost get-called")
tester.AssertPostResponseBody("http://localhost:9080/version", []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost unknown")
harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne()), 200, "hello from localhost get-called")
harness.AssertPostResponseBody(fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne()), []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost unknown")
}
func TestMapAsJSON(t *testing.T) {
// arrange
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"pki": {
@@ -82,12 +83,12 @@ func TestMapAsJSON(t *testing.T) {
}
},
"http": {
"http_port": 9080,
"https_port": 9443,
"http_port": {$TESTING_CADDY_PORT_ONE},
"https_port": {$TESTING_CADDY_PORT_TWO},
"servers": {
"srv0": {
"listen": [
":9080"
":{$TESTING_CADDY_PORT_ONE}"
],
"routes": [
{
@@ -145,7 +146,7 @@ func TestMapAsJSON(t *testing.T) {
}
}
}`, "json")
tester.AssertGetResponse("http://localhost:9080/version", 200, "hello from localhost get-called")
tester.AssertPostResponseBody("http://localhost:9080/version", []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost post-called")
target := fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne())
harness.AssertGetResponse(target, 200, "hello from localhost get-called")
harness.AssertPostResponseBody(target, []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost post-called")
}
+49 -45
View File
@@ -14,11 +14,11 @@ import (
)
func TestSRVReverseProxy(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"pki": {
@@ -87,11 +87,11 @@ func TestDialWithPlaceholderUnix(t *testing.T) {
})
runtime.Gosched() // Allow other goroutines to run
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"pki": {
@@ -135,15 +135,15 @@ func TestDialWithPlaceholderUnix(t *testing.T) {
return
}
req.Header.Set("X-Caddy-Upstream-Dial", socketName)
tester.AssertResponse(req, 200, "Hello, World!")
harness.AssertResponse(req, 200, "Hello, World!")
}
func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"pki": {
@@ -186,7 +186,7 @@ func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
},
"srv1": {
"listen": [
":9080"
":{$TESTING_CADDY_PORT_ONE}"
],
"routes": [
{
@@ -199,7 +199,7 @@ func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
],
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
@@ -223,21 +223,21 @@ func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
}
`, "json")
req, err := http.NewRequest(http.MethodGet, "http://localhost:9080", nil)
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d", harness.Tester().PortOne()), nil)
if err != nil {
t.Fail()
return
}
req.Header.Set("X-Caddy-Upstream-Dial", "localhost:18080")
tester.AssertResponse(req, 200, "Hello, World!")
harness.AssertResponse(req, 200, "Hello, World!")
}
func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"pki": {
@@ -280,7 +280,7 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
},
"srv1": {
"listen": [
":9080"
":{$TESTING_CADDY_PORT_ONE}"
],
"routes": [
{
@@ -293,7 +293,7 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
],
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
@@ -317,23 +317,23 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
}
`, "json")
req, err := http.NewRequest(http.MethodGet, "http://localhost:9080", nil)
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d", harness.Tester().PortOne()), nil)
if err != nil {
t.Fail()
return
}
req.Header.Set("X-Caddy-Upstream-Dial", "localhost")
tester.AssertResponse(req, 200, "Hello, World!")
harness.AssertResponse(req, 200, "Hello, World!")
}
func TestReverseProxyHealthCheck(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
http://localhost:2020 {
@@ -342,27 +342,30 @@ func TestReverseProxyHealthCheck(t *testing.T) {
http://localhost:2021 {
respond "ok"
}
http://localhost:9080 {
http://localhost:{$TESTING_CADDY_PORT_ONE} {
reverse_proxy {
to localhost:2020
health_uri /health
health_port 2021
health_interval 10ms
health_timeout 100ms
health_passes 1
health_fails 1
}
}
`, "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!")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target, 200, "Hello, World!")
}
func TestReverseProxyHealthCheckUnixSocket(t *testing.T) {
if runtime.GOOS == "windows" {
t.SkipNow()
}
tester := caddytest.NewTester(t)
harness := caddytest.StartHarness(t)
f, err := os.CreateTemp("", "*.sock")
if err != nil {
t.Errorf("failed to create TempFile: %s", err)
@@ -393,18 +396,18 @@ func TestReverseProxyHealthCheckUnixSocket(t *testing.T) {
})
runtime.Gosched() // Allow other goroutines to run
tester.InitServer(fmt.Sprintf(`
harness.LoadConfig(fmt.Sprintf(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
http://localhost:9080 {
http://localhost:{$TESTING_CADDY_PORT_ONE} {
reverse_proxy {
to unix/%s
health_uri /health
health_port 2021
health_interval 2s
@@ -413,14 +416,15 @@ func TestReverseProxyHealthCheckUnixSocket(t *testing.T) {
}
`, socketName), "caddyfile")
tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
harness.AssertGetResponse(target, 200, "Hello, World!")
}
func TestReverseProxyHealthCheckUnixSocketWithoutPort(t *testing.T) {
if runtime.GOOS == "windows" {
t.SkipNow()
}
tester := caddytest.NewTester(t)
harness := caddytest.StartHarness(t)
f, err := os.CreateTemp("", "*.sock")
if err != nil {
t.Errorf("failed to create TempFile: %s", err)
@@ -451,18 +455,18 @@ func TestReverseProxyHealthCheckUnixSocketWithoutPort(t *testing.T) {
})
runtime.Gosched() // Allow other goroutines to run
tester.InitServer(fmt.Sprintf(`
harness.LoadConfig(fmt.Sprintf(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
admin {$TESTING_CADDY_ADMIN_BIND}
http_port {$TESTING_CADDY_PORT_ONE}
https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
http://localhost:9080 {
http://localhost:{$TESTING_CADDY_PORT_ONE} {
reverse_proxy {
to unix/%s
health_uri /health
health_interval 2s
health_timeout 5s
@@ -470,5 +474,5 @@ func TestReverseProxyHealthCheckUnixSocketWithoutPort(t *testing.T) {
}
`, socketName), "caddyfile")
tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), 200, "Hello, World!")
}
+25 -21
View File
@@ -1,6 +1,7 @@
package integration
import (
"fmt"
"testing"
"github.com/caddyserver/caddy/v2/caddytest"
@@ -8,20 +9,20 @@ import (
func TestDefaultSNI(t *testing.T) {
// arrange
tester := caddytest.NewTester(t)
tester.InitServer(`{
harness := caddytest.StartHarness(t)
harness.LoadConfig(`{
"admin": {
"listen": "localhost:2999"
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"http": {
"http_port": 9080,
"https_port": 9443,
"http_port": {$TESTING_CADDY_PORT_ONE},
"https_port": {$TESTING_CADDY_PORT_TWO},
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
":9443"
":{$TESTING_CADDY_PORT_TWO}"
],
"routes": [
{
@@ -102,26 +103,27 @@ func TestDefaultSNI(t *testing.T) {
// act and assert
// makes a request with no sni
tester.AssertGetResponse("https://127.0.0.1:9443/version", 200, "hello from a.caddy.localhost")
target := fmt.Sprintf("https://127.0.0.1:%d/", harness.Tester().PortTwo())
harness.AssertGetResponse(target+"version", 200, "hello from a.caddy.localhost")
}
func TestDefaultSNIWithNamedHostAndExplicitIP(t *testing.T) {
// arrange
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"http": {
"http_port": 9080,
"https_port": 9443,
"http_port": {$TESTING_CADDY_PORT_ONE},
"https_port": {$TESTING_CADDY_PORT_TWO},
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
":9443"
":{$TESTING_CADDY_PORT_TWO}"
],
"routes": [
{
@@ -206,26 +208,27 @@ func TestDefaultSNIWithNamedHostAndExplicitIP(t *testing.T) {
// act and assert
// makes a request with no sni
tester.AssertGetResponse("https://127.0.0.1:9443/version", 200, "hello from a")
target := fmt.Sprintf("https://127.0.0.1:%d/", harness.Tester().PortTwo())
harness.AssertGetResponse(target+"version", 200, "hello from a")
}
func TestDefaultSNIWithPortMappingOnly(t *testing.T) {
// arrange
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"http": {
"http_port": 9080,
"https_port": 9443,
"http_port": {$TESTING_CADDY_PORT_ONE},
"https_port": {$TESTING_CADDY_PORT_TWO},
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
":9443"
":{$TESTING_CADDY_PORT_TWO}"
],
"routes": [
{
@@ -282,7 +285,8 @@ func TestDefaultSNIWithPortMappingOnly(t *testing.T) {
// act and assert
// makes a request with no sni
tester.AssertGetResponse("https://127.0.0.1:9443/version", 200, "hello from a.caddy.localhost")
target := fmt.Sprintf("https://127.0.0.1:%d/", harness.Tester().PortTwo())
harness.AssertGetResponse(target+"version", 200, "hello from a.caddy.localhost")
}
func TestHttpOnlyOnDomainWithSNI(t *testing.T) {
+25 -32
View File
@@ -20,21 +20,21 @@ import (
// (see https://github.com/caddyserver/caddy/issues/3556 for use case)
func TestH2ToH2CStream(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"http": {
"http_port": 9080,
"https_port": 9443,
"grace_period": 1,
"http_port": {$TESTING_CADDY_PORT_ONE},
"https_port": {$TESTING_CADDY_PORT_TWO},
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
":9443"
":{$TESTING_CADDY_PORT_TWO}"
],
"routes": [
{
@@ -102,7 +102,7 @@ func TestH2ToH2CStream(t *testing.T) {
expectedBody := "some data to be echoed"
// start the server
server := testH2ToH2CStreamServeH2C(t)
server := testH2ToH2CStreamServeH2C(harness, t)
go server.ListenAndServe()
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
@@ -116,7 +116,7 @@ func TestH2ToH2CStream(t *testing.T) {
Body: io.NopCloser(r),
URL: &url.URL{
Scheme: "https",
Host: "127.0.0.1:9443",
Host: fmt.Sprintf("127.0.0.1:%d", harness.Tester().PortTwo()),
Path: "/tov2ray",
},
Proto: "HTTP/2",
@@ -127,7 +127,7 @@ func TestH2ToH2CStream(t *testing.T) {
// Disable any compression method from server.
req.Header.Set("Accept-Encoding", "identity")
resp := tester.AssertResponseCode(req, http.StatusOK)
resp := harness.AssertResponseCode(req, http.StatusOK)
if resp.StatusCode != http.StatusOK {
return
}
@@ -149,7 +149,7 @@ func TestH2ToH2CStream(t *testing.T) {
}
}
func testH2ToH2CStreamServeH2C(t *testing.T) *http.Server {
func testH2ToH2CStreamServeH2C(harness *caddytest.TestHarness, t *testing.T) *http.Server {
h2s := &http2.Server{}
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rstring, err := httputil.DumpRequest(r, false)
@@ -163,7 +163,7 @@ func testH2ToH2CStreamServeH2C(t *testing.T) *http.Server {
return
}
if r.Host != "127.0.0.1:9443" {
if r.Host != fmt.Sprintf("127.0.0.1:%d", harness.Tester().PortTwo()) {
t.Errorf("r.Host doesn't match, %v!", r.Host)
w.WriteHeader(http.StatusNotFound)
return
@@ -204,28 +204,21 @@ func testH2ToH2CStreamServeH2C(t *testing.T) *http.Server {
// (see https://github.com/caddyserver/caddy/issues/3606 for use case)
func TestH2ToH1ChunkedResponse(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
harness := caddytest.StartHarness(t)
harness.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"logging": {
"logs": {
"default": {
"level": "DEBUG"
}
}
},
"apps": {
"http": {
"http_port": 9080,
"https_port": 9443,
"grace_period": 1,
"http_port": {$TESTING_CADDY_PORT_ONE},
"https_port": {$TESTING_CADDY_PORT_TWO},
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
":9443"
":{$TESTING_CADDY_PORT_TWO}"
],
"routes": [
{
@@ -312,7 +305,7 @@ func TestH2ToH1ChunkedResponse(t *testing.T) {
}
// start the server
server := testH2ToH1ChunkedResponseServeH1(t)
server := testH2ToH1ChunkedResponseServeH1(harness, t)
go server.ListenAndServe()
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
@@ -326,7 +319,7 @@ func TestH2ToH1ChunkedResponse(t *testing.T) {
Body: io.NopCloser(r),
URL: &url.URL{
Scheme: "https",
Host: "127.0.0.1:9443",
Host: fmt.Sprintf("127.0.0.1:%d", harness.Tester().PortTwo()),
Path: "/tov2ray",
},
Proto: "HTTP/2",
@@ -334,13 +327,13 @@ func TestH2ToH1ChunkedResponse(t *testing.T) {
ProtoMinor: 0,
Header: make(http.Header),
}
// underlying transport will automaticlly add gzip
// underlying transport will automatically add gzip
// req.Header.Set("Accept-Encoding", "gzip")
go func() {
fmt.Fprint(w, expectedBody)
w.Close()
}()
resp := tester.AssertResponseCode(req, http.StatusOK)
resp := harness.AssertResponseCode(req, http.StatusOK)
if resp.StatusCode != http.StatusOK {
return
}
@@ -358,9 +351,9 @@ func TestH2ToH1ChunkedResponse(t *testing.T) {
}
}
func testH2ToH1ChunkedResponseServeH1(t *testing.T) *http.Server {
func testH2ToH1ChunkedResponseServeH1(harness *caddytest.TestHarness, t *testing.T) *http.Server {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Host != "127.0.0.1:9443" {
if r.Host != fmt.Sprintf("127.0.0.1:%d", harness.Tester().PortTwo()) {
t.Errorf("r.Host doesn't match, %v!", r.Host)
w.WriteHeader(http.StatusNotFound)
return
+1
View File
@@ -0,0 +1 @@
foo
+15
View File
@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICUTCCAfugAwIBAgIBADANBgkqhkiG9w0BAQQFADBXMQswCQYDVQQGEwJDTjEL
MAkGA1UECBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMC
VU4xFDASBgNVBAMTC0hlcm9uZyBZYW5nMB4XDTA1MDcxNTIxMTk0N1oXDTA1MDgx
NDIxMTk0N1owVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlBOMQswCQYDVQQHEwJD
TjELMAkGA1UEChMCT04xCzAJBgNVBAsTAlVOMRQwEgYDVQQDEwtIZXJvbmcgWWFu
ZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCp5hnG7ogBhtlynpOS21cBewKE/B7j
V14qeyslnr26xZUsSVko36ZnhiaO/zbMOoRcKK9vEcgMtcLFuQTWDl3RAgMBAAGj
gbEwga4wHQYDVR0OBBYEFFXI70krXeQDxZgbaCQoR4jUDncEMH8GA1UdIwR4MHaA
FFXI70krXeQDxZgbaCQoR4jUDncEoVukWTBXMQswCQYDVQQGEwJDTjELMAkGA1UE
CBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMCVU4xFDAS
BgNVBAMTC0hlcm9uZyBZYW5nggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEE
BQADQQA/ugzBrjjK9jcWnDVfGHlk3icNRq0oV7Ri32z/+HQX67aRfgZu7KWdI+Ju
Wm7DCfrPNGVwFWUQOmsPue9rZBgO
-----END CERTIFICATE-----
+241
View File
@@ -0,0 +1,241 @@
package caddytest
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"path"
"regexp"
"runtime"
"testing"
"github.com/stretchr/testify/require"
)
// use the convention to replace /[certificatename].[crt|key] with the full path
// this helps reduce the noise in test configurations and also allow this
// to run in any path
func prependCaddyFilePath(rawConfig string) string {
r := matchKey.ReplaceAllString(rawConfig, getIntegrationDir()+"$1")
r = matchCert.ReplaceAllString(r, getIntegrationDir()+"$1")
return r
}
func getIntegrationDir() string {
_, filename, _, ok := runtime.Caller(1)
if !ok {
panic("unable to determine the current file path")
}
return path.Dir(filename)
}
var (
matchKey = regexp.MustCompile(`(/[\w\d\.]+\.key)`)
matchCert = regexp.MustCompile(`(/[\w\d\.]+\.crt)`)
)
type TestHarness struct {
t testing.TB
tester *Tester
}
// StartHarness creates and starts a test harness environment which spans the lifetime a single caddy instance
// This is used for the integration tests
func StartHarness(t *testing.T) *TestHarness {
if testing.Short() {
t.SkipNow()
return nil
}
o := &TestHarness{t: t}
o.init()
return o
}
func (tc *TestHarness) Tester() *Tester {
return tc.tester
}
func (tc *TestHarness) Client() *http.Client {
return tc.tester.Client
}
func (tc *TestHarness) LoadConfig(rawConfig, configType string) {
rawConfig = prependCaddyFilePath(rawConfig)
err := tc.tester.LoadConfig(rawConfig, configType)
require.NoError(tc.t, err)
}
func (tc *TestHarness) init() {
// start the server
tester, err := NewTester()
if err != nil {
tc.t.Errorf("Failed to create caddy tester: %s", err)
return
}
tc.tester = tester
err = tc.tester.LaunchCaddy()
if err != nil {
tc.t.Errorf("Failed to launch caddy server: %s", err)
tc.t.FailNow()
return
}
// cleanup
tc.t.Cleanup(func() {
func() {
if tc.t.Failed() {
res, err := http.Get(fmt.Sprintf("http://localhost:%d/config/", tc.tester.adminPort))
if err != nil {
tc.t.Log("unable to read the current config")
return
}
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
var out bytes.Buffer
_ = json.Indent(&out, body, "", " ")
tc.t.Logf("----------- failed with config -----------\n%s", out.String())
}
}()
// shutdown server after extracing the config
err = tc.tester.CleanupCaddy()
if err != nil {
tc.t.Errorf("failed to clean up caddy instance: %s", err)
tc.t.FailNow()
}
})
}
// AssertRedirect makes a request and asserts the redirection happens
func (tc *TestHarness) AssertRedirect(requestURI string, expectedToLocation string, expectedStatusCode int) *http.Response {
redirectPolicyFunc := func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
// using the existing client, we override the check redirect policy for this test
old := tc.tester.Client.CheckRedirect
tc.tester.Client.CheckRedirect = redirectPolicyFunc
defer func() { tc.tester.Client.CheckRedirect = old }()
resp, err := tc.tester.Client.Get(requestURI)
if err != nil {
tc.t.Errorf("failed to call server %s", err)
return nil
}
if expectedStatusCode != resp.StatusCode {
tc.t.Errorf("requesting \"%s\" expected status code: %d but got %d", requestURI, expectedStatusCode, resp.StatusCode)
}
loc, err := resp.Location()
if err != nil {
tc.t.Errorf("requesting \"%s\" expected location: \"%s\" but got error: %s", requestURI, expectedToLocation, err)
}
if loc == nil && expectedToLocation != "" {
tc.t.Errorf("requesting \"%s\" expected a Location header, but didn't get one", requestURI)
}
if loc != nil {
if expectedToLocation != loc.String() {
tc.t.Errorf("requesting \"%s\" expected location: \"%s\" but got \"%s\"", requestURI, expectedToLocation, loc.String())
}
}
return resp
}
// AssertResponseCode will execute the request and verify the status code, returns a response for additional assertions
func (tc *TestHarness) AssertResponseCode(req *http.Request, expectedStatusCode int) *http.Response {
resp, err := tc.tester.Client.Do(req)
if err != nil {
tc.t.Fatalf("failed to call server %s", err)
}
if expectedStatusCode != resp.StatusCode {
tc.t.Errorf("requesting \"%s\" expected status code: %d but got %d", req.URL.RequestURI(), expectedStatusCode, resp.StatusCode)
}
return resp
}
// AssertResponse request a URI and assert the status code and the body contains a string
func (tc *TestHarness) AssertResponse(req *http.Request, expectedStatusCode int, expectedBody string) (*http.Response, string) {
resp := tc.AssertResponseCode(req, expectedStatusCode)
defer resp.Body.Close()
bytes, err := io.ReadAll(resp.Body)
if err != nil {
tc.t.Fatalf("unable to read the response body %s", err)
}
body := string(bytes)
if body != expectedBody {
tc.t.Errorf("requesting \"%s\" expected response body \"%s\" but got \"%s\"", req.RequestURI, expectedBody, body)
}
return resp, body
}
// Verb specific test functions
// AssertGetResponse GET a URI and expect a statusCode and body text
func (tc *TestHarness) AssertGetResponse(requestURI string, expectedStatusCode int, expectedBody string) (*http.Response, string) {
req, err := http.NewRequest("GET", requestURI, nil)
if err != nil {
tc.t.Fatalf("unable to create request %s", err)
}
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
}
// AssertDeleteResponse request a URI and expect a statusCode and body text
func (tc *TestHarness) AssertDeleteResponse(requestURI string, expectedStatusCode int, expectedBody string) (*http.Response, string) {
req, err := http.NewRequest("DELETE", requestURI, nil)
if err != nil {
tc.t.Fatalf("unable to create request %s", err)
}
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
}
// AssertPostResponseBody POST to a URI and assert the response code and body
func (tc *TestHarness) AssertPostResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
req, err := http.NewRequest("POST", requestURI, requestBody)
if err != nil {
tc.t.Errorf("failed to create request %s", err)
return nil, ""
}
applyHeaders(tc.t, req, requestHeaders)
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
}
// AssertPutResponseBody PUT to a URI and assert the response code and body
func (tc *TestHarness) AssertPutResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
req, err := http.NewRequest("PUT", requestURI, requestBody)
if err != nil {
tc.t.Errorf("failed to create request %s", err)
return nil, ""
}
applyHeaders(tc.t, req, requestHeaders)
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
}
// AssertPatchResponseBody PATCH to a URI and assert the response code and body
func (tc *TestHarness) AssertPatchResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
req, err := http.NewRequest("PATCH", requestURI, requestBody)
if err != nil {
tc.t.Errorf("failed to create request %s", err)
return nil, ""
}
applyHeaders(tc.t, req, requestHeaders)
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
}
+6 -3
View File
@@ -1,13 +1,16 @@
#!/bin/sh
# USAGE: go run -exec ./setcap.sh main.go <args...>
# USAGE:
# go run -exec ./setcap.sh main.go <args...>
#
# (Example: `go run -exec ./setcap.sh main.go run --config caddy.json`)
#
# For some reason this does not work on my Arch system, so if you find that's
# the case, you can instead do: go build && ./setcap.sh ./caddy <args...>
# but this will leave the ./caddy binary laying around.
# the case, you can instead do:
#
# go build && ./setcap.sh ./caddy <args...>
#
# but this will leave the ./caddy binary laying around.
#
sudo setcap cap_net_bind_service=+ep "$1"
+16 -12
View File
@@ -8,9 +8,10 @@ import (
"github.com/caddyserver/caddy/v2"
)
var rootCmd = &cobra.Command{
Use: "caddy",
Long: `Caddy is an extensible server platform written in Go.
var defaultFactory = NewRootCommandFactory(func() *cobra.Command {
return &cobra.Command{
Use: "caddy",
Long: `Caddy is an extensible server platform written in Go.
At its core, Caddy merely manages configuration. Modules are plugged
in statically at compile-time to provide useful functionality. Caddy's
@@ -91,23 +92,26 @@ package installers: https://caddyserver.com/docs/install
Instructions for running Caddy in production are also available:
https://caddyserver.com/docs/running
`,
Example: ` $ caddy run
Example: ` $ caddy run
$ caddy run --config caddy.json
$ caddy reload --config caddy.json
$ caddy stop`,
// kind of annoying to have all the help text printed out if
// caddy has an error provisioning its modules, for instance...
SilenceUsage: true,
Version: onlyVersionText(),
}
// kind of annoying to have all the help text printed out if
// caddy has an error provisioning its modules, for instance...
SilenceUsage: true,
Version: onlyVersionText(),
}
})
const fullDocsFooter = `Full documentation is available at:
https://caddyserver.com/docs/command-line`
func init() {
rootCmd.SetVersionTemplate("{{.Version}}\n")
rootCmd.SetHelpTemplate(rootCmd.HelpTemplate() + "\n" + fullDocsFooter + "\n")
defaultFactory.Use(func(cmd *cobra.Command) {
cmd.SetVersionTemplate("{{.Version}}\n")
cmd.SetHelpTemplate(cmd.HelpTemplate() + "\n" + fullDocsFooter + "\n")
})
}
func onlyVersionText() string {
@@ -117,7 +121,7 @@ func onlyVersionText() string {
func caddyCmdToCobra(caddyCmd Command) *cobra.Command {
cmd := &cobra.Command{
Use: caddyCmd.Name,
Use: caddyCmd.Name + " " + caddyCmd.Usage,
Short: caddyCmd.Short,
Long: caddyCmd.Long,
}
+28
View File
@@ -0,0 +1,28 @@
package caddycmd
import (
"github.com/spf13/cobra"
)
type RootCommandFactory struct {
constructor func() *cobra.Command
options []func(*cobra.Command)
}
func NewRootCommandFactory(fn func() *cobra.Command) *RootCommandFactory {
return &RootCommandFactory{
constructor: fn,
}
}
func (f *RootCommandFactory) Use(fn func(cmd *cobra.Command)) {
f.options = append(f.options, fn)
}
func (f *RootCommandFactory) Build() *cobra.Command {
o := f.constructor()
for _, v := range f.options {
v(o)
}
return o
}
+7 -1
View File
@@ -20,6 +20,7 @@ import (
"crypto/rand"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"io/fs"
@@ -257,6 +258,7 @@ func cmdRun(fl Flags) (int, error) {
// if enabled, reload config file automatically on changes
// (this better only be used in dev!)
// do not enable this during tests, it will cause leaks
if watchFlag {
go watchConfigFile(configFile, configAdapterFlag)
}
@@ -280,7 +282,11 @@ func cmdRun(fl Flags) (int, error) {
}
}
select {}
if flag.Lookup("test.v") == nil || !strings.Contains(os.Args[0], ".test") {
select {}
} else {
return caddy.ExitCodeSuccess, nil
}
}
func cmdStop(fl Flags) (int, error) {
+29 -24
View File
@@ -459,7 +459,8 @@ argument of --directory. If the directory does not exist, it will be created.
if err := os.MkdirAll(dir, 0o755); err != nil {
return caddy.ExitCodeFailedQuit, err
}
if err := doc.GenManTree(rootCmd, &doc.GenManHeader{
ccmd := defaultFactory.Build()
if err := doc.GenManTree(ccmd, &doc.GenManHeader{
Title: "Caddy",
Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections
}, dir); err != nil {
@@ -471,10 +472,11 @@ argument of --directory. If the directory does not exist, it will be created.
})
// source: https://github.com/spf13/cobra/blob/main/shell_completions.md
rootCmd.AddCommand(&cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate completion script",
Long: fmt.Sprintf(`To load completions:
defaultFactory.Use(func(ccmd *cobra.Command) {
ccmd.AddCommand(&cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate completion script",
Long: fmt.Sprintf(`To load completions:
Bash:
@@ -512,24 +514,25 @@ argument of --directory. If the directory does not exist, it will be created.
# To load completions for every new session, run:
PS> %[1]s completion powershell > %[1]s.ps1
# and source this file from your PowerShell profile.
`, rootCmd.Root().Name()),
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
RunE: func(cmd *cobra.Command, args []string) error {
switch args[0] {
case "bash":
return cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
return cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
return cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
default:
return fmt.Errorf("unrecognized shell: %s", args[0])
}
},
`, defaultFactory.constructor().Name()),
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
RunE: func(cmd *cobra.Command, args []string) error {
switch args[0] {
case "bash":
return cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
return cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
return cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
default:
return fmt.Errorf("unrecognized shell: %s", args[0])
}
},
})
})
}
@@ -563,7 +566,9 @@ func RegisterCommand(cmd Command) {
if !commandNameRegex.MatchString(cmd.Name) {
panic("invalid command name")
}
rootCmd.AddCommand(caddyCmdToCobra(cmd))
defaultFactory.Use(func(ccmd *cobra.Command) {
ccmd.AddCommand(caddyCmdToCobra(cmd))
})
}
var commandNameRegex = regexp.MustCompile(`^[a-z0-9]$|^([a-z0-9]+-?[a-z0-9]*)+[a-z0-9]$`)
+77 -21
View File
@@ -17,6 +17,7 @@ package caddycmd
import (
"bufio"
"bytes"
"encoding/json"
"errors"
"flag"
"fmt"
@@ -70,7 +71,7 @@ func Main() {
if err != nil {
caddy.Log().Warn("failed to set GOMAXPROCS", zap.Error(err))
}
rootCmd := defaultFactory.Build()
if err := rootCmd.Execute(); err != nil {
var exitError *exitError
if errors.As(err, &exitError) {
@@ -80,6 +81,18 @@ func Main() {
}
}
// MainForTesting implements the main function of the caddy command, used internally for testing
func MainForTesting(args ...string) error {
// create a root command for testing which will not pollute the global namespace, and does not
// call os.Exit().
rootCmd := defaultFactory.Build()
rootCmd.SetArgs(args)
if err := rootCmd.Execute(); err != nil {
return err
}
return nil
}
// handlePingbackConn reads from conn and ensures it matches
// the bytes in expect, or returns an error if it doesn't.
func handlePingbackConn(conn net.Conn, expect []byte) error {
@@ -106,7 +119,47 @@ func LoadConfig(configFile, adapterName string) ([]byte, string, error) {
return loadConfigWithLogger(caddy.Log(), configFile, adapterName)
}
func isCaddyfile(configFile, adapterName string) (bool, error) {
if adapterName == "caddyfile" {
return true, nil
}
// as a special case, if a config file starts with "caddyfile" or
// has a ".caddyfile" extension, and no adapter is specified, and
// no adapter module name matches the extension, assume
// caddyfile adapter for convenience
baseConfig := strings.ToLower(filepath.Base(configFile))
baseConfigExt := filepath.Ext(baseConfig)
startsOrEndsInCaddyfile := strings.HasPrefix(baseConfig, "caddyfile") || strings.HasSuffix(baseConfig, ".caddyfile")
if baseConfigExt == ".json" {
return false, nil
}
// If the adapter is not specified,
// the config file starts with "caddyfile",
// the config file has an extension,
// and isn't a JSON file (e.g. Caddyfile.yaml),
// then we don't know what the config format is.
if adapterName == "" && startsOrEndsInCaddyfile {
return true, nil
}
// adapter is not empty,
// adapter is not "caddyfile",
// extension is not ".json",
// extension is not ".caddyfile"
// file does not start with "Caddyfile"
return false, nil
}
func loadConfigWithLogger(logger *zap.Logger, configFile, adapterName string) ([]byte, string, error) {
// if no logger is provided, use a nop logger
// just so we don't have to check for nil
if logger == nil {
logger = zap.NewNop()
}
// specifying an adapter without a config file is ambiguous
if adapterName != "" && configFile == "" {
return nil, "", fmt.Errorf("cannot adapt config without config file (use --config)")
@@ -119,16 +172,16 @@ func loadConfigWithLogger(logger *zap.Logger, configFile, adapterName string) ([
if configFile != "" {
if configFile == "-" {
config, err = io.ReadAll(os.Stdin)
if err != nil {
return nil, "", fmt.Errorf("reading config from stdin: %v", err)
}
logger.Info("using config from stdin")
} else {
config, err = os.ReadFile(configFile)
}
if err != nil {
return nil, "", fmt.Errorf("reading config file: %v", err)
}
if logger != nil {
logger.Info("using provided configuration",
zap.String("config_file", configFile),
zap.String("config_adapter", adapterName))
if err != nil {
return nil, "", fmt.Errorf("reading config from file: %v", err)
}
logger.Info("using config from file", zap.String("file", configFile))
}
} else if adapterName == "" {
// if the Caddyfile adapter is plugged in, we can try using an
@@ -145,20 +198,15 @@ func loadConfigWithLogger(logger *zap.Logger, configFile, adapterName string) ([
} else {
// success reading default Caddyfile
configFile = "Caddyfile"
if logger != nil {
logger.Info("using adjacent Caddyfile")
}
logger.Info("using adjacent Caddyfile")
}
}
}
// as a special case, if a config file called "Caddyfile" was
// specified, and no adapter is specified, assume caddyfile adapter
// for convenience
if strings.HasPrefix(filepath.Base(configFile), "Caddyfile") &&
filepath.Ext(configFile) != ".json" &&
adapterName == "" {
if yes, err := isCaddyfile(configFile, adapterName); yes {
adapterName = "caddyfile"
} else if err != nil {
return nil, "", err
}
// load config adapter
@@ -177,16 +225,24 @@ func loadConfigWithLogger(logger *zap.Logger, configFile, adapterName string) ([
if err != nil {
return nil, "", fmt.Errorf("adapting config using %s: %v", adapterName, err)
}
logger.Info("adapted config to JSON", zap.String("adapter", adapterName))
for _, warn := range warnings {
msg := warn.Message
if warn.Directive != "" {
msg = fmt.Sprintf("%s: %s", warn.Directive, warn.Message)
}
if logger != nil {
logger.Warn(msg, zap.String("adapter", adapterName), zap.String("file", warn.File), zap.Int("line", warn.Line))
}
logger.Warn(msg,
zap.String("adapter", adapterName),
zap.String("file", warn.File),
zap.Int("line", warn.Line))
}
config = adaptedConfig
} else if len(config) != 0 {
// validate that the config is at least valid JSON
err = json.Unmarshal(config, new(any))
if err != nil {
return nil, "", fmt.Errorf("config is not valid JSON: %v; did you mean to use a config adapter (the --adapter flag)?", err)
}
}
return config, configFile, nil
+110
View File
@@ -168,3 +168,113 @@ here"
}
}
}
func Test_isCaddyfile(t *testing.T) {
type args struct {
configFile string
adapterName string
}
tests := []struct {
name string
args args
want bool
wantErr bool
}{
{
name: "bare Caddyfile without adapter",
args: args{
configFile: "Caddyfile",
adapterName: "",
},
want: true,
wantErr: false,
},
{
name: "local Caddyfile without adapter",
args: args{
configFile: "./Caddyfile",
adapterName: "",
},
want: true,
wantErr: false,
},
{
name: "local caddyfile with adapter",
args: args{
configFile: "./Caddyfile",
adapterName: "caddyfile",
},
want: true,
wantErr: false,
},
{
name: "ends with .caddyfile with adapter",
args: args{
configFile: "./conf.caddyfile",
adapterName: "caddyfile",
},
want: true,
wantErr: false,
},
{
name: "ends with .caddyfile without adapter",
args: args{
configFile: "./conf.caddyfile",
adapterName: "",
},
want: true,
wantErr: false,
},
{
name: "config is Caddyfile.yaml with adapter",
args: args{
configFile: "./Caddyfile.yaml",
adapterName: "yaml",
},
want: false,
wantErr: false,
},
{
name: "json is not caddyfile but not error",
args: args{
configFile: "./Caddyfile.json",
adapterName: "",
},
want: false,
wantErr: false,
},
{
name: "prefix of Caddyfile and ./ with any extension is Caddyfile",
args: args{
configFile: "./Caddyfile.prd",
adapterName: "",
},
want: true,
wantErr: false,
},
{
name: "prefix of Caddyfile without ./ with any extension is Caddyfile",
args: args{
configFile: "Caddyfile.prd",
adapterName: "",
},
want: true,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := isCaddyfile(tt.args.configFile, tt.args.adapterName)
if (err != nil) != tt.wantErr {
t.Errorf("isCaddyfile() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("isCaddyfile() = %v, want %v", got, tt.want)
}
})
}
}
+33
View File
@@ -0,0 +1,33 @@
// 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 caddycmd
import (
// For running in minimal environments, this can ease
// headaches related to establishing TLS connections.
// "Package fallback embeds a set of fallback X.509 trusted
// roots in the application by automatically invoking
// x509.SetFallbackRoots. This allows the application to
// work correctly even if the operating system does not
// provide a verifier or system roots pool. ... It's
// recommended that only binaries, and not libraries,
// import this package. This package must be kept up to
// date for security and compatibility reasons."
//
// This is in its own file only because of conflicts
// between gci and goimports when in main.go.
// See https://github.com/daixiang0/gci/issues/76
_ "golang.org/x/crypto/x509roots/fallback"
)
+44 -17
View File
@@ -44,8 +44,9 @@ type Context struct {
moduleInstances map[string][]Module
cfg *Config
cleanupFuncs []func()
ancestry []Module
cleanupFuncs []func() // invoked at every config unload
exitFuncs []func(context.Context) // invoked at config unload ONLY IF the process is exiting (EXPERIMENTAL)
}
// NewContext provides a new context derived from the given
@@ -86,7 +87,8 @@ func (ctx *Context) OnCancel(f func()) {
ctx.cleanupFuncs = append(ctx.cleanupFuncs, f)
}
// Filesystems returns a ref to the FilesystemMap
// Filesystems returns a ref to the FilesystemMap.
// EXPERIMENTAL: This API is subject to change.
func (ctx *Context) Filesystems() FileSystems {
// if no config is loaded, we use a default filesystemmap, which includes the osfs
if ctx.cfg == nil {
@@ -95,6 +97,15 @@ func (ctx *Context) Filesystems() FileSystems {
return ctx.cfg.filesystems
}
// OnExit executes f when the process exits gracefully.
// The function is only executed if the process is gracefully
// shut down while this context is active.
//
// EXPERIMENTAL API: subject to change or removal.
func (ctx *Context) OnExit(f func(context.Context)) {
ctx.exitFuncs = append(ctx.exitFuncs, f)
}
// LoadModule loads the Caddy module(s) from the specified field of the parent struct
// pointer and returns the loaded module(s). The struct pointer and its field name as
// a string are necessary so that reflection can be used to read the struct tag on the
@@ -442,25 +453,29 @@ func (ctx Context) App(name string) (any, error) {
return modVal, nil
}
// 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. If
// the app has not been loaded, nil is returned.
//
// We return any type instead of the App type because it is not
// intended for the caller of this method to be the one to start
// or stop App modules. The caller is expected to assert to the
// concrete type.
func (ctx Context) AppIfConfigured(name string) any {
// AppIfConfigured is like App, but it returns an error if the
// app has not been configured. This is useful when the app is
// required and its absence is a configuration error; or when
// the app is optional and you don't want to instantiate a
// new one that hasn't been explicitly configured. If the app
// is not in the configuration, the error wraps ErrNotConfigured.
func (ctx Context) AppIfConfigured(name string) (any, error) {
if ctx.cfg == nil {
// this can happen if the currently-active context
// is being accessed, but no config has successfully
// been loaded yet
return nil
return nil, fmt.Errorf("app module %s: %w", name, ErrNotConfigured)
}
return ctx.cfg.apps[name]
if app, ok := ctx.cfg.apps[name]; ok {
return app, nil
}
appRaw := ctx.cfg.AppsRaw[name]
if appRaw == nil {
return nil, fmt.Errorf("app module %s: %w", name, ErrNotConfigured)
}
return ctx.App(name)
}
// ErrNotConfigured indicates a module is not configured.
var ErrNotConfigured = fmt.Errorf("module not configured")
// Storage returns the configured Caddy storage implementation.
func (ctx Context) Storage() certmagic.Storage {
return ctx.cfg.storage
@@ -545,3 +560,15 @@ func (ctx Context) Module() Module {
}
return ctx.ancestry[len(ctx.ancestry)-1]
}
// WithValue returns a new context with the given key-value pair.
func (ctx *Context) WithValue(key, value any) Context {
return Context{
Context: context.WithValue(ctx.Context, key, value),
moduleInstances: ctx.moduleInstances,
cfg: ctx.cfg,
ancestry: ctx.ancestry,
cleanupFuncs: ctx.cleanupFuncs,
exitFuncs: ctx.exitFuncs,
}
}
+66 -64
View File
@@ -1,128 +1,132 @@
module github.com/caddyserver/caddy/v2
go 1.21
go 1.21.0
toolchain go1.21.4
toolchain go1.22.2
require (
github.com/BurntSushi/toml v1.3.2
github.com/Masterminds/sprig/v3 v3.2.3
github.com/alecthomas/chroma/v2 v2.12.1-0.20240220090827-381050ba0001
github.com/alecthomas/chroma/v2 v2.13.0
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b
github.com/caddyserver/certmagic v0.20.0
github.com/caddyserver/certmagic v0.21.3
github.com/caddyserver/zerossl v0.1.3
github.com/dustin/go-humanize v1.0.1
github.com/go-chi/chi/v5 v5.0.10
github.com/google/cel-go v0.15.1
github.com/google/uuid v1.3.1
github.com/klauspost/compress v1.17.0
github.com/klauspost/cpuid/v2 v2.2.5
github.com/mholt/acmez v1.2.0
github.com/prometheus/client_golang v1.18.0
github.com/quic-go/quic-go v0.41.0
github.com/smallstep/certificates v0.25.0
github.com/smallstep/nosql v0.6.0
github.com/smallstep/truststore v0.12.1
github.com/go-chi/chi/v5 v5.0.12
github.com/google/cel-go v0.20.1
github.com/google/uuid v1.6.0
github.com/klauspost/compress v1.17.8
github.com/klauspost/cpuid/v2 v2.2.7
github.com/mholt/acmez/v2 v2.0.1
github.com/prometheus/client_golang v1.19.1
github.com/quic-go/quic-go v0.44.0
github.com/smallstep/certificates v0.26.1
github.com/smallstep/nosql v0.6.1
github.com/smallstep/truststore v0.13.0
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.4
github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046
github.com/yuin/goldmark v1.5.6
github.com/stretchr/testify v1.9.0
github.com/tailscale/tscert v0.0.0-20240608151842-d3f834017e53
github.com/yuin/goldmark v1.7.1
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0
go.opentelemetry.io/contrib/propagators/autoprop v0.42.0
go.opentelemetry.io/otel v1.21.0
go.opentelemetry.io/otel v1.24.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0
go.opentelemetry.io/otel/sdk v1.21.0
go.uber.org/automaxprocs v1.5.3
go.uber.org/zap v1.26.0
go.uber.org/zap v1.27.0
go.uber.org/zap/exp v0.2.0
golang.org/x/crypto v0.18.0
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611
golang.org/x/net v0.19.0
golang.org/x/sync v0.5.0
golang.org/x/term v0.16.0
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f
golang.org/x/crypto v0.23.0
golang.org/x/crypto/x509roots/fallback v0.0.0-20240507223354-67b13616a595
golang.org/x/net v0.25.0
golang.org/x/sync v0.7.0
golang.org/x/term v0.20.0
golang.org/x/time v0.5.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
)
require (
cloud.google.com/go/iam v1.1.2 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fxamacker/cbor/v2 v2.5.0 // indirect
github.com/golang/glog v1.1.2 // indirect
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/golang/glog v1.2.0 // indirect
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 // indirect
github.com/google/go-tpm v0.9.0 // indirect
github.com/google/go-tspi v0.3.0 // indirect
github.com/google/pprof v0.0.0-20231212022811-ec68065c825e // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/onsi/ginkgo/v2 v2.13.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/smallstep/go-attestation v0.4.4-0.20230627102604-cf579e53cbd2 // indirect
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 // indirect
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81 // indirect
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect
go.opentelemetry.io/contrib/propagators/aws v1.17.0 // indirect
go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.17.0 // indirect
go.opentelemetry.io/contrib/propagators/ot v1.17.0 // indirect
go.uber.org/mock v0.3.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6 // indirect
)
require (
filippo.io/edwards25519 v1.0.0 // indirect
filippo.io/edwards25519 v1.1.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.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0
github.com/chzyer/readline v1.5.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/dgraph-io/badger v1.6.2 // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.1.0 // indirect
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/dlclark/regexp2 v1.11.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-kit/kit v0.10.0 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-kit/kit v0.13.0 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.14.0 // indirect
github.com/jackc/pgconn v1.14.3 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.2 // indirect
github.com/jackc/pgproto3/v2 v2.3.3 // 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/jackc/pgx/v4 v4.18.3 // indirect
github.com/libdns/libdns v0.2.2 // indirect
github.com/manifoldco/promptui v0.9.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/micromdm/scep/v2 v2.1.0 // indirect
github.com/miekg/dns v1.1.55 // indirect
github.com/miekg/dns v1.1.59 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pires/go-proxyproto v0.7.0
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rs/xid v1.5.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
@@ -133,22 +137,20 @@ require (
github.com/spf13/cast v1.4.1 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
github.com/urfave/cli v1.22.14 // indirect
go.etcd.io/bbolt v1.3.8 // indirect
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
go.etcd.io/bbolt v1.3.9 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
go.opentelemetry.io/otel/metric v1.21.0 // indirect
go.opentelemetry.io/otel/trace v1.21.0
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.step.sm/cli-utils v0.8.0 // indirect
go.step.sm/crypto v0.35.1
go.step.sm/cli-utils v0.9.0 // indirect
go.step.sm/crypto v0.45.0
go.step.sm/linkedca v0.20.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/sys v0.16.0
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.16.1 // indirect
google.golang.org/grpc v1.60.1 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/sys v0.20.0
golang.org/x/text v0.15.0 // indirect
golang.org/x/tools v0.21.0 // indirect
google.golang.org/grpc v1.63.2 // indirect
google.golang.org/protobuf v1.34.1 // indirect
howett.net/plist v1.0.0 // indirect
)
+194 -443
View File
@@ -1,22 +1,24 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o=
cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/iam v1.1.2 h1:gacbrBdWcoVmGLozRuStX45YKvJtzIjJdAolzUs1sm4=
cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU=
cloud.google.com/go/kms v1.15.2 h1:lh6qra6oC4AyWe5fUUUBe/S27k12OHAleOOOw6KakdE=
cloud.google.com/go/kms v1.15.2/go.mod h1:3hopT4+7ooWRCjc2DxgnpESFxhIraaI2IpAVUEhbT/w=
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM=
cloud.google.com/go/auth v0.4.1 h1:Z7YNIhlWRtrnKlZke7z3GMqzvuYzdc2z98F9D1NV5Hg=
cloud.google.com/go/auth v0.4.1/go.mod h1:QVBuVEKpCn4Zp58hzRGvL0tjRGU0YqdRTdCHM1IHnro=
cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg=
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/iam v1.1.8 h1:r7umDwhj+BQyz0ScZMp4QrGXjSTI3ZINnpgU2nlB/K0=
cloud.google.com/go/iam v1.1.8/go.mod h1:GvE6lyMmfxXauzNq8NbgJbeVQNspG+tcdL/W8QO1+zE=
cloud.google.com/go/kms v1.16.0 h1:1yZsRPhmargZOmY+fVAh8IKiR9HzCb0U1zsxb5g2nRY=
cloud.google.com/go/kms v1.16.0/go.mod h1:olQUXy2Xud+1GzYfiBO9N0RhjsJk5IJLU6n/ethLXVc=
cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU=
cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
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.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
@@ -28,53 +30,55 @@ github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2y
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/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU=
github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
github.com/alecthomas/chroma/v2 v2.9.1 h1:0O3lTQh9FxazJ4BYE/MOi/vDGuHn7B+6Bu902N2UZvU=
github.com/alecthomas/chroma/v2 v2.9.1/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
github.com/alecthomas/chroma/v2 v2.12.1-0.20240220090827-381050ba0001 h1:Nl5Om7AhgtN3tML9kLn2/lr8IDVKxHT2t2+xWc4Q6Fs=
github.com/alecthomas/chroma/v2 v2.12.1-0.20240220090827-381050ba0001/go.mod h1:b6DmXsg5hSmn0AcHaTsU+UH0vO73VzhR+JrpFihjsXM=
github.com/alecthomas/chroma/v2 v2.13.0 h1:VP72+99Fb2zEcYM0MeaWJmV+xQvz5v5cxRHd+ooU1lI=
github.com/alecthomas/chroma/v2 v2.13.0/go.mod h1:BUGjjsD+ndS6eX37YgTchSEG+Jg9Jv1GiZs9sqPqztk=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw=
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.46.4 h1:48tKgtm9VMPkb6y7HuYlsfhQmoIRAsTEXTsWLVlty4M=
github.com/aws/aws-sdk-go v1.46.4/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA=
github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
github.com/aws/aws-sdk-go-v2/config v1.27.13 h1:WbKW8hOzrWoOA/+35S5okqO/2Ap8hkkFUzoW8Hzq24A=
github.com/aws/aws-sdk-go-v2/config v1.27.13/go.mod h1:XLiyiTMnguytjRER7u5RIkhIqS8Nyz41SwAWb4xEjxs=
github.com/aws/aws-sdk-go-v2/credentials v1.17.13 h1:XDCJDzk/u5cN7Aple7D/MiAhx1Rjo/0nueJ0La8mRuE=
github.com/aws/aws-sdk-go-v2/credentials v1.17.13/go.mod h1:FMNcjQrmuBYvOTZDtOLCIu0esmxjF7RuA/89iSXWzQI=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 h1:ogRAwT1/gxJBcSWDMZlgyFUM962F51A5CRhDLbxLdmo=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7/go.mod h1:YCsIZhXfRPLFFCl5xxY+1T9RKzOKjCut+28JSX2DnAk=
github.com/aws/aws-sdk-go-v2/service/kms v1.31.1 h1:5wtyAwuUiJiM3DHYeGZmP5iMonM7DFBWAEaaVPHYZA0=
github.com/aws/aws-sdk-go-v2/service/kms v1.31.1/go.mod h1:2snWQJQUKsbN66vAawJuOGX7dr37pfOq9hb0tZDGIqQ=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.6 h1:o5cTaeunSpfXiLTIBx5xo2enQmiChtu1IBbzXnfU9Hs=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.6/go.mod h1:qGzynb/msuZIE8I75DVRCUXw3o3ZyBmUvMwQ2t/BrGM=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.0 h1:Qe0r0lVURDDeBQJ4yP+BOrJkvkiCo/3FH/t+wY11dmw=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.0/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak=
github.com/aws/aws-sdk-go-v2/service/sts v1.28.7 h1:et3Ta53gotFR4ERLXXHIHl/Uuk1qYpP5uU7cvNql8ns=
github.com/aws/aws-sdk-go-v2/service/sts v1.28.7/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw=
github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/caddyserver/certmagic v0.21.3 h1:pqRRry3yuB4CWBVq9+cUqu+Y6E2z8TswbhNx1AZeYm0=
github.com/caddyserver/certmagic v0.21.3/go.mod h1:Zq6pklO9nVRl3DIFUw9gVUfXKdpc/0qwTUAQMBlfgtI=
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/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=
@@ -89,21 +93,14 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@@ -119,166 +116,100 @@ github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70d
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
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/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE=
github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU=
github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
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-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/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.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
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.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.15.1 h1:iTgVZor2x9okXtmTrqO8cg4uvqIeaBcWhXtruaWFMYQ=
github.com/google/cel-go v0.15.1/go.mod h1:YzWEoI07MC/a/wj9in8GeVatqfypkldgBlwXh9bCwqY=
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg=
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 h1:heyoXNxkRT155x4jTAiSv5BVSVkueifPUm+Q8LUXMRo=
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745/go.mod h1:zN0wUQgV9LjwLZeFHnrAbQi8hzMVvEWePyk+MhPOk7k=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk=
github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU=
github.com/google/go-tpm-tools v0.4.1 h1:gYU6iwRo0tY3V6NDnS6m+XYog+b3g6YFhHQl3sYaUL4=
github.com/google/go-tpm-tools v0.4.1/go.mod h1:w03m0jynhTo7puXTYoyfpNOMqyQ9SB7sixnKWsS/1L0=
github.com/google/go-tpm-tools v0.4.4 h1:oiQfAIkc6xTy9Fl5NKTeTJkBTlXdHsxAofmQyxBKY98=
github.com/google/go-tpm-tools v0.4.4/go.mod h1:T8jXkp2s+eltnCDIsXR84/MTcVU9Ja7bh3Mit0pa4AY=
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20231212022811-ec68065c825e h1:bwOy7hAFd0C91URzMIEBfr6BAz29yk7Qj0cy6S7DJlU=
github.com/google/pprof v0.0.0-20231212022811-ec68065c825e/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.4.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/googleapis/gax-go/v2 v2.12.4 h1:9gWcmF85Wvq4ryPFvGFaOgPIs1AQX0d0bcbGw4Z96qg=
github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
@@ -289,8 +220,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.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q=
github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E=
github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
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=
@@ -306,8 +237,8 @@ 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.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0=
github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
github.com/jackc/pgproto3/v2 v2.3.3/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=
@@ -321,33 +252,21 @@ github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.18.0 h1:Ltaa1ePvc7msFGALnCrqKJVEByu/qYh5jJBYcDtAno4=
github.com/jackc/pgx/v4 v4.18.0/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE=
github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA=
github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -363,137 +282,65 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
github.com/micromdm/scep/v2 v2.1.0 h1:2fS9Rla7qRR266hvUoEauBJ7J6FhgssEiq2OkSKXmaU=
github.com/micromdm/scep/v2 v2.1.0/go.mod h1:BkF7TkPPhmgJAMtHfP+sFTKXmgzNJgLQlvvGoOExBcc=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mholt/acmez/v2 v2.0.1 h1:3/3N0u1pLjMK4sNEAFSI+bcvzbPhRpY383sy1kLHJ6k=
github.com/mholt/acmez/v2 v2.0.1/go.mod h1:fX4c9r5jYwMyMsC+7tkYRxHibkOTgta5DIFGoe67e1U=
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs=
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU=
github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
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_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
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.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
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/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k=
github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/quic-go/quic-go v0.44.0 h1:So5wOr7jyO4vzL2sd8/pD9Kesciv91zSk8BoFngItQ0=
github.com/quic-go/quic-go v0.44.0/go.mod h1:z4cx/9Ny9UtGITIPzmPTXh1ULfOyWh4qGQlpnPcWmek=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
@@ -503,21 +350,16 @@ github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E=
github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@@ -527,18 +369,18 @@ github.com/slackhq/nebula v1.6.1 h1:/OCTR3abj0Sbf2nGoLUrdDXImrCv0ZVFpVPP5qa0DsM=
github.com/slackhq/nebula v1.6.1/go.mod h1:UmkqnXe4O53QwToSl/gG7sM4BroQwAB7dd4hUaT6MlI=
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY=
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc=
github.com/smallstep/certificates v0.25.0 h1:WWihtjQ7SprnRxDV44mBp8t5SMsNO5EWsQaEwy1rgFg=
github.com/smallstep/certificates v0.25.0/go.mod h1:thJmekMKUplKYip+la99Lk4IwQej/oVH/zS9PVMagEE=
github.com/smallstep/go-attestation v0.4.4-0.20230627102604-cf579e53cbd2 h1:UIAS8DTWkeclraEGH2aiJPyNPu16VbT41w4JoBlyFfU=
github.com/smallstep/go-attestation v0.4.4-0.20230627102604-cf579e53cbd2/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4=
github.com/smallstep/nosql v0.6.0 h1:ur7ysI8s9st0cMXnTvB8tA3+x5Eifmkb6hl4uqNV5jc=
github.com/smallstep/nosql v0.6.0/go.mod h1:jOXwLtockXORUPPZ2MCUcIkGR6w0cN1QGZniY9DITQA=
github.com/smallstep/truststore v0.12.1 h1:guLUKkc1UlsXeS3t6BuVMa4leOOpdiv02PCRTiy1WdY=
github.com/smallstep/truststore v0.12.1/go.mod h1:M4mebeNy28KusGX3lJxpLARIktLcyqBOrj3ZiZ46pqw=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/smallstep/certificates v0.26.1 h1:FIUliEBcExSfJJDhRFA/s8aZgMIFuorexnRSKQd884o=
github.com/smallstep/certificates v0.26.1/go.mod h1:OQMrW39IrGKDViKSHrKcgSQArMZ8c7EcjhYKK7mYqis=
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 h1:kjYvkvS/Wdy0PVRDUAA0gGJIVSEZYhiAJtfwYgOYoGA=
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4=
github.com/smallstep/nosql v0.6.1 h1:X8IBZFTRIp1gmuf23ne/jlD/BWKJtDQbtatxEn7Et1Y=
github.com/smallstep/nosql v0.6.1/go.mod h1:vrN+CftYYNnDM+DQqd863ATynvYFm/6FuY9D4TeAm2Y=
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81 h1:B6cED3iLJTgxpdh4tuqByDjRRKan2EvtnOfHr2zHJVg=
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81/go.mod h1:SoUAr/4M46rZ3WaLstHxGhLEgoYIDRqxQEXLOmOEB0Y=
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d h1:06LUHn4Ia2X6syjIaCMNaXXDNdU+1N/oOHynJbWgpXw=
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d/go.mod h1:4d0ub42ut1mMtvGyMensjuHYEUpRrASvkzLEJvoRQcU=
github.com/smallstep/truststore v0.13.0 h1:90if9htAOblavbMeWlqNLnO9bsjjgVv2hQeQJCi/py4=
github.com/smallstep/truststore v0.13.0/go.mod h1:3tmMp2aLKZ/OA/jnFUB0cYPcho402UG2knuJoPh4j7A=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
@@ -547,21 +389,16 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@@ -575,25 +412,21 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046 h1:8rUlviSVOEe7TMk7W0gIPrW8MqEzYfZHpsNWSf8s2vg=
github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046/go.mod h1:kNGUQ3VESx3VZwRwA9MSCUegIl6+saPL8Noq82ozCaU=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tailscale/tscert v0.0.0-20240608151842-d3f834017e53 h1:uxMgm0C+EjytfAqyfBG55ZONKQ7mvd7x4YYCWsf8QHQ=
github.com/tailscale/tscert v0.0.0-20240608151842-d3f834017e53/go.mod h1:kNGUQ3VESx3VZwRwA9MSCUegIl6+saPL8Noq82ozCaU=
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.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
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.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.5.6 h1:COmQAWTCcGetChm3Ig7G/t8AFAN00t+o8Mt4cf7JpwA=
github.com/yuin/goldmark v1.5.6/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
@@ -603,22 +436,14 @@ github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvv
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.mozilla.org/pkcs7 v0.0.0-20210730143726-725912489c62/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak=
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/contrib/propagators/autoprop v0.42.0 h1:s2RzYOAqHVgG23q8fPWYChobUoZM6rJZ98EnylJr66w=
go.opentelemetry.io/contrib/propagators/autoprop v0.42.0/go.mod h1:Mv/tWNtZn+NbALDb2XcItP0OM3lWWZjAfSroINxfW+Y=
go.opentelemetry.io/contrib/propagators/aws v1.17.0 h1:IX8d7l2uRw61BlmZBOTQFaK+y22j6vytMVTs9wFrO+c=
@@ -629,24 +454,24 @@ go.opentelemetry.io/contrib/propagators/jaeger v1.17.0 h1:Zbpbmwav32Ea5jSotpmkWE
go.opentelemetry.io/contrib/propagators/jaeger v1.17.0/go.mod h1:tcTUAlmO8nuInPDSBVfG+CP6Mzjy5+gNV4mPxMbL0IA=
go.opentelemetry.io/contrib/propagators/ot v1.17.0 h1:ufo2Vsz8l76eI47jFjuVyjyB3Ae2DmfiCV/o6Vc8ii0=
go.opentelemetry.io/contrib/propagators/ot v1.17.0/go.mod h1:SbKPj5XGp8K/sGm05XblaIABgMgw2jDczP8gGeuaVLk=
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0=
go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.step.sm/cli-utils v0.8.0 h1:b/Tc1/m3YuQq+u3ghTFP7Dz5zUekZj6GUmd5pCvkEXQ=
go.step.sm/cli-utils v0.8.0/go.mod h1:S77aISrC0pKuflqiDfxxJlUbiXcAanyJ4POOnzFSxD4=
go.step.sm/crypto v0.35.1 h1:QAZZ7Q8xaM4TdungGSAYw/zxpyH4fMYTkfaXVV9H7pY=
go.step.sm/crypto v0.35.1/go.mod h1:vn8Vkx/Mbqgoe7AG8btC0qZ995Udm3e+JySuDS1LCJA=
go.step.sm/cli-utils v0.9.0 h1:55jYcsQbnArNqepZyAwcato6Zy2MoZDRkWW+jF+aPfQ=
go.step.sm/cli-utils v0.9.0/go.mod h1:Y/CRoWl1FVR9j+7PnAewufAwKmBOTzR6l9+7EYGAnp8=
go.step.sm/crypto v0.45.0 h1:Z0WYAaaOYrJmKP9sJkPW+6wy3pgN3Ija8ek/D4serjc=
go.step.sm/crypto v0.45.0/go.mod h1:6IYlT0L2jfj81nVyCPpvA5cORy0EVHPhieSgQyuwHIY=
go.step.sm/linkedca v0.20.1 h1:bHDn1+UG1NgRrERkWbbCiAIvv4lD5NOFaswPDTyO5vU=
go.step.sm/linkedca v0.20.1/go.mod h1:Vaq4+Umtjh7DLFI1KuIxeo598vfBzgSYZUjgVJ7Syxw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -657,8 +482,8 @@ go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
@@ -668,17 +493,14 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.uber.org/zap/exp v0.2.0 h1:FtGenNNeCATRB3CmB/yEUnjEFeJWpB/pMcy7e2bKPYs=
go.uber.org/zap/exp v0.2.0/go.mod h1:t0gqAIdh1MfKv9EwN/dLwfZnJxe9ITAZN78HEWPFWDQ=
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=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -687,66 +509,39 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 h1:qCEDpW1G+vcj3Y7Fy52pEM1AWm3abj8WimGYejI3SC4=
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto/x509roots/fallback v0.0.0-20240507223354-67b13616a595 h1:TgSqweA595vD0Zt86JzLv3Pb/syKg8gd5KMGGbJPYFw=
golang.org/x/crypto/x509roots/fallback v0.0.0-20240507223354-67b13616a595/go.mod h1:kNa9WdvYnzFwC79zRpLRMJbdEFlhyM5RPFBBZp/wWH8=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20170726083632-f5079bd7f6f7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
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.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -754,13 +549,9 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -773,15 +564,20 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -790,22 +586,15 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -813,69 +602,36 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
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=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.154.0 h1:X7QkVKZBskztmpPKWQXgjJRPA2dJYrL6r+sYPRLj050=
google.golang.org/api v0.154.0/go.mod h1:qhSMkM85hgqiokIYsrRyKxrjfBeIhgl4Z2JmeRkYylc=
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=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f h1:Vn+VyHU5guc9KjB5KrjI2q0wCOWEOIh0OEsleqakHJg=
google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f/go.mod h1:nWSwAFPb+qfNJXsoeO3Io7zf4tMSfN8EA8RlDA04GhY=
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f h1:2yNACc1O40tTnrsbk9Cv6oxiW8pxI/pXj0wRtdlYmgY=
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 h1:DC7wcm+i+P1rN3Ff07vL+OndGg5OhNddHyTA+ocPqYE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM=
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=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
google.golang.org/api v0.180.0 h1:M2D87Yo0rGBPWpo1orwfCLehUUL6E7/TYe5gvMQWDh4=
google.golang.org/api v0.180.0/go.mod h1:51AiyoEg1MJPSZ9zvklA8VnRILPXxn1iVen9v25XHAE=
google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda h1:wu/KJm9KJwpfHWhkkZGohVC6KRrc1oJNr4jwtQMOQXw=
google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda/go.mod h1:g2LLCvCeCSir/JJSWosk19BR4NVxGqHUC6rxIRsd7Aw=
google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae h1:AH34z6WAGVNkllnKs5raNq3yRq93VnjBG6rpfub/jYk=
google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae/go.mod h1:FfiGhwUm6CJviekPrc0oJ+7h29e+DmWU6UtjX0ZvI7Y=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6 h1:DujSIu+2tC9Ht0aPNA7jgj23Iq8Ewi5sgkQ++wdvonE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
@@ -883,11 +639,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
+41
View File
@@ -0,0 +1,41 @@
package testmocks
import (
"crypto/x509"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddytls"
)
func init() {
caddy.RegisterModule(new(dummyVerifier))
}
type dummyVerifier struct{}
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (dummyVerifier) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
return nil
}
// CaddyModule implements caddy.Module.
func (dummyVerifier) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "tls.client_auth.verifier.dummy",
New: func() caddy.Module {
return new(dummyVerifier)
},
}
}
// VerifyClientCertificate implements ClientCertificateVerifier.
func (dummyVerifier) VerifyClientCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
return nil
}
var (
_ caddy.Module = dummyVerifier{}
_ caddytls.ClientCertificateVerifier = dummyVerifier{}
_ caddyfile.Unmarshaler = dummyVerifier{}
)
+34 -122
View File
@@ -28,11 +28,11 @@ import (
"strings"
"sync"
"sync/atomic"
"time"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/http3"
"go.uber.org/zap"
"golang.org/x/time/rate"
"github.com/caddyserver/caddy/v2/internal"
)
@@ -149,11 +149,11 @@ func (na NetworkAddress) Listen(ctx context.Context, portOffset uint, config net
func (na NetworkAddress) listen(ctx context.Context, portOffset uint, config net.ListenConfig) (any, error) {
var (
ln any
err error
address string
unixFileMode fs.FileMode
isAbtractUnixSocket bool
ln any
err error
address string
unixFileMode fs.FileMode
isAbstractUnixSocket bool
)
// split unix socket addr early so lnKey
@@ -164,7 +164,7 @@ func (na NetworkAddress) listen(ctx context.Context, portOffset uint, config net
if err != nil {
return nil, err
}
isAbtractUnixSocket = strings.HasPrefix(address, "@")
isAbstractUnixSocket = strings.HasPrefix(address, "@")
} else {
address = na.JoinHostPort(portOffset)
}
@@ -172,7 +172,7 @@ func (na NetworkAddress) listen(ctx context.Context, portOffset uint, config net
// if this is a unix socket, see if we already have it open,
// force socket permissions on it and return early
if socket, err := reuseUnixSocket(na.Network, address); socket != nil || err != nil {
if !isAbtractUnixSocket {
if !isAbstractUnixSocket {
if err := os.Chmod(address, unixFileMode); err != nil {
return nil, fmt.Errorf("unable to set permissions (%s) on %s: %v", unixFileMode, address, err)
}
@@ -195,7 +195,7 @@ func (na NetworkAddress) listen(ctx context.Context, portOffset uint, config net
}
if IsUnixNetwork(na.Network) {
if !isAbtractUnixSocket {
if !isAbstractUnixSocket {
if err := os.Chmod(address, unixFileMode); err != nil {
return nil, fmt.Errorf("unable to set permissions (%s) on %s: %v", unixFileMode, address, err)
}
@@ -406,48 +406,12 @@ func JoinNetworkAddress(network, host, port string) string {
return a
}
// DEPRECATED: Use NetworkAddress.Listen instead. This function will likely be changed or removed in the future.
func Listen(network, addr string) (net.Listener, error) {
// a 0 timeout means Go uses its default
return ListenTimeout(network, addr, 0)
}
// DEPRECATED: Use NetworkAddress.Listen instead. This function will likely be changed or removed in the future.
func ListenTimeout(network, addr string, keepalivePeriod time.Duration) (net.Listener, error) {
netAddr, err := ParseNetworkAddress(JoinNetworkAddress(network, addr, ""))
if err != nil {
return nil, err
}
ln, err := netAddr.Listen(context.TODO(), 0, net.ListenConfig{KeepAlive: keepalivePeriod})
if err != nil {
return nil, err
}
return ln.(net.Listener), nil
}
// DEPRECATED: Use NetworkAddress.Listen instead. This function will likely be changed or removed in the future.
func ListenPacket(network, addr string) (net.PacketConn, error) {
netAddr, err := ParseNetworkAddress(JoinNetworkAddress(network, addr, ""))
if err != nil {
return nil, err
}
ln, err := netAddr.Listen(context.TODO(), 0, net.ListenConfig{})
if err != nil {
return nil, err
}
return ln.(net.PacketConn), nil
}
// ListenQUIC returns a quic.EarlyListener suitable for use in a Caddy module.
// The network will be transformed into a QUIC-compatible type (if unix, then
// unixgram will be used; otherwise, udp will be used).
//
// NOTE: This API is EXPERIMENTAL and may be changed or removed.
func (na NetworkAddress) ListenQUIC(ctx context.Context, portOffset uint, config net.ListenConfig, tlsConf *tls.Config, activeRequests *int64) (http3.QUICEarlyListener, error) {
func (na NetworkAddress) ListenQUIC(ctx context.Context, portOffset uint, config net.ListenConfig, tlsConf *tls.Config) (http3.QUICEarlyListener, error) {
lnKey := listenerKey("quic"+na.Network, na.JoinHostPort(portOffset))
sharedEarlyListener, _, err := listenerPool.LoadOrNew(lnKey, func() (Destructor, error) {
@@ -468,20 +432,22 @@ func (na NetworkAddress) ListenQUIC(ctx context.Context, portOffset uint, config
}
}
sqs := newSharedQUICState(tlsConf, activeRequests)
sqs := newSharedQUICState(tlsConf)
// http3.ConfigureTLSConfig only uses this field and tls App sets this field as well
//nolint:gosec
quicTlsConfig := &tls.Config{GetConfigForClient: sqs.getConfigForClient}
earlyLn, err := quic.ListenEarly(h3ln, http3.ConfigureTLSConfig(quicTlsConfig), &quic.Config{
Allow0RTT: true,
RequireAddressValidation: func(clientAddr net.Addr) bool {
// TODO: make tunable?
return sqs.getActiveRequests() > 1000
},
})
// Require clients to verify their source address when we're handling more than 1000 handshakes per second.
// TODO: make tunable?
limiter := rate.NewLimiter(1000, 1000)
tr := &quic.Transport{
Conn: h3ln,
VerifySourceAddress: func(addr net.Addr) bool { return !limiter.Allow() },
}
earlyLn, err := tr.ListenEarly(http3.ConfigureTLSConfig(quicTlsConfig), &quic.Config{Allow0RTT: true})
if err != nil {
return nil, err
}
// TODO: figure out when to close the listener and the transport
// using the original net.PacketConn to close them properly
return &sharedQuicListener{EarlyListener: earlyLn, packetConn: ln, sqs: sqs, key: lnKey}, nil
})
@@ -490,47 +456,8 @@ func (na NetworkAddress) ListenQUIC(ctx context.Context, portOffset uint, config
}
sql := sharedEarlyListener.(*sharedQuicListener)
// add current tls.Config to sqs, so GetConfigForClient will always return the latest tls.Config in case of context cancellation,
// and the request counter will reflect current http server
ctx, cancel := sql.sqs.addState(tlsConf, activeRequests)
return &fakeCloseQuicListener{
sharedQuicListener: sql,
context: ctx,
contextCancel: cancel,
}, nil
}
// DEPRECATED: Use NetworkAddress.ListenQUIC instead. This function will likely be changed or removed in the future.
// TODO: See if we can find a more elegant solution closer to the new NetworkAddress.Listen API.
func ListenQUIC(ln net.PacketConn, tlsConf *tls.Config, activeRequests *int64) (http3.QUICEarlyListener, error) {
lnKey := listenerKey("quic+"+ln.LocalAddr().Network(), ln.LocalAddr().String())
sharedEarlyListener, _, err := listenerPool.LoadOrNew(lnKey, func() (Destructor, error) {
sqs := newSharedQUICState(tlsConf, activeRequests)
// http3.ConfigureTLSConfig only uses this field and tls App sets this field as well
//nolint:gosec
quicTlsConfig := &tls.Config{GetConfigForClient: sqs.getConfigForClient}
earlyLn, err := quic.ListenEarly(ln, http3.ConfigureTLSConfig(quicTlsConfig), &quic.Config{
Allow0RTT: true,
RequireAddressValidation: func(clientAddr net.Addr) bool {
// TODO: make tunable?
return sqs.getActiveRequests() > 1000
},
})
if err != nil {
return nil, err
}
return &sharedQuicListener{EarlyListener: earlyLn, sqs: sqs, key: lnKey}, nil
})
if err != nil {
return nil, err
}
sql := sharedEarlyListener.(*sharedQuicListener)
// add current tls.Config and request counter to sqs, so GetConfigForClient will always return the latest tls.Config in case of context cancellation,
// and the request counter will reflect current http server
ctx, cancel := sql.sqs.addState(tlsConf, activeRequests)
// add current tls.Config to sqs, so GetConfigForClient will always return the latest tls.Config in case of context cancellation
ctx, cancel := sql.sqs.addState(tlsConf)
return &fakeCloseQuicListener{
sharedQuicListener: sql,
@@ -551,25 +478,21 @@ type contextAndCancelFunc struct {
context.CancelFunc
}
// sharedQUICState manages GetConfigForClient and current number of active requests
// sharedQUICState manages GetConfigForClient
// see issue: https://github.com/caddyserver/caddy/pull/4849
type sharedQUICState struct {
rmu sync.RWMutex
tlsConfs map[*tls.Config]contextAndCancelFunc
requestCounters map[*tls.Config]*int64
activeTlsConf *tls.Config
activeRequestsCounter *int64
rmu sync.RWMutex
tlsConfs map[*tls.Config]contextAndCancelFunc
activeTlsConf *tls.Config
}
// newSharedQUICState creates a new sharedQUICState
func newSharedQUICState(tlsConfig *tls.Config, activeRequests *int64) *sharedQUICState {
func newSharedQUICState(tlsConfig *tls.Config) *sharedQUICState {
sqtc := &sharedQUICState{
tlsConfs: make(map[*tls.Config]contextAndCancelFunc),
requestCounters: make(map[*tls.Config]*int64),
activeTlsConf: tlsConfig,
activeRequestsCounter: activeRequests,
tlsConfs: make(map[*tls.Config]contextAndCancelFunc),
activeTlsConf: tlsConfig,
}
sqtc.addState(tlsConfig, activeRequests)
sqtc.addState(tlsConfig)
return sqtc
}
@@ -580,17 +503,9 @@ func (sqs *sharedQUICState) getConfigForClient(ch *tls.ClientHelloInfo) (*tls.Co
return sqs.activeTlsConf.GetConfigForClient(ch)
}
// getActiveRequests returns the number of active requests
func (sqs *sharedQUICState) getActiveRequests() int64 {
// Prevent a race when a context is cancelled and active request counter is being changed
sqs.rmu.RLock()
defer sqs.rmu.RUnlock()
return atomic.LoadInt64(sqs.activeRequestsCounter)
}
// addState adds tls.Config and activeRequests to the map if not present and returns the corresponding context and its cancelFunc
// so that when cancelled, the active tls.Config and request counter will change
func (sqs *sharedQUICState) addState(tlsConfig *tls.Config, activeRequests *int64) (context.Context, context.CancelFunc) {
// so that when cancelled, the active tls.Config will change
func (sqs *sharedQUICState) addState(tlsConfig *tls.Config) (context.Context, context.CancelFunc) {
sqs.rmu.Lock()
defer sqs.rmu.Unlock()
@@ -606,19 +521,16 @@ func (sqs *sharedQUICState) addState(tlsConfig *tls.Config, activeRequests *int6
defer sqs.rmu.Unlock()
delete(sqs.tlsConfs, tlsConfig)
delete(sqs.requestCounters, tlsConfig)
if sqs.activeTlsConf == tlsConfig {
// select another tls.Config and request counter, if there is none,
// select another tls.Config, if there is none,
// related sharedQuicListener will be destroyed anyway
for tc, counter := range sqs.requestCounters {
for tc := range sqs.tlsConfs {
sqs.activeTlsConf = tc
sqs.activeRequestsCounter = counter
break
}
}
}
sqs.tlsConfs[tlsConfig] = contextAndCancelFunc{ctx, wrappedCancel}
sqs.requestCounters[tlsConfig] = activeRequests
// there should be at most 2 tls.Configs
if len(sqs.tlsConfs) > 2 {
Log().Warn("quic listener tls configs are more than 2", zap.Int("number of configs", len(sqs.tlsConfs)))
+21 -2
View File
@@ -16,6 +16,7 @@ package caddy
import (
"encoding/json"
"flag"
"fmt"
"io"
"log"
@@ -292,6 +293,10 @@ type BaseLog struct {
// 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"`
// Tees entries through a zap.Core module which can extract
// log entry metadata and fields for further processing.
CoreRaw json.RawMessage `json:"core,omitempty" caddy:"namespace=caddy.logging.cores inline_key=module"`
// Level is the minimum level to emit, and is inclusive.
// Possible levels: DEBUG, INFO, WARN, ERROR, PANIC, and FATAL
Level string `json:"level,omitempty"`
@@ -366,13 +371,21 @@ func (cl *BaseLog) provisionCommon(ctx Context, logging *Logging) error {
cl.encoder = newDefaultProductionLogEncoder(cl.writerOpener)
}
cl.buildCore()
if cl.CoreRaw != nil {
mod, err := ctx.LoadModule(cl, "CoreRaw")
if err != nil {
return fmt.Errorf("loading log core module: %v", err)
}
core := mod.(zapcore.Core)
cl.core = zapcore.NewTee(cl.core, core)
}
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
// at all, so just shortcut to a nop core instead
if _, ok := cl.writerOpener.(*DiscardWriter); ok {
cl.core = zapcore.NewNopCore()
return
@@ -687,7 +700,13 @@ type defaultCustomLog struct {
// and enables INFO-level logs and higher.
func newDefaultProductionLog() (*defaultCustomLog, error) {
cl := new(CustomLog)
cl.writerOpener = StderrWriter{}
f := flag.Lookup("test.v")
if (f != nil && f.Value.String() != "true") || strings.Contains(os.Args[0], ".test") {
cl.writerOpener = &DiscardWriter{}
} else {
cl.writerOpener = StderrWriter{}
}
var err error
cl.writer, err = cl.writerOpener.OpenWriter()
if err != nil {
+14 -1
View File
@@ -261,7 +261,9 @@ func (app *App) Emit(ctx caddy.Context, eventName string, data map[string]any) E
return nil, false
})
logger.Debug("event", zap.Any("data", e.Data))
logger = logger.With(zap.Any("data", e.Data))
logger.Debug("event")
// invoke handlers bound to the event by name and also all events; this for loop
// iterates twice at most: once for the event name, once for "" (all events)
@@ -282,6 +284,12 @@ func (app *App) Emit(ctx caddy.Context, eventName string, data map[string]any) E
default:
}
// this log can be a useful sanity check to ensure your handlers are in fact being invoked
// (see https://github.com/mholt/caddy-events-exec/issues/6)
logger.Debug("invoking subscribed handler",
zap.String("subscribed_to", eventName),
zap.Any("handler", handler))
if err := handler.Handle(ctx, e); err != nil {
aborted := errors.Is(err, ErrAborted)
@@ -347,6 +355,11 @@ type Event struct {
origin caddy.Module
}
func (e Event) ID() uuid.UUID { return e.id }
func (e Event) Timestamp() time.Time { return e.ts }
func (e Event) Name() string { return e.name }
func (e Event) Origin() caddy.Module { return e.origin }
// CloudEvent exports event e as a structure that, when
// serialized as JSON, is compatible with the
// CloudEvents spec.
+1 -1
View File
@@ -72,7 +72,7 @@ func (xs *Filesystems) Provision(ctx caddy.Context) error {
ctx.Filesystems().Register(f.Key, f.fileSystem)
// remember to unregister the module when we are done
xs.defers = append(xs.defers, func() {
ctx.Logger().Debug("registering fs", zap.String("fs", f.Key))
ctx.Logger().Debug("unregistering fs", zap.String("fs", f.Key))
ctx.Filesystems().Unregister(f.Key)
})
}
+18 -2
View File
@@ -68,6 +68,9 @@ func init() {
// `{http.request.orig_uri.query}` | The request's original query string (without `?`)
// `{http.request.port}` | The port part of the request's Host header
// `{http.request.proto}` | The protocol of the request
// `{http.request.local.host}` | The host (IP) part of the local address the connection arrived on
// `{http.request.local.port}` | The port part of the local address the connection arrived on
// `{http.request.local}` | The local address the connection arrived on
// `{http.request.remote.host}` | The host (IP) part of the remote client's address
// `{http.request.remote.port}` | The port part of the remote client's address
// `{http.request.remote}` | The address of the remote client
@@ -195,6 +198,9 @@ func (app *App) Provision(ctx caddy.Context) error {
// only enable access logs if configured
if srv.Logs != nil {
srv.accessLogger = app.logger.Named("log.access")
if srv.Logs.Trace {
srv.traceLogger = app.logger.Named("log.trace")
}
}
// the Go standard library does not let us serve only HTTP/2 using
@@ -326,9 +332,10 @@ func (app *App) Provision(ctx caddy.Context) error {
// Validate ensures the app's configuration is valid.
func (app *App) Validate() error {
// each server must use distinct listener addresses
lnAddrs := make(map[string]string)
for srvName, srv := range app.Servers {
// each server must use distinct listener addresses
for _, addr := range srv.Listen {
listenAddr, err := caddy.ParseNetworkAddress(addr)
if err != nil {
@@ -344,6 +351,15 @@ func (app *App) Validate() error {
lnAddrs[addr] = srvName
}
}
// logger names must not have ports
if srv.Logs != nil {
for host := range srv.Logs.LoggerNames {
if _, _, err := net.SplitHostPort(host); err == nil {
return fmt.Errorf("server %s: logger name must not have a port: %s", srvName, host)
}
}
}
}
return nil
}
@@ -523,7 +539,7 @@ func (app *App) Stop() error {
ctx := context.Background()
// see if any listeners in our config will be closing or if they are continuing
// hrough a reload; because if any are closing, we will enforce shutdown delay
// through a reload; because if any are closing, we will enforce shutdown delay
var delay bool
scheduledTime := time.Now().Add(time.Duration(app.ShutdownDelay))
if app.ShutdownDelay > 0 {
+13 -3
View File
@@ -117,7 +117,7 @@ func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) er
srv.AutoHTTPS = new(AutoHTTPSConfig)
}
if srv.AutoHTTPS.Disabled {
logger.Warn("automatic HTTPS is completely disabled for server", zap.String("server_name", srvName))
logger.Info("automatic HTTPS is completely disabled for server", zap.String("server_name", srvName))
continue
}
@@ -225,7 +225,7 @@ func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) er
// nothing left to do if auto redirects are disabled
if srv.AutoHTTPS.DisableRedir {
logger.Warn("automatic HTTP->HTTPS redirects are disabled", zap.String("server_name", srvName))
logger.Info("automatic HTTP->HTTPS redirects are disabled", zap.String("server_name", srvName))
continue
}
@@ -260,7 +260,7 @@ func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) er
// port, we'll have to choose one, so prefer the HTTPS port
if _, ok := redirDomains[d]; !ok ||
addr.StartPort == uint(app.httpsPort()) {
redirDomains[d] = []caddy.NetworkAddress{addr}
redirDomains[d] = append(redirDomains[d], addr)
}
}
}
@@ -287,6 +287,16 @@ uniqueDomainsLoop:
for _, ap := range app.tlsApp.Automation.Policies {
for _, apHost := range ap.Subjects() {
if apHost == d {
// if the automation policy has all internal subjects but no issuers,
// it will default to CertMagic's issuers which are public CAs; use
// our internal issuer instead
if len(ap.Issuers) == 0 && ap.AllInternalSubjects() {
iss := new(caddytls.InternalIssuer)
if err := iss.Provision(ctx); err != nil {
return err
}
ap.Issuers = append(ap.Issuers, iss)
}
continue uniqueDomainsLoop
}
}
+16 -4
View File
@@ -76,7 +76,10 @@ type MiddlewareHandler interface {
}
// emptyHandler is used as a no-op handler.
var emptyHandler Handler = HandlerFunc(func(http.ResponseWriter, *http.Request) error { return nil })
var emptyHandler Handler = HandlerFunc(func(_ http.ResponseWriter, req *http.Request) error {
SetVar(req.Context(), "unhandled", true)
return nil
})
// An implicit suffix middleware that, if reached, sets the StatusCode to the
// error stored in the ErrorCtxKey. This is to prevent situations where the
@@ -120,7 +123,7 @@ type ResponseHandler struct {
Routes RouteList `json:"routes,omitempty"`
}
// Provision sets up the routse in rh.
// Provision sets up the routes in rh.
func (rh *ResponseHandler) Provision(ctx caddy.Context) error {
if rh.Routes != nil {
err := rh.Routes.Provision(ctx)
@@ -226,13 +229,22 @@ func StatusCodeMatches(actual, configured int) bool {
// in the implementation of http.Dir. The root is assumed to
// be a trusted path, but reqPath is not; and the output will
// never be outside of root. The resulting path can be used
// with the local file system.
// with the local file system. If root is empty, the current
// directory is assumed. If the cleaned request path is deemed
// not local according to lexical processing (i.e. ignoring links),
// it will be rejected as unsafe and only the root will be returned.
func SanitizedPathJoin(root, reqPath string) string {
if root == "" {
root = "."
}
path := filepath.Join(root, path.Clean("/"+reqPath))
relPath := path.Clean("/" + reqPath)[1:] // clean path and trim the leading /
if relPath != "" && !filepath.IsLocal(relPath) {
// path is unsafe (see https://github.com/golang/go/issues/56336#issuecomment-1416214885)
return root
}
path := filepath.Join(root, filepath.FromSlash(relPath))
// filepath.Join also cleans the path, and cleaning strips
// the trailing slash, so we need to re-add it afterwards.
+59 -15
View File
@@ -3,6 +3,7 @@ package caddyhttp
import (
"net/url"
"path/filepath"
"runtime"
"testing"
)
@@ -12,9 +13,10 @@ func TestSanitizedPathJoin(t *testing.T) {
// %2f = /
// %5c = \
for i, tc := range []struct {
inputRoot string
inputPath string
expect string
inputRoot string
inputPath string
expect string
expectWindows string
}{
{
inputPath: "",
@@ -24,22 +26,28 @@ func TestSanitizedPathJoin(t *testing.T) {
inputPath: "/",
expect: ".",
},
{
// fileserver.MatchFile passes an inputPath of "//" for some try_files values.
// See https://github.com/caddyserver/caddy/issues/6352
inputPath: "//",
expect: filepath.FromSlash("./"),
},
{
inputPath: "/foo",
expect: "foo",
},
{
inputPath: "/foo/",
expect: "foo" + separator,
expect: filepath.FromSlash("foo/"),
},
{
inputPath: "/foo/bar",
expect: filepath.Join("foo", "bar"),
expect: filepath.FromSlash("foo/bar"),
},
{
inputRoot: "/a",
inputPath: "/foo/bar",
expect: filepath.Join("/", "a", "foo", "bar"),
expect: filepath.FromSlash("/a/foo/bar"),
},
{
inputPath: "/foo/../bar",
@@ -48,32 +56,34 @@ func TestSanitizedPathJoin(t *testing.T) {
{
inputRoot: "/a/b",
inputPath: "/foo/../bar",
expect: filepath.Join("/", "a", "b", "bar"),
expect: filepath.FromSlash("/a/b/bar"),
},
{
inputRoot: "/a/b",
inputPath: "/..%2fbar",
expect: filepath.Join("/", "a", "b", "bar"),
expect: filepath.FromSlash("/a/b/bar"),
},
{
inputRoot: "/a/b",
inputPath: "/%2e%2e%2fbar",
expect: filepath.Join("/", "a", "b", "bar"),
expect: filepath.FromSlash("/a/b/bar"),
},
{
// inputPath fails the IsLocal test so only the root is returned,
// but with a trailing slash since one was included in inputPath
inputRoot: "/a/b",
inputPath: "/%2e%2e%2f%2e%2e%2f",
expect: filepath.Join("/", "a", "b") + separator,
expect: filepath.FromSlash("/a/b/"),
},
{
inputRoot: "/a/b",
inputPath: "/foo%2fbar",
expect: filepath.Join("/", "a", "b", "foo", "bar"),
expect: filepath.FromSlash("/a/b/foo/bar"),
},
{
inputRoot: "/a/b",
inputPath: "/foo%252fbar",
expect: filepath.Join("/", "a", "b", "foo%2fbar"),
expect: filepath.FromSlash("/a/b/foo%2fbar"),
},
{
inputRoot: "C:\\www",
@@ -81,9 +91,40 @@ func TestSanitizedPathJoin(t *testing.T) {
expect: filepath.Join("C:\\www", "foo", "bar"),
},
{
inputRoot: "C:\\www",
inputPath: "/D:\\foo\\bar",
expect: filepath.Join("C:\\www", "D:\\foo\\bar"),
inputRoot: "C:\\www",
inputPath: "/D:\\foo\\bar",
expect: filepath.Join("C:\\www", "D:\\foo\\bar"),
expectWindows: "C:\\www", // inputPath fails IsLocal on Windows
},
{
inputRoot: `C:\www`,
inputPath: `/..\windows\win.ini`,
expect: `C:\www/..\windows\win.ini`,
expectWindows: `C:\www`,
},
{
inputRoot: `C:\www`,
inputPath: `/..\..\..\..\..\..\..\..\..\..\windows\win.ini`,
expect: `C:\www/..\..\..\..\..\..\..\..\..\..\windows\win.ini`,
expectWindows: `C:\www`,
},
{
inputRoot: `C:\www`,
inputPath: `/..%5cwindows%5cwin.ini`,
expect: `C:\www/..\windows\win.ini`,
expectWindows: `C:\www`,
},
{
inputRoot: `C:\www`,
inputPath: `/..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5cwindows%5cwin.ini`,
expect: `C:\www/..\..\..\..\..\..\..\..\..\..\windows\win.ini`,
expectWindows: `C:\www`,
},
{
// https://github.com/golang/go/issues/56336#issuecomment-1416214885
inputRoot: "root",
inputPath: "/a/b/../../c",
expect: filepath.FromSlash("root/c"),
},
} {
// we don't *need* to use an actual parsed URL, but it
@@ -96,6 +137,9 @@ func TestSanitizedPathJoin(t *testing.T) {
t.Fatalf("Test %d: invalid URL: %v", i, err)
}
actual := SanitizedPathJoin(tc.inputRoot, u.Path)
if runtime.GOOS == "windows" && tc.expectWindows != "" {
tc.expect = tc.expectWindows
}
if actual != tc.expect {
t.Errorf("Test %d: SanitizedPathJoin('%s', '%s') => '%s' (expected '%s')",
i, tc.inputRoot, tc.inputPath, actual, tc.expect)

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