Compare commits

..

30 Commits

Author SHA1 Message Date
Dave Henderson d16ede358a metrics: Fix hidden panic while observing with bad exemplars (#3733)
* metrics: Fixing panic while observing with bad exemplars

Signed-off-by: Dave Henderson <dhenderson@gmail.com>

* Minor cleanup

The server is already added to the context. So, we can simply use that
to get the server name, which is a field on the server.

* Add integration test for auto HTTP->HTTPS redirects

A test like this would have caught the problem in the first place

Co-authored-by: Matthew Holt <mholt@users.noreply.github.com>
2020-09-17 21:46:24 -06:00
Matthew Holt c82c231ba7 caddyhttp: Remove server name from metrics
For some reason this breaks automatic HTTP->HTTPS redirects. I am not
sure why yet, but as a hotfix remove this until we understand it better.
2020-09-17 17:23:58 -06:00
Matthew Holt 3ee663dee1 go.mod: Upgrade dependencies 2020-09-17 12:35:25 -06:00
Dave Henderson 8ec51bbede metrics: Initial integration of Prometheus metrics (#3709)
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
2020-09-17 12:01:20 -06:00
Mohammed Al Sahaf bc453fa6ae reverseproxy: Correct alternate port for active health checks (#3693)
* reverseproxy: construct active health-check transport from scratch (Fixes #3691)

* reverseproxy: do upstream health-check on the correct alternative port

* reverseproxy: add integration test for health-check on alternative port

* reverseproxy: put back the custom transport for health-check http client

* reverseproxy: cleanup health-check integration test

* reverseproxy: fix health-check of unix socket upstreams

* reverseproxy: skip unix socket tests on Windows

* tabs > spaces

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

* make the linter (and @francislavoie) happy

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

* One more lint fix

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

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2020-09-17 10:25:34 -06:00
Francis Lavoie e3324aa6de httpcaddyfile: Ensure handle_path is sorted equally to handle (#3676)
* httpcaddyfile: Ensure handle_path is sorted as equal to handle

* httpcaddyfile: Make mutual exclusivity grouping deterministic (I hope)

* httpcaddyfile: Add comment linking to the issue being fixed

* httpcaddyfile: Typo fix, comment clarity

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

* Update caddyconfig/httpcaddyfile/httptype.go

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2020-09-16 20:01:22 -06:00
Mohammed Al Sahaf d55d50b3b3 reverseproxy: Enforce port range size of 1 at provision (#3695)
* reverse_proxy: ensure upstream address has port range of only 1

* reverse_proxy: don't log the error if upstream range size is more than 1
2020-09-16 19:48:37 -06:00
Francis Lavoie b95b87381a fileserver: Fix try_files for directories; windows fix (#3684)
* fileserver: Fix try_files for directories, windows fix

* fileserver: Add new file type placeholder, refactoring, tests

* fileserver: Review cleanup

* fileserver: Flip the return args order
2020-09-16 18:09:28 -06:00
Gaurav Dhameeja b01bb275b3 caddyhttp: New placeholder for PEM of client certificate (#3662)
* Fix-3585: added placeholder for a PEM encoded value of the certificate

* Update modules/caddyhttp/replacer.go

Change type of block and empty headers removed

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

* fixed tests

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2020-09-16 15:06:51 -06:00
Francis Lavoie 309c1fec62 logging: Implement Caddyfile support for filter encoder (#3578)
* logging: Implement Caddyfile support for filter encoder

* logging: Add support for parsing IP masks from strings


wip

* logging: Implement Caddyfile support for ip_mask

* logging: Get rid of unnecessary logic to allow strings, not that useful

* logging: Add adapt test
2020-09-15 12:37:41 -06:00
Matthew Penner b88e2b6a49 cmd: Allow caddy fmt to read from stdin (#3680)
* Allow 'caddy fmt' to read from stdin

* fmt: use '-' as the file name for reading from stdin

* Minor adjustments

Co-authored-by: Matthew Holt <mholt@users.noreply.github.com>
2020-09-14 12:30:12 -06:00
Matthew Holt 4217217bad httpcaddyfile: Properly record whether we added catch-all conn policy
We recently introduced `if !cp.SettingsEmpty()` which conditionally
adds the connection policy to the list. If the condition evaluates to
false, the policy wouldn't actually be added, even if
hasCatchAllTLSConnPolicy was set to true on the previous line.

Now we set that variable in accordance with whether we actually add
the policy.

While debugging this I noticed that catch-all policies added early in
that loop (i.e. not at the end if we later determine we need one) are
not always at the end of the list. They should be, though, since they
are selected by which one matches first, and having a catch-all first
would nullify any more specific ones later in the list. So I added a
sort in consolidateConnPolicies to take care of that.

Should fix #3670 and
https://caddy.community/t/combining-on-demand-tls-with-custom-ssl-certs-doesnt-seem-to-work-in-2-1-1/9719
but I won't know for sure until somebody verifies it, since at least in
the GitHub issue there is not yet enough information (the configs are
redacted).
2020-09-11 13:45:21 -06:00
Matt Holt 1c5969b576 fileserver: Fix new file hide tests on Windows (#3719) 2020-09-11 13:09:16 -06:00
Matthew Holt 0ee4378227 fileserver: Improve file hiding logic for directories and prefixes
Now, a filename to hide that is specified without a path separator will
count as hidden if it appears in any component of the file path (not
only the last component); semantically, this means hiding a file by only
its name (without any part of a path) will hide both files and folders,
e.g. hiding ".git" will hide "/.git" and also "/.git/foo".

We also do prefix matching so that hiding "/.git" will hide "/.git"
and "/.git/foo" but not "/.gitignore".

The remaining logic is a globular match like before.
2020-09-11 12:20:39 -06:00
Matthew Holt 9859ab8148 caddytls: Fix resolvers option of acme issuer (Caddyfile)
Reported in:
https://caddy.community/t/dns-challenge-with-namecheap-and-split-horizon-dns/9611/17?u=matt
2020-09-09 10:21:59 -06:00
Francis Lavoie 00e6b77fe4 caddytls: Add dns config to acmeissuer (#3701) 2020-09-08 11:36:46 -06:00
Mohammed Al Sahaf d4f249741e browse: align template to struct field renames from 4940325 (#3706) 2020-09-08 10:45:48 -06:00
Francis Lavoie 04f50a9759 caddyhttp: Wrap http.Server logging with zap (#3668) 2020-09-08 10:44:58 -06:00
Francis Lavoie 4cd7ae35b3 reverseproxy: Add buffer_requests option to reverse_proxy directive (#3710) 2020-09-08 10:37:46 -06:00
Matthew Holt 24f34780b6 caddytls: Customize DNS resolvers for DNS challenge with Caddyfile 2020-08-31 13:23:26 -06:00
Matthew Holt 724b74d981 reverseproxy: Abort active health checks on context cancellation 2020-08-31 13:22:34 -06:00
Matthew Holt 4940325844 fileserver: Fix inconsistencies in browse JSON 2020-08-31 12:33:43 -06:00
Matthew Holt 744d04c258 caddytls: Configure custom DNS resolvers for DNS challenge (close #2476)
And #3391

Maybe also related: #3664
2020-08-21 20:30:14 -06:00
Francis Lavoie ecbc1f85c5 ci: Tweaks for multi go version tests (#3673) 2020-08-20 22:40:26 -04:00
Matthew Holt 997ef522bc go.mod: Use v0.15(.1) of smallstep libs
Update internal issuer for compatibility -- yay simpler code!

The .1 version also fixes non-critical SAN extensions that caused trust
issues on several clients.
2020-08-20 19:28:25 -06:00
Francis Lavoie 0279a57ac4 ci: Upgrade to Go 1.15 (#3642)
* ci: Try Go 1.15 RC1 out of curiosity

* Go 1.15 was released; let's try it

* Update to latest quic-go

* Attempt at fixing broken test

Co-authored-by: Matthew Holt <mholt@users.noreply.github.com>
2020-08-20 14:04:10 -06:00
Matthew Holt c94f5bb7dd reverseproxy: Make default buffer size const 2020-08-17 16:17:16 -06:00
Francis Lavoie 0afbab8667 httpcaddyfile: Improve directive sorting logic (#3658)
* httpcaddyfile: Flip `root` directive sort order

* httpcaddyfile: Sort directives with any matcher before those with none

* httpcaddyfile: Generalize reverse sort directives, improve logic

* httpcaddyfile: Fix "spelling" issue

* httpcaddyfile: Turns out the second change precludes the first


httpcaddyfile: Delete test that no longer makes sense

* httpcaddyfile: Shorten logic

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

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2020-08-17 16:15:51 -06:00
Francis Lavoie fc65320e9c reverseproxy: Support header selection policy on Host field (#3653) 2020-08-17 15:14:46 -06:00
Matthew Holt e385be9225 Update comment and Caddy 1 EOL 2020-08-11 11:26:19 -06:00
53 changed files with 1638 additions and 283 deletions
+2 -2
View File
@@ -11,12 +11,12 @@ Please note that we consider publicly-registered domain names to be public infor
| Version | Supported |
| ------- | ------------------ |
| 2.x | :white_check_mark: |
| 1.x | :white_check_mark: (deprecating soon) |
| 1.x | :white_check_mark: (EOL 1 Oct 2020) |
| < 1.x | :x: |
## Reporting a Vulnerability
Please email Matt Holt (the author) directly: matt [at] lightcodelabs [dot com].
Please email Matt Holt (the author) directly: matt [at] ardanlabs [dot com].
We'll need enough information to verify the bug and make a patch. It will speed things up if you suggest a working patch, such as a code diff, and explain why and how it works. Reports that are not actionable, do not contain enough information, are too pushy/demanding, or are not able to convince us that it is a viable and practical attack on the web server itself may be deferred to a later time or possibly ignored, resources permitting. Priority will be given to credible, responsible reports that are constructive, specific, and actionable. Thank you for understanding.
+8 -8
View File
@@ -17,7 +17,7 @@ jobs:
fail-fast: false
matrix:
os: [ ubuntu-latest, macos-latest, windows-latest ]
go-version: [ 1.14.x ]
go: [ '1.14', '1.15' ]
# Set some variables per OS, usable via ${{ matrix.VAR }}
# CADDY_BIN_PATH: the path to the compiled Caddy binary, for artifact publishing
@@ -39,9 +39,9 @@ jobs:
steps:
- name: Install Go
uses: actions/setup-go@v1
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
go-version: ${{ matrix.go }}
- name: Checkout code
uses: actions/checkout@v2
@@ -69,12 +69,12 @@ jobs:
echo "::set-output name=go_cache::$(go env GOCACHE)"
- name: Cache the build cache
uses: actions/cache@v1
uses: actions/cache@v2
with:
path: ${{ steps.vars.outputs.go_cache }}
key: ${{ runner.os }}-go-ci-${{ hashFiles('**/go.sum') }}
key: ${{ runner.os }}-${{ matrix.go }}-go-ci-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-ci
${{ runner.os }}-${{ matrix.go }}-go-ci
- name: Get dependencies
run: |
@@ -91,7 +91,7 @@ jobs:
- name: Publish Build Artifact
uses: actions/upload-artifact@v1
with:
name: caddy_v2_${{ runner.os }}_${{ steps.vars.outputs.short_sha }}
name: caddy_${{ runner.os }}_go${{ matrix.go }}_${{ steps.vars.outputs.short_sha }}
path: ${{ matrix.CADDY_BIN_PATH }}
# Commented bits below were useful to allow the job to continue
@@ -167,7 +167,7 @@ jobs:
steps:
- name: checkout
uses: actions/checkout@v2
- uses: goreleaser/goreleaser-action@v1
- uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: check
+9 -5
View File
@@ -14,14 +14,15 @@ jobs:
fail-fast: false
matrix:
goos: ['android', 'linux', 'solaris', 'illumos', 'dragonfly', 'freebsd', 'openbsd', 'plan9', 'windows', 'darwin', 'netbsd']
go-version: [ 1.14.x ]
go: [ '1.14', '1.15' ]
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
go-version: ${{ matrix.go }}
- name: Print Go version and environment
id: vars
run: |
@@ -32,15 +33,18 @@ jobs:
printf "\n\nSystem environment:\n\n"
env
echo "::set-output name=go_cache::$(go env GOCACHE)"
- name: Cache the build cache
uses: actions/cache@v1
uses: actions/cache@v2
with:
path: ${{ steps.vars.outputs.go_cache }}
key: cross-build-go-${{ matrix.goos }}-${{ hashFiles('**/go.sum') }}
key: cross-build-go${{ matrix.go }}-${{ matrix.goos }}-${{ hashFiles('**/go.sum') }}
restore-keys: |
cross-build-go-${{ matrix.goos }}
cross-build-go${{ matrix.go }}-${{ matrix.goos }}
- name: Checkout code into the Go module directory
uses: actions/checkout@v2
- name: Run Build
env:
CGO_ENABLED: 0
+3 -3
View File
@@ -12,14 +12,14 @@ jobs:
strategy:
matrix:
os: [ ubuntu-latest ]
go-version: [ 1.14.x ]
go: [ '1.14' ]
runs-on: ${{ matrix.os }}
steps:
- name: Install Go
uses: actions/setup-go@v1
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
go-version: ${{ matrix.go }}
- name: Checkout code
uses: actions/checkout@v2
+7 -7
View File
@@ -11,14 +11,14 @@ jobs:
strategy:
matrix:
os: [ ubuntu-latest ]
go-version: [ 1.14.x ]
go: [ '1.15' ]
runs-on: ${{ matrix.os }}
steps:
- name: Install Go
uses: actions/setup-go@v1
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
go-version: ${{ matrix.go }}
- name: Checkout code
uses: actions/checkout@v2
@@ -54,16 +54,16 @@ jobs:
echo "::set-output name=tag_special::${TAG_SPECIAL}"
- name: Cache the build cache
uses: actions/cache@v1
uses: actions/cache@v2
with:
path: ${{ steps.vars.outputs.go_cache }}
key: ${{ runner.os }}-go-release-${{ hashFiles('**/go.sum') }}
key: ${{ runner.os }}-go${{ matrix.go }}-release-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-release
${{ runner.os }}-go${{ matrix.go }}-release
# GoReleaser will take care of publishing those artifacts into the release
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v1
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist
+33 -12
View File
@@ -35,6 +35,8 @@ import (
"sync"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.uber.org/zap"
)
@@ -107,34 +109,53 @@ func (admin AdminConfig) newAdminHandler(addr NetworkAddress) adminHandler {
mux: http.NewServeMux(),
}
addRouteWithMetrics := func(pattern string, handlerLabel string, h http.Handler) {
labels := prometheus.Labels{"path": pattern, "handler": handlerLabel}
h = promhttp.InstrumentHandlerCounter(
adminMetrics.requestCount.MustCurryWith(labels),
h,
)
muxWrap.mux.Handle(pattern, h)
}
// addRoute just calls muxWrap.mux.Handle after
// wrapping the handler with error handling
addRoute := func(pattern string, h AdminHandler) {
addRoute := func(pattern string, handlerLabel string, h AdminHandler) {
wrapper := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
err := h.ServeHTTP(w, r)
if err != nil {
labels := prometheus.Labels{
"path": pattern,
"handler": handlerLabel,
"method": r.Method,
}
adminMetrics.requestErrors.With(labels).Inc()
}
muxWrap.handleError(w, r, err)
})
muxWrap.mux.Handle(pattern, wrapper)
addRouteWithMetrics(pattern, handlerLabel, wrapper)
}
const handlerLabel = "admin"
// register standard config control endpoints
addRoute("/"+rawConfigKey+"/", AdminHandlerFunc(handleConfig))
addRoute("/id/", AdminHandlerFunc(handleConfigID))
addRoute("/stop", AdminHandlerFunc(handleStop))
addRoute("/"+rawConfigKey+"/", handlerLabel, AdminHandlerFunc(handleConfig))
addRoute("/id/", handlerLabel, AdminHandlerFunc(handleConfigID))
addRoute("/stop", handlerLabel, AdminHandlerFunc(handleStop))
// register debugging endpoints
muxWrap.mux.HandleFunc("/debug/pprof/", pprof.Index)
muxWrap.mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
muxWrap.mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
muxWrap.mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
muxWrap.mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
muxWrap.mux.Handle("/debug/vars", expvar.Handler())
addRouteWithMetrics("/debug/pprof/", handlerLabel, http.HandlerFunc(pprof.Index))
addRouteWithMetrics("/debug/pprof/cmdline", handlerLabel, http.HandlerFunc(pprof.Cmdline))
addRouteWithMetrics("/debug/pprof/profile", handlerLabel, http.HandlerFunc(pprof.Profile))
addRouteWithMetrics("/debug/pprof/symbol", handlerLabel, http.HandlerFunc(pprof.Symbol))
addRouteWithMetrics("/debug/pprof/trace", handlerLabel, http.HandlerFunc(pprof.Trace))
addRouteWithMetrics("/debug/vars", handlerLabel, expvar.Handler())
// register third-party module endpoints
for _, m := range GetModules("admin.api") {
router := m.New().(AdminRouter)
handlerLabel := m.ID.Name()
for _, route := range router.Routes() {
addRoute(route.Pattern, route.Handler)
addRoute(route.Pattern, handlerLabel, route.Handler)
}
}
+1
View File
@@ -77,6 +77,7 @@ func parseBind(h Helper) ([]ConfigValue, error) {
// ca_root <pem_file>
// dns <provider_name>
// on_demand
// eab <key_id> <mac_key>
// issuer <module_name> ...
// }
//
+9
View File
@@ -63,6 +63,7 @@ var directiveOrder = []string{
// handlers that typically respond to requests
"respond",
"metrics",
"reverse_proxy",
"php_fastcgi",
"file_server",
@@ -398,6 +399,14 @@ func sortRoutes(routes []ConfigValue) {
if len(jPM) > 0 {
jPathLen = len(jPM[0])
}
// if both directives have no path matcher, use whichever one
// has any kind of matcher defined first.
if iPathLen == 0 && jPathLen == 0 {
return len(iRoute.MatcherSetsRaw) > 0 && len(jRoute.MatcherSetsRaw) == 0
}
// sort with the most-specific (longest) path first
return iPathLen > jPathLen
})
}
+30 -4
View File
@@ -172,6 +172,15 @@ func (st ServerType) Setup(inputServerBlocks []caddyfile.ServerBlock,
if err != nil {
return nil, warnings, fmt.Errorf("parsing caddyfile tokens for '%s': %v", dir, err)
}
// As a special case, we want "handle_path" to be sorted
// at the same level as "handle", so we force them to use
// the same directive name after their parsing is complete.
// See https://github.com/caddyserver/caddy/issues/3675#issuecomment-678042377
if dir == "handle_path" {
dir = "handle"
}
for _, result := range results {
result.directive = dir
sb.pile[result.Class] = append(sb.pile[result.Class], result)
@@ -446,12 +455,12 @@ func (st *ServerType) serversFromPairings(
}
} else {
cp.DefaultSNI = defaultSNI
hasCatchAllTLSConnPolicy = true
}
// only append this policy if it actually changes something
if !cp.SettingsEmpty() {
srv.TLSConnPolicies = append(srv.TLSConnPolicies, cp)
hasCatchAllTLSConnPolicy = len(hosts) == 0
}
}
}
@@ -653,9 +662,15 @@ func detectConflictingSchemes(srv *caddyhttp.Server, serverBlocks []serverBlock,
return nil
}
// consolidateConnPolicies removes empty TLS connection policies and combines
// equivalent ones for a cleaner overall output.
// consolidateConnPolicies sorts any catch-all policy to the end, removes empty TLS connection
// policies, and combines equivalent ones for a cleaner overall output.
func consolidateConnPolicies(cps caddytls.ConnectionPolicies) (caddytls.ConnectionPolicies, error) {
// catch-all policies (those without any matcher) should be at the
// end, otherwise it nullifies any more specific policies
sort.SliceStable(cps, func(i, j int) bool {
return cps[j].MatchersRaw == nil && cps[i].MatchersRaw != nil
})
for i := 0; i < len(cps); i++ {
// compare it to the others
for j := 0; j < len(cps); j++ {
@@ -848,7 +863,18 @@ func buildSubroute(routes []ConfigValue, groupCounter counter) (*caddyhttp.Subro
// root directives would overwrite previously-matched ones; they should not cascade
"root": {},
}
for meDir, info := range mutuallyExclusiveDirs {
// we need to deterministically loop over each of these directives
// in order to keep the group numbers consistent
keys := make([]string, 0, len(mutuallyExclusiveDirs))
for k := range mutuallyExclusiveDirs {
keys = append(keys, k)
}
sort.Strings(keys)
for _, meDir := range keys {
info := mutuallyExclusiveDirs[meDir]
// see how many instances of the directive there are
for _, r := range routes {
if r.directive == meDir {
+22
View File
@@ -0,0 +1,22 @@
package integration
import (
"net/http"
"testing"
"github.com/caddyserver/caddy/v2/caddytest"
)
func TestAutoHTTPtoHTTPSRedirects(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
{
http_port 9080
https_port 9443
}
localhost:9443
respond "Yahaha! You found me!"
`, "caddyfile")
tester.AssertRedirect("http://localhost:9080/", "https://localhost/", http.StatusPermanentRedirect)
}
@@ -0,0 +1,105 @@
:80 {
handle /api/* {
respond "api"
}
handle_path /static/* {
respond "static"
}
handle {
respond "handle"
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":80"
],
"routes": [
{
"group": "group3",
"match": [
{
"path": [
"/static/*"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "rewrite",
"strip_path_prefix": "/static"
}
]
},
{
"handle": [
{
"body": "static",
"handler": "static_response"
}
]
}
]
}
]
},
{
"group": "group3",
"match": [
{
"path": [
"/api/*"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "api",
"handler": "static_response"
}
]
}
]
}
]
},
{
"group": "group3",
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "handle",
"handler": "static_response"
}
]
}
]
}
]
}
]
}
}
}
}
}
@@ -0,0 +1,69 @@
:80
log {
output stdout
format filter {
wrap console
fields {
request>headers>Authorization delete
request>headers>Server delete
request>remote_addr ip_mask {
ipv4 24
ipv6 32
}
}
}
}
----------
{
"logging": {
"logs": {
"default": {
"exclude": [
"http.log.access.log0"
]
},
"log0": {
"writer": {
"output": "stdout"
},
"encoder": {
"fields": {
"request\u003eheaders\u003eAuthorization": {
"filter": "delete"
},
"request\u003eheaders\u003eServer": {
"filter": "delete"
},
"request\u003eremote_addr": {
"filter": "ip_mask",
"ipv4_cidr": 24,
"ipv6_cidr": 32
}
},
"format": "filter",
"wrap": {
"format": "console"
}
},
"include": [
"http.log.access.log0"
]
}
}
},
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":80"
],
"logs": {
"default_logger_name": "log0"
}
}
}
}
}
}
@@ -10,6 +10,7 @@ https://example.com {
versions h2c 2
compression off
}
buffer_requests
}
}
@@ -38,6 +39,7 @@ https://example.com {
{
"handle": [
{
"buffer_requests": true,
"handler": "reverse_proxy",
"headers": {
"request": {
@@ -0,0 +1,55 @@
:80
file_server
@untrusted not remote_ip 10.1.1.0/24
file_server @untrusted
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":80"
],
"routes": [
{
"match": [
{
"not": [
{
"remote_ip": {
"ranges": [
"10.1.1.0/24"
]
}
}
]
}
],
"handle": [
{
"handler": "file_server",
"hide": [
"Caddyfile"
]
}
]
},
{
"handle": [
{
"handler": "file_server",
"hide": [
"Caddyfile"
]
}
]
}
]
}
}
}
}
}
+28
View File
@@ -0,0 +1,28 @@
package integration
import (
"net/http"
"testing"
"github.com/caddyserver/caddy/v2/caddytest"
)
func TestBrowse(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
{
http_port 9080
https_port 9443
}
http://localhost:9080 {
file_server browse
}
`, "caddyfile")
req, err := http.NewRequest(http.MethodGet, "http://localhost:9080/", nil)
if err != nil {
t.Fail()
return
}
tester.AssertResponseCode(req, 200)
}
@@ -0,0 +1,97 @@
package integration
import (
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"runtime"
"strings"
"testing"
"github.com/caddyserver/caddy/v2/caddytest"
)
func TestReverseProxyHealthCheck(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
{
http_port 9080
https_port 9443
}
http://localhost:2020 {
respond "Hello, World!"
}
http://localhost:2021 {
respond "ok"
}
http://localhost:9080 {
reverse_proxy {
to localhost:2020
health_path /health
health_port 2021
health_interval 2s
health_timeout 5s
}
}
`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
}
func TestReverseProxyHealthCheckUnixSocket(t *testing.T) {
if runtime.GOOS == "windows" {
t.SkipNow()
}
tester := caddytest.NewTester(t)
f, err := ioutil.TempFile("", "*.sock")
if err != nil {
t.Errorf("failed to create TempFile: %s", err)
return
}
// a hack to get a file name within a valid path to use as socket
socketName := f.Name()
os.Remove(f.Name())
server := http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if strings.HasPrefix(req.URL.Path, "/health") {
w.Write([]byte("ok"))
return
}
w.Write([]byte("Hello, World!"))
}),
}
unixListener, err := net.Listen("unix", socketName)
if err != nil {
t.Errorf("failed to listen on the socket: %s", err)
return
}
go server.Serve(unixListener)
t.Cleanup(func() {
server.Close()
})
runtime.Gosched() // Allow other goroutines to run
tester.InitServer(fmt.Sprintf(`
{
http_port 9080
https_port 9443
}
http://localhost:9080 {
reverse_proxy {
to unix/%s
health_path /health
health_port 2021
health_interval 2s
health_timeout 5s
}
}
`, socketName), "caddyfile")
tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
}
+13 -4
View File
@@ -533,7 +533,17 @@ func cmdFmt(fl Flags) (int, error) {
if formatCmdConfigFile == "" {
formatCmdConfigFile = "Caddyfile"
}
overwrite := fl.Bool("overwrite")
// as a special case, read from stdin if the file name is "-"
if formatCmdConfigFile == "-" {
input, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return caddy.ExitCodeFailedStartup,
fmt.Errorf("reading stdin: %v", err)
}
fmt.Print(string(caddyfile.Format(input)))
return caddy.ExitCodeSuccess, nil
}
input, err := ioutil.ReadFile(formatCmdConfigFile)
if err != nil {
@@ -543,9 +553,8 @@ func cmdFmt(fl Flags) (int, error) {
output := caddyfile.Format(input)
if overwrite {
err = ioutil.WriteFile(formatCmdConfigFile, output, 0644)
if err != nil {
if fl.Bool("overwrite") {
if err := ioutil.WriteFile(formatCmdConfigFile, output, 0600); err != nil {
return caddy.ExitCodeFailedStartup, nil
}
} else {
+6 -2
View File
@@ -263,8 +263,12 @@ provisioning stages.`,
Formats the Caddyfile by adding proper indentation and spaces to improve
human readability. It prints the result to stdout.
If --write is specified, the output will be written to the config file
directly instead of printing it.`,
If --overwrite is specified, the output will be written to the config file
directly instead of printing it.
If you wish you use stdin instead of a regular file, use - as the path.
When reading from stdin, the --overwrite flag has no effect: the result
is always printed to stdout.`,
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("format", flag.ExitOnError)
fs.Bool("overwrite", false, "Overwrite the input file with the results")
+10 -9
View File
@@ -6,20 +6,21 @@ require (
github.com/Masterminds/sprig/v3 v3.1.0
github.com/alecthomas/chroma v0.8.0
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a
github.com/caddyserver/certmagic v0.11.3-0.20200810220624-10a8b5c72339
github.com/caddyserver/certmagic v0.12.0
github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac
github.com/go-chi/chi v4.1.2+incompatible
github.com/google/cel-go v0.5.1
github.com/jsternberg/zap-logfmt v1.2.0
github.com/klauspost/compress v1.10.10
github.com/klauspost/cpuid v1.2.5
github.com/lucas-clemente/quic-go v0.17.3
github.com/mholt/acmez v0.1.1-0.20200810215816-dbe88fc6cf09
github.com/klauspost/compress v1.11.0
github.com/klauspost/cpuid v1.2.5 // cannot upgrade until arm is fixed: https://github.com/klauspost/cpuid/issues/52
github.com/lucas-clemente/quic-go v0.18.0
github.com/mholt/acmez v0.1.1
github.com/naoina/go-stringutil v0.1.0 // indirect
github.com/naoina/toml v0.1.1
github.com/smallstep/certificates v0.14.6
github.com/smallstep/cli v0.14.6
github.com/smallstep/nosql v0.3.0
github.com/prometheus/client_golang v1.7.1
github.com/smallstep/certificates v0.15.4
github.com/smallstep/cli v0.15.2
github.com/smallstep/nosql v0.3.0 // cannot upgrade until protobuf warning is fixed
github.com/smallstep/truststore v0.9.6
github.com/yuin/goldmark v1.2.1
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691
@@ -27,7 +28,7 @@ require (
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
golang.org/x/net v0.0.0-20200707034311-ab3426394381
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98
google.golang.org/protobuf v1.24.0
google.golang.org/protobuf v1.24.0 // cannot upgrade until warning is fixed
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v2 v2.3.0
)
+71 -48
View File
@@ -35,10 +35,8 @@ github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RP
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver/v3 v3.0.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk=
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/sprig/v3 v3.0.0/go.mod h1:NEUY/Qq8Gdm2xgYA+NwJM6wmfdRV9xkh8h/Rld20R0U=
github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TNDYD9Y=
github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA=
github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
@@ -80,18 +78,20 @@ github.com/aws/aws-sdk-go v1.30.29 h1:NXNqBS9hjOCpDL8SyCyl38gZX3LLLunKOJc5E7vJ8P
github.com/aws/aws-sdk-go v1.30.29/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bombsimon/wsl/v2 v2.0.0/go.mod h1:mf25kr/SqFEPhhcxW1+7pxzGlW+hIl/hYTKY95VwV8U=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/caddyserver/certmagic v0.11.3-0.20200810220624-10a8b5c72339 h1:wTD+Y63XoBtiTJhe/Xn7WLrwKenmjkt2WxH3FP+Y0DM=
github.com/caddyserver/certmagic v0.11.3-0.20200810220624-10a8b5c72339/go.mod h1:mqOzOvKa7UcC+TWbBLcP0ZLRut/xaaQBw0hRGWHBIkY=
github.com/caddyserver/certmagic v0.12.0 h1:1f7kxykaJkOVVpXJ8ZrC6RAO5F6+kKm9U7dBFbLNeug=
github.com/caddyserver/certmagic v0.12.0/go.mod h1:tr26xh+9fY5dN0J6IPAlMj07qpog22PJKa7Nw7j835U=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
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 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
@@ -149,10 +149,10 @@ github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.8.0/go.mod h1:3l45GVGkyrnYNl9HoIjnp2NnNWvh6hLAqD8yTfGjnw8=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
@@ -195,6 +195,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekf
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
@@ -202,8 +203,9 @@ github.com/golang/mock v1.0.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0 h1:Rd1kQnQu0Hq3qvJppYSG0HtP+f5LPPUiDswTLiEegLg=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -213,10 +215,10 @@ github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
@@ -293,14 +295,11 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs=
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo=
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
@@ -313,6 +312,7 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jsternberg/zap-logfmt v1.2.0 h1:1v+PK4/B48cy8cfQbxL4FmmNZrjnIMr2BsnyEmXqv2o=
@@ -329,8 +329,8 @@ github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7IL
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.10.10 h1:a/y8CglcM7gLGYmlbP/stPE5sR3hbhFRUjCBfd/0B3I=
github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.0 h1:wJbzvpYMVGG9iTI9VxpnNZfd4DzMPoCWze3GgSqz8yg=
github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.5 h1:VBd9MyVIiJHzzgnrLQG5Bcv75H4YaWrlKqWHjurxCGo=
@@ -351,11 +351,11 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+
github.com/letsencrypt/pkcs11key v2.0.1-0.20170608213348-396559074696+incompatible/go.mod h1:iGYXKqDXt0cpBthCHdr9ZdsQwyGlYFh/+8xa4WzIQ34=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/libdns/libdns v0.0.0-20200501023120-186724ffc821 h1:663opx/RKxiISi1ozf0WbvweQpYBgf34dx8hKSIau3w=
github.com/libdns/libdns v0.0.0-20200501023120-186724ffc821/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/libdns/libdns v0.1.0 h1:0ctCOrVJsVzj53mop1angHp/pE3hmAhP7KiHvR0HD04=
github.com/libdns/libdns v0.1.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/lucas-clemente/quic-go v0.17.3 h1:jMX/MmDNCljfisgMmPGUcBJ+zUh9w3d3ia4YJjYS3TM=
github.com/lucas-clemente/quic-go v0.17.3/go.mod h1:I0+fcNTdb9eS1ZcjQZbDVPGchJ86chcIxPALn9lEJqE=
github.com/lucas-clemente/quic-go v0.18.0 h1:JhQDdqxdwdmGdKsKgXi1+coHRoGhvU6z0rNzOJqZ/4o=
github.com/lucas-clemente/quic-go v0.18.0/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg=
github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@@ -364,10 +364,12 @@ github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/manifoldco/promptui v0.3.1 h1:BxqNa7q1hVHXIXy3iupJMkXYS3aHhbubJWv2Jmg6x64=
github.com/manifoldco/promptui v0.3.1/go.mod h1:zoCNXiJnyM03LlBgTsWv8mq28s7aTC71UgKasqRJHww=
github.com/marten-seemann/qpack v0.1.0 h1:/0M7lkda/6mus9B8u34Asqm8ZhHAAt9Ho0vniNuVSVg=
github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI=
github.com/marten-seemann/qtls v0.9.1 h1:O0YKQxNVPaiFgMng0suWEOY2Sb4LT2sRn9Qimq3Z1IQ=
github.com/marten-seemann/qtls v0.9.1/go.mod h1:T1MmAdDPyISzxlK6kjRr0pcZFBVd1OZbBb/j3cvzHhk=
github.com/marten-seemann/qpack v0.2.0 h1:/r1rhZoOmgxVKBqPNnYilZBDEyw+6OUHCbBzA5jc2y0=
github.com/marten-seemann/qpack v0.2.0/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc=
github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs=
github.com/marten-seemann/qtls-go1-15 v0.1.0 h1:i/YPXVxz8q9umso/5y474CNcHmTpA+5DH+mFPjx6PZg=
github.com/marten-seemann/qtls-go1-15 v0.1.0/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@@ -383,9 +385,10 @@ github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mholt/acmez v0.1.1-0.20200810215816-dbe88fc6cf09 h1:J7NVJ46iBFeWsUc5aeVv8QNO2mLhI6rJKIbpAsH7d7g=
github.com/mholt/acmez v0.1.1-0.20200810215816-dbe88fc6cf09/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
github.com/mholt/acmez v0.1.1 h1:KQODCqk+hBn3O7qfCRPj6L96uG65T5BSS95FKNEqtdA=
github.com/mholt/acmez v0.1.1/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo=
github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
@@ -421,21 +424,25 @@ github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a
github.com/newrelic/go-agent v2.15.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ=
github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM=
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
@@ -456,20 +463,28 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf
github.com/prometheus/client_golang v0.9.4/go.mod h1:oCXIBxdI62A4cR6aTRJCgetEjecSIYzOEaeAn4iYEpM=
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.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@@ -525,18 +540,21 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5/go.mod h1:TC9A4+RjIOS+HyTH7wG17/gSqVv95uDw2J64dQZx7RE=
github.com/smallstep/assert v0.0.0-20200103212524-b99dc1097b15 h1:kSImCuenAkXtCaBeQ1UhmzzJGRhSm8sVH7I3sHE2Qdg=
github.com/smallstep/assert v0.0.0-20200103212524-b99dc1097b15/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc=
github.com/smallstep/certificates v0.14.5/go.mod h1:zzpB8wMz967gL8FmK6zvCNB4pDVwFDKjPg1diTVc1h8=
github.com/smallstep/certificates v0.14.6 h1:sLVnIvIjZhd6qAosuf4Mw1Mh7IMA9XrZ7lKrGBuoHZQ=
github.com/smallstep/certificates v0.14.6/go.mod h1:nhih0lhKpbWE8JB0fg4EVCh/po18ivLiTPcF8eBeNtA=
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.15.0/go.mod h1:awyVXYIVn4J9ANSSnDuA3FjKia7+QixaqorGW8CKGq8=
github.com/smallstep/certificates v0.15.1 h1:Es2IGgU4D0RE4OKys/VB1KvAuGuZwkxR7D9A0DeM/Ow=
github.com/smallstep/certificates v0.15.1/go.mod h1:hDmNejfuVTDDe1NBIKrhL9xjELRJb2ZFM+bDXwSW9xI=
github.com/smallstep/certificates v0.15.4 h1:bBGb2GqrQ8wKHVOhcUfgRlaTHsO1S5rqsq/z93/mRSc=
github.com/smallstep/certificates v0.15.4/go.mod h1:uMrxSjDsPBxCPLV58WQhj3L1R3zdnW7mvJloiC+cyws=
github.com/smallstep/certinfo v1.3.0/go.mod h1:1gQJekdPwPvUwFWGTi7bZELmQT09cxC9wJ0VBkBNiwU=
github.com/smallstep/cli v0.14.5/go.mod h1:mRFuqC3cGwQESBGJvog4o76jZZZ7bMjkE+hAnq2QyR8=
github.com/smallstep/cli v0.14.6 h1:xc9rawDKB70Vgvg10gfQAh9EpDWS3k1O002J5bApqUk=
github.com/smallstep/cli v0.14.6/go.mod h1:Gs9mXwk5tIBlSISt1tvCAMso7eEAiJRyNgR/6JsoojI=
github.com/smallstep/cli v0.15.0 h1:ZBU1o4vm4FvcOFnfiT09jxuE/OsZX1BsZl5E/l2gv/c=
github.com/smallstep/cli v0.15.0/go.mod h1:trYpP49s+XF4fROcNgmPi4yO1EIKfqsc/eEck6SosO4=
github.com/smallstep/cli v0.15.2 h1:bOrYD1w0Vu82XN3r7mHuXoEI9RyczHyzKjzDHQ7V7EE=
github.com/smallstep/cli v0.15.2/go.mod h1:7SDI+reLecUkYdaluSt3ASH2vQYHxdeMXtsIGD9AQXA=
github.com/smallstep/nosql v0.3.0 h1:V1X5vfDsDt89499h3jZFUlR4VnnsYYs5tXaQZ0w8z5U=
github.com/smallstep/nosql v0.3.0/go.mod h1:QG7gNOpidifn99MjZaiNbm7HPesIyBd97F/OfacNz8Q=
github.com/smallstep/truststore v0.9.3/go.mod h1:PRSkpRIhAYBK/KLWkHNgRdYgzWMEy45bN7PSJCfKKGE=
github.com/smallstep/truststore v0.9.6 h1:vNzEJmaJL0XOZD8uouXLmYu4/aP1UQ/wHUopH3qKeYA=
github.com/smallstep/truststore v0.9.6/go.mod h1:HwHKRcBi0RUxxw1LYDpTRhYC4jZUuxPpkHdVonlkoDM=
github.com/smallstep/zcrypto v0.0.0-20200203191936-fbc32cf76bce/go.mod h1:+F24VU3UCxfVFvvqgm5jNUFQOm/L6ed13ImwWGFgg/g=
@@ -623,6 +641,12 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.step.sm/crypto v0.0.0-20200805202904-ec18b6df3cf0/go.mod h1:8VYxmvSKt5yOTBx3MGsD2Gk4F1Es/3FIxrjnfeYWE8U=
go.step.sm/crypto v0.1.1/go.mod h1:cIoSWTfTQ5xqvwTeZH9ZXZzi6jdMepjK4A/TDWMUvw8=
go.step.sm/crypto v0.2.0 h1:Rx+XqrNO4ZGHWlT9QCXls2L9pvcNiI23zEpAq0fctvY=
go.step.sm/crypto v0.2.0/go.mod h1:YNLnHj4JgABFoRkUq8brkscIB9THdiJUFoDxLQw1tww=
go.step.sm/crypto v0.6.0 h1:fbGUG5VJmDetC+RQ/T0tb6Sx0wCOgqKZcZYzTpUa7eo=
go.step.sm/crypto v0.6.0/go.mod h1:AKS4yMZVZD4EGjpSkY4eibuMenrvKCscb+BpWMet8c0=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@@ -650,15 +674,13 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -694,7 +716,6 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r
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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -710,6 +731,7 @@ golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -749,9 +771,12 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -760,8 +785,10 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -853,9 +880,7 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200305110556-506484158171 h1:xes2Q2k+d/+YNXVw0FpZkIDJiaux4OVrRKXRAzH6A0U=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98 h1:LCO0fg4kb6WwkXQXRQQgUYsFeFb5taTX5WAx5O/Vt28=
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
@@ -878,9 +903,7 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc h1:TnonUr8u3himcMY0vSh23jFOXA+cnucl1gB6EQTReBI=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
@@ -892,7 +915,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.28/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 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
@@ -903,6 +925,8 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.4.0 h1:0kXPskUMGAXXWJlP05ktEMOV0vmzFQUWw6d+aZJQU8A=
gopkg.in/square/go-jose.v2 v2.4.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
@@ -922,9 +946,8 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
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 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M=
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5 h1:AQkaJpH+/FmqRjmXZPELom5zIERYZfwTjnHpfoVMQEc=
howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY=
+32
View File
@@ -0,0 +1,32 @@
package caddy
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
// define and register the metrics used in this package.
func init() {
prometheus.MustRegister(prometheus.NewBuildInfoCollector())
const ns, sub = "caddy", "admin"
adminMetrics.requestCount = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: ns,
Subsystem: sub,
Name: "http_requests_total",
Help: "Counter of requests made to the Admin API's HTTP endpoints.",
}, []string{"handler", "path", "code", "method"})
adminMetrics.requestErrors = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: ns,
Subsystem: sub,
Name: "http_request_errors_total",
Help: "Number of requests resulting in middleware errors.",
}, []string{"handler", "path", "method"})
}
// adminMetrics is a collection of metrics that can be tracked for the admin API.
var adminMetrics = struct {
requestCount *prometheus.CounterVec
requestErrors *prometheus.CounterVec
}{}
+10 -1
View File
@@ -155,6 +155,7 @@ func (app *App) Provision(ctx caddy.Context) error {
// prepare each server
for srvName, srv := range app.Servers {
srv.name = srvName
srv.tlsApp = app.tlsApp
srv.logger = app.logger.Named("log")
srv.errorLogger = app.logger.Named("log.error")
@@ -281,6 +282,12 @@ func (app *App) Validate() error {
// Start runs the app. It finishes automatic HTTPS if enabled,
// including management of certificates.
func (app *App) Start() error {
// get a logger compatible with http.Server
serverLogger, err := zap.NewStdLogAt(app.logger.Named("stdlib"), zap.DebugLevel)
if err != nil {
return fmt.Errorf("failed to set up server logger: %v", err)
}
for srvName, srv := range app.Servers {
s := &http.Server{
ReadTimeout: time.Duration(srv.ReadTimeout),
@@ -289,6 +296,7 @@ func (app *App) Start() error {
IdleTimeout: time.Duration(srv.IdleTimeout),
MaxHeaderBytes: srv.MaxHeaderBytes,
Handler: srv,
ErrorLog: serverLogger,
}
// enable h2c if configured
@@ -344,6 +352,7 @@ func (app *App) Start() error {
Addr: hostport,
Handler: srv,
TLSConfig: tlsCfg,
ErrorLog: serverLogger,
},
}
go h3srv.Serve(h3ln)
@@ -382,7 +391,7 @@ func (app *App) Start() error {
// finish automatic HTTPS by finally beginning
// certificate management
err := app.automaticHTTPSPhase2()
err = app.automaticHTTPSPhase2()
if err != nil {
return fmt.Errorf("finalizing automatic HTTPS: %v", err)
}
+18 -18
View File
@@ -11,15 +11,15 @@ func BenchmarkBrowseWriteJSON(b *testing.B) {
fsrv := new(FileServer)
fsrv.Provision(caddy.Context{})
listing := browseListing{
Name: "test",
Path: "test",
CanGoUp: false,
Items: make([]fileInfo, 100),
NumDirs: 42,
NumFiles: 420,
Sort: "",
Order: "",
ItemsLimitedTo: 42,
Name: "test",
Path: "test",
CanGoUp: false,
Items: make([]fileInfo, 100),
NumDirs: 42,
NumFiles: 420,
Sort: "",
Order: "",
Limit: 42,
}
b.ResetTimer()
@@ -36,15 +36,15 @@ func BenchmarkBrowseWriteHTML(b *testing.B) {
template: template.New("test"),
}
listing := browseListing{
Name: "test",
Path: "test",
CanGoUp: false,
Items: make([]fileInfo, 100),
NumDirs: 42,
NumFiles: 420,
Sort: "",
Order: "",
ItemsLimitedTo: 42,
Name: "test",
Path: "test",
CanGoUp: false,
Items: make([]fileInfo, 100),
NumDirs: 42,
NumFiles: 420,
Sort: "",
Order: "",
Limit: 42,
}
b.ResetTimer()
+22 -22
View File
@@ -76,34 +76,34 @@ func (fsrv *FileServer) directoryListing(files []os.FileInfo, canGoUp bool, urlP
type browseListing struct {
// The name of the directory (the last element of the path).
Name string
Name string `json:"name"`
// The full path of the request.
Path string
Path string `json:"path"`
// Whether the parent directory is browseable.
CanGoUp bool
CanGoUp bool `json:"can_go_up"`
// The items (files and folders) in the path.
Items []fileInfo
// The number of directories in the listing.
NumDirs int
// The number of files (items that aren't directories) in the listing.
NumFiles int
// Sort column used
Sort string
// Sorting order
Order string
// If ≠0 then Items have been limited to that many elements.
ItemsLimitedTo int
Items []fileInfo `json:"items,omitempty"`
// If ≠0 then Items starting from that many elements.
ItemOffset int
Offset int `json:"offset,omitempty"`
// If ≠0 then Items have been limited to that many elements.
Limit int `json:"limit,omitempty"`
// The number of directories in the listing.
NumDirs int `json:"num_dirs"`
// The number of files (items that aren't directories) in the listing.
NumFiles int `json:"num_files"`
// Sort column used
Sort string `json:"sort,omitempty"`
// Sorting order
Order string `json:"order,omitempty"`
}
// Breadcrumbs returns l.Path where every element maps
@@ -166,7 +166,7 @@ func (l *browseListing) applySortAndLimit(sortParam, orderParam, limitParam stri
offset, _ := strconv.Atoi(offsetParam)
if offset > 0 && offset <= len(l.Items) {
l.Items = l.Items[offset:]
l.ItemOffset = offset
l.Offset = offset
}
}
@@ -175,7 +175,7 @@ func (l *browseListing) applySortAndLimit(sortParam, orderParam, limitParam stri
if limit > 0 && limit <= len(l.Items) {
l.Items = l.Items[:limit]
l.ItemsLimitedTo = limit
l.Limit = limit
}
}
}
+14 -14
View File
@@ -283,8 +283,8 @@ footer {
<div id="summary">
<span class="meta-item"><b>{{.NumDirs}}</b> director{{if eq 1 .NumDirs}}y{{else}}ies{{end}}</span>
<span class="meta-item"><b>{{.NumFiles}}</b> file{{if ne 1 .NumFiles}}s{{end}}</span>
{{- if ne 0 .ItemsLimitedTo}}
<span class="meta-item">(of which only <b>{{.ItemsLimitedTo}}</b> are displayed)</span>
{{- if ne 0 .Limit}}
<span class="meta-item">(of which only <b>{{.Limit}}</b> are displayed)</span>
{{- end}}
<span class="meta-item"><input type="text" placeholder="filter" id="filter" onkeyup='filter()'></span>
</div>
@@ -296,37 +296,37 @@ footer {
<th></th>
<th>
{{- if and (eq .Sort "namedirfirst") (ne .Order "desc")}}
<a href="?sort=namedirfirst&order=desc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}{{if ne 0 .ItemOffset}}&offset={{.ItemOffset}}{{end}}" class="icon"><svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg></a>
<a href="?sort=namedirfirst&order=desc{{if ne 0 .Limit}}&limit={{.Limit}}{{end}}{{if ne 0 .Offset}}&offset={{.Offset}}{{end}}" class="icon"><svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg></a>
{{- else if and (eq .Sort "namedirfirst") (ne .Order "asc")}}
<a href="?sort=namedirfirst&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}{{if ne 0 .ItemOffset}}&offset={{.ItemOffset}}{{end}}" class="icon"><svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#down-arrow"></use></svg></a>
<a href="?sort=namedirfirst&order=asc{{if ne 0 .Limit}}&limit={{.Limit}}{{end}}{{if ne 0 .Offset}}&offset={{.Offset}}{{end}}" class="icon"><svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#down-arrow"></use></svg></a>
{{- else}}
<a href="?sort=namedirfirst&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}{{if ne 0 .ItemOffset}}&offset={{.ItemOffset}}{{end}}" class="icon sort"><svg class="top" width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg><svg class="bottom" width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#down-arrow"></use></svg></a>
<a href="?sort=namedirfirst&order=asc{{if ne 0 .Limit}}&limit={{.Limit}}{{end}}{{if ne 0 .Offset}}&offset={{.Offset}}{{end}}" class="icon sort"><svg class="top" width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg><svg class="bottom" width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#down-arrow"></use></svg></a>
{{- end}}
{{- if and (eq .Sort "name") (ne .Order "desc")}}
<a href="?sort=name&order=desc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}{{if ne 0 .ItemOffset}}&offset={{.ItemOffset}}{{end}}">Name <svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg></a>
<a href="?sort=name&order=desc{{if ne 0 .Limit}}&limit={{.Limit}}{{end}}{{if ne 0 .Offset}}&offset={{.Offset}}{{end}}">Name <svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg></a>
{{- else if and (eq .Sort "name") (ne .Order "asc")}}
<a href="?sort=name&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}{{if ne 0 .ItemOffset}}&offset={{.ItemOffset}}{{end}}">Name <svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#down-arrow"></use></svg></a>
<a href="?sort=name&order=asc{{if ne 0 .Limit}}&limit={{.Limit}}{{end}}{{if ne 0 .Offset}}&offset={{.Offset}}{{end}}">Name <svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#down-arrow"></use></svg></a>
{{- else}}
<a href="?sort=name&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}{{if ne 0 .ItemOffset}}&offset={{.ItemOffset}}{{end}}">Name</a>
<a href="?sort=name&order=asc{{if ne 0 .Limit}}&limit={{.Limit}}{{end}}{{if ne 0 .Offset}}&offset={{.Offset}}{{end}}">Name</a>
{{- end}}
</th>
<th>
{{- if and (eq .Sort "size") (ne .Order "desc")}}
<a href="?sort=size&order=desc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}{{if ne 0 .ItemOffset}}&offset={{.ItemOffset}}{{end}}">Size <svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg></a>
<a href="?sort=size&order=desc{{if ne 0 .Limit}}&limit={{.Limit}}{{end}}{{if ne 0 .Offset}}&offset={{.Offset}}{{end}}">Size <svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg></a>
{{- else if and (eq .Sort "size") (ne .Order "asc")}}
<a href="?sort=size&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}{{if ne 0 .ItemOffset}}&offset={{.ItemOffset}}{{end}}">Size <svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#down-arrow"></use></svg></a>
<a href="?sort=size&order=asc{{if ne 0 .Limit}}&limit={{.Limit}}{{end}}{{if ne 0 .Offset}}&offset={{.Offset}}{{end}}">Size <svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#down-arrow"></use></svg></a>
{{- else}}
<a href="?sort=size&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}{{if ne 0 .ItemOffset}}&offset={{.ItemOffset}}{{end}}">Size</a>
<a href="?sort=size&order=asc{{if ne 0 .Limit}}&limit={{.Limit}}{{end}}{{if ne 0 .Offset}}&offset={{.Offset}}{{end}}">Size</a>
{{- end}}
</th>
<th class="hideable">
{{- if and (eq .Sort "time") (ne .Order "desc")}}
<a href="?sort=time&order=desc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}{{if ne 0 .ItemOffset}}&offset={{.ItemOffset}}{{end}}">Modified <svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg></a>
<a href="?sort=time&order=desc{{if ne 0 .Limit}}&limit={{.Limit}}{{end}}{{if ne 0 .Offset}}&offset={{.Offset}}{{end}}">Modified <svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg></a>
{{- else if and (eq .Sort "time") (ne .Order "asc")}}
<a href="?sort=time&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}{{if ne 0 .ItemOffset}}&offset={{.ItemOffset}}{{end}}">Modified <svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#down-arrow"></use></svg></a>
<a href="?sort=time&order=asc{{if ne 0 .Limit}}&limit={{.Limit}}{{end}}{{if ne 0 .Offset}}&offset={{.Offset}}{{end}}">Modified <svg width="1em" height=".5em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#down-arrow"></use></svg></a>
{{- else}}
<a href="?sort=time&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}{{if ne 0 .ItemOffset}}&offset={{.ItemOffset}}{{end}}">Modified</a>
<a href="?sort=time&order=asc{{if ne 0 .Limit}}&limit={{.Limit}}{{end}}{{if ne 0 .Offset}}&offset={{.Offset}}{{end}}">Modified</a>
{{- end}}
</th>
<th class="hideable"></th>
+53 -32
View File
@@ -19,6 +19,7 @@ import (
"net/http"
"os"
"path"
"path/filepath"
"strings"
"time"
@@ -34,7 +35,7 @@ func init() {
// MatchFile is an HTTP request matcher that can match
// requests based upon file existence.
//
// Upon matching, two new placeholders will be made
// Upon matching, three new placeholders will be made
// available:
//
// - `{http.matchers.file.relative}` The root-relative
@@ -42,6 +43,8 @@ func init() {
// requests.
// - `{http.matchers.file.absolute}` The absolute path
// of the matched file.
// - `{http.matchers.file.type}` Set to "directory" if
// the matched file is a directory, "file" otherwise.
type MatchFile struct {
// The root directory, used for creating absolute
// file paths, and required when working with
@@ -153,25 +156,18 @@ func (m MatchFile) Validate() error {
}
// Match returns true if r matches m. Returns true
// if a file was matched. If so, two placeholders
// if a file was matched. If so, three placeholders
// will be available:
// - http.matchers.file.relative
// - http.matchers.file.absolute
// - http.matchers.file.type
func (m MatchFile) Match(r *http.Request) bool {
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
rel, abs, matched := m.selectFile(r)
if matched {
repl.Set("http.matchers.file.relative", rel)
repl.Set("http.matchers.file.absolute", abs)
}
return matched
return m.selectFile(r)
}
// selectFile chooses a file according to m.TryPolicy by appending
// the paths in m.TryFiles to m.Root, with placeholder replacements.
// It returns the root-relative path to the matched file, the full
// or absolute path, and whether a match was made.
func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) {
func (m MatchFile) selectFile(r *http.Request) (matched bool) {
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
root := repl.ReplaceAll(m.Root, ".")
@@ -183,13 +179,35 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) {
m.TryFiles = []string{r.URL.Path}
}
// common preparation of the file into parts
prepareFilePath := func(file string) (string, string) {
suffix := m.firstSplit(path.Clean(repl.ReplaceAll(file, "")))
if strings.HasSuffix(file, "/") {
suffix += "/"
}
fullpath := sanitizedPathJoin(root, suffix)
return suffix, fullpath
}
// sets up the placeholders for the matched file
setPlaceholders := func(info os.FileInfo, rel string, abs string) {
repl.Set("http.matchers.file.relative", rel)
repl.Set("http.matchers.file.absolute", abs)
fileType := "file"
if info.IsDir() {
fileType = "directory"
}
repl.Set("http.matchers.file.type", fileType)
}
switch m.TryPolicy {
case "", tryPolicyFirstExist:
for _, f := range m.TryFiles {
suffix := m.firstSplit(path.Clean(repl.ReplaceAll(f, "")))
fullpath := sanitizedPathJoin(root, suffix)
if strictFileExists(fullpath) {
return suffix, fullpath, true
suffix, fullpath := prepareFilePath(f)
if info, exists := strictFileExists(fullpath); exists {
setPlaceholders(info, suffix, fullpath)
return true
}
}
@@ -197,9 +215,9 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) {
var largestSize int64
var largestFilename string
var largestSuffix string
var info os.FileInfo
for _, f := range m.TryFiles {
suffix := m.firstSplit(path.Clean(repl.ReplaceAll(f, "")))
fullpath := sanitizedPathJoin(root, suffix)
suffix, fullpath := prepareFilePath(f)
info, err := os.Stat(fullpath)
if err == nil && info.Size() > largestSize {
largestSize = info.Size()
@@ -207,15 +225,16 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) {
largestSuffix = suffix
}
}
return largestSuffix, largestFilename, true
setPlaceholders(info, largestSuffix, largestFilename)
return true
case tryPolicySmallestSize:
var smallestSize int64
var smallestFilename string
var smallestSuffix string
var info os.FileInfo
for _, f := range m.TryFiles {
suffix := m.firstSplit(path.Clean(repl.ReplaceAll(f, "")))
fullpath := sanitizedPathJoin(root, suffix)
suffix, fullpath := prepareFilePath(f)
info, err := os.Stat(fullpath)
if err == nil && (smallestSize == 0 || info.Size() < smallestSize) {
smallestSize = info.Size()
@@ -223,15 +242,16 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) {
smallestSuffix = suffix
}
}
return smallestSuffix, smallestFilename, true
setPlaceholders(info, smallestSuffix, smallestFilename)
return true
case tryPolicyMostRecentlyMod:
var recentDate time.Time
var recentFilename string
var recentSuffix string
var info os.FileInfo
for _, f := range m.TryFiles {
suffix := m.firstSplit(path.Clean(repl.ReplaceAll(f, "")))
fullpath := sanitizedPathJoin(root, suffix)
suffix, fullpath := prepareFilePath(f)
info, err := os.Stat(fullpath)
if err == nil &&
(recentDate.IsZero() || info.ModTime().After(recentDate)) {
@@ -240,7 +260,8 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) {
recentSuffix = suffix
}
}
return recentSuffix, recentFilename, true
setPlaceholders(info, recentSuffix, recentFilename)
return true
}
return
@@ -252,7 +273,7 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) {
// the file must also be a directory; if it does
// NOT end in a forward slash, the file must NOT
// be a directory.
func strictFileExists(file string) bool {
func strictFileExists(file string) (os.FileInfo, bool) {
stat, err := os.Stat(file)
if err != nil {
// in reality, this can be any error
@@ -263,16 +284,16 @@ func strictFileExists(file string) bool {
// the file exists, so we just treat any
// error as if it does not exist; see
// https://stackoverflow.com/a/12518877/1048862
return false
return nil, false
}
if strings.HasSuffix(file, "/") {
if strings.HasSuffix(file, string(filepath.Separator)) {
// by convention, file paths ending
// in a slash must be a directory
return stat.IsDir()
// in a path separator must be a directory
return stat, stat.IsDir()
}
// by convention, file paths NOT ending
// in a slash must NOT be a directory
return !stat.IsDir()
// in a path separator must NOT be a directory
return stat, !stat.IsDir()
}
// firstSplit returns the first result where the path
@@ -22,30 +22,114 @@ import (
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
func TestFileMatcher(t *testing.T) {
for i, tc := range []struct {
path string
expectedPath string
expectedType string
matched bool
}{
{
path: "/foo.txt",
expectedPath: "/foo.txt",
expectedType: "file",
matched: true,
},
{
path: "/foo.txt/",
expectedPath: "/foo.txt",
expectedType: "file",
matched: true,
},
{
path: "/foodir",
expectedPath: "/foodir/",
expectedType: "directory",
matched: true,
},
{
path: "/foodir/",
expectedPath: "/foodir/",
expectedType: "directory",
matched: true,
},
{
path: "/foodir/foo.txt",
expectedPath: "/foodir/foo.txt",
expectedType: "file",
matched: true,
},
{
path: "/missingfile.php",
matched: false,
},
} {
m := &MatchFile{
Root: "./testdata",
TryFiles: []string{"{http.request.uri.path}", "{http.request.uri.path}/"},
}
u, err := url.Parse(tc.path)
if err != nil {
t.Fatalf("Test %d: parsing path: %v", i, err)
}
req := &http.Request{URL: u}
repl := caddyhttp.NewTestReplacer(req)
result := m.Match(req)
if result != tc.matched {
t.Fatalf("Test %d: expected match=%t, got %t", i, tc.matched, result)
}
rel, ok := repl.Get("http.matchers.file.relative")
if !ok && result {
t.Fatalf("Test %d: expected replacer value", i)
}
if !result {
continue
}
if rel != tc.expectedPath {
t.Fatalf("Test %d: actual path: %v, expected: %v", i, rel, tc.expectedPath)
}
fileType, ok := repl.Get("http.matchers.file.type")
if fileType != tc.expectedType {
t.Fatalf("Test %d: actual file type: %v, expected: %v", i, fileType, tc.expectedType)
}
}
}
func TestPHPFileMatcher(t *testing.T) {
for i, tc := range []struct {
path string
expectedPath string
expectedType string
matched bool
}{
{
path: "/index.php",
expectedPath: "/index.php",
expectedType: "file",
matched: true,
},
{
path: "/index.php/somewhere",
expectedPath: "/index.php",
expectedType: "file",
matched: true,
},
{
path: "/remote.php",
expectedPath: "/remote.php",
expectedType: "file",
matched: true,
},
{
path: "/remote.php/somewhere",
expectedPath: "/remote.php",
expectedType: "file",
matched: true,
},
{
@@ -55,11 +139,13 @@ func TestPHPFileMatcher(t *testing.T) {
{
path: "/notphp.php.txt",
expectedPath: "/notphp.php.txt",
expectedType: "file",
matched: true,
},
{
path: "/notphp.php.txt/",
expectedPath: "/notphp.php.txt",
expectedType: "file",
matched: true,
},
{
@@ -69,12 +155,14 @@ func TestPHPFileMatcher(t *testing.T) {
{
path: "/foo.php.php/index.php",
expectedPath: "/foo.php.php/index.php",
expectedType: "file",
matched: true,
},
{
// See https://github.com/caddyserver/caddy/issues/3623
path: "/%E2%C3",
expectedPath: "/%E2%C3",
expectedType: "file",
matched: false,
},
} {
@@ -108,6 +196,11 @@ func TestPHPFileMatcher(t *testing.T) {
if rel != tc.expectedPath {
t.Fatalf("Test %d: actual path: %v, expected: %v", i, rel, tc.expectedPath)
}
fileType, ok := repl.Get("http.matchers.file.type")
if fileType != tc.expectedType {
t.Fatalf("Test %d: actual file type: %v, expected: %v", i, fileType, tc.expectedType)
}
}
}
+36 -17
View File
@@ -23,7 +23,6 @@ import (
"mime"
"net/http"
"os"
"path"
"path/filepath"
"strconv"
"strings"
@@ -323,34 +322,54 @@ func sanitizedPathJoin(root, reqPath string) string {
if root == "" {
root = "."
}
return filepath.Join(root, filepath.FromSlash(path.Clean("/"+reqPath)))
path := filepath.Join(root, filepath.Clean("/"+reqPath))
// filepath.Join also cleans the path, and cleaning strips
// the trailing slash, so we need to re-add it afterwards.
// if the length is 1, then it's a path to the root,
// and that should return ".", so we don't append the separator.
if strings.HasSuffix(reqPath, "/") && len(reqPath) > 1 {
path += string(filepath.Separator)
}
return path
}
// fileHidden returns true if filename is hidden
// according to the hide list.
func fileHidden(filename string, hide []string) bool {
nameOnly := filepath.Base(filename)
sep := string(filepath.Separator)
var components []string
for _, h := range hide {
// assuming h is a glob/shell-like pattern,
// use it to compare the whole file path;
// but if there is no separator in h, then
// just compare against the file's name
compare := filename
if !strings.Contains(h, sep) {
compare = nameOnly
}
hidden, err := filepath.Match(h, compare)
if err != nil {
// malformed pattern; fallback by checking prefix
if strings.HasPrefix(filename, h) {
// if there is no separator in h, then we assume the user
// wants to hide any files or folders that match that
// name; thus we have to compare against each component
// of the filename, e.g. hiding "bar" would hide "/bar"
// as well as "/foo/bar/baz" but not "/barstool".
if len(components) == 0 {
components = strings.Split(filename, sep)
}
for _, c := range components {
if c == h {
return true
}
}
} else if strings.HasPrefix(filename, h) {
// otherwise, if there is a separator in h, and
// filename is exactly prefixed with h, then we
// can do a prefix match so that "/foo" matches
// "/foo/bar" but not "/foobar".
withoutPrefix := strings.TrimPrefix(filename, h)
if strings.HasPrefix(withoutPrefix, sep) {
return true
}
}
if hidden {
// file name or path matches hide pattern
// in the general case, a glob match will suffice
if hidden, _ := filepath.Match(h, filename); hidden {
return true
}
}
@@ -42,6 +42,10 @@ func TestSanitizedPathJoin(t *testing.T) {
inputPath: "/foo",
expect: "foo",
},
{
inputPath: "/foo/",
expect: "foo" + string(filepath.Separator),
},
{
inputPath: "/foo/bar",
expect: filepath.Join("foo", "bar"),
@@ -73,7 +77,7 @@ func TestSanitizedPathJoin(t *testing.T) {
{
inputRoot: "/a/b",
inputPath: "/%2e%2e%2f%2e%2e%2f",
expect: filepath.Join("/", "a", "b"),
expect: filepath.Join("/", "a", "b") + string(filepath.Separator),
},
{
inputRoot: "C:\\www",
@@ -93,9 +97,84 @@ func TestSanitizedPathJoin(t *testing.T) {
}
actual := sanitizedPathJoin(tc.inputRoot, u.Path)
if actual != tc.expect {
t.Errorf("Test %d: [%s %s] => %s (expected %s)", i, tc.inputRoot, tc.inputPath, actual, tc.expect)
t.Errorf("Test %d: [%s %s] => %s (expected %s)",
i, tc.inputRoot, tc.inputPath, actual, tc.expect)
}
}
}
// TODO: test fileHidden
func TestFileHidden(t *testing.T) {
for i, tc := range []struct {
inputHide []string
inputPath string
expect bool
}{
{
inputHide: nil,
inputPath: "",
expect: false,
},
{
inputHide: []string{".gitignore"},
inputPath: "/.gitignore",
expect: true,
},
{
inputHide: []string{".git"},
inputPath: "/.gitignore",
expect: false,
},
{
inputHide: []string{"/.git"},
inputPath: "/.gitignore",
expect: false,
},
{
inputHide: []string{".git"},
inputPath: "/.git",
expect: true,
},
{
inputHide: []string{".git"},
inputPath: "/.git/foo",
expect: true,
},
{
inputHide: []string{".git"},
inputPath: "/foo/.git/bar",
expect: true,
},
{
inputHide: []string{"/prefix"},
inputPath: "/prefix/foo",
expect: true,
},
{
inputHide: []string{"/foo/*/bar"},
inputPath: "/foo/asdf/bar",
expect: true,
},
{
inputHide: []string{"/foo"},
inputPath: "/foo",
expect: true,
},
{
inputHide: []string{"/foo"},
inputPath: "/foobar",
expect: false,
},
} {
// for Windows' sake
tc.inputPath = filepath.FromSlash(tc.inputPath)
for i := range tc.inputHide {
tc.inputHide[i] = filepath.FromSlash(tc.inputHide[i])
}
actual := fileHidden(tc.inputPath, tc.inputHide)
if actual != tc.expect {
t.Errorf("Test %d: Is %s hidden in %v? Got %t but expected %t",
i, tc.inputPath, tc.inputHide, actual, tc.expect)
}
}
}
+1
View File
@@ -0,0 +1 @@
foo.txt
+1
View File
@@ -0,0 +1 @@
foodir/foo.txt
+175
View File
@@ -0,0 +1,175 @@
package caddyhttp
import (
"context"
"net/http"
"strconv"
"sync"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
var httpMetrics = struct {
init sync.Once
requestInFlight *prometheus.GaugeVec
requestCount *prometheus.CounterVec
requestErrors *prometheus.CounterVec
requestDuration *prometheus.HistogramVec
requestSize *prometheus.HistogramVec
responseSize *prometheus.HistogramVec
responseDuration *prometheus.HistogramVec
}{
init: sync.Once{},
}
func initHTTPMetrics() {
const ns, sub = "caddy", "http"
basicLabels := []string{"server", "handler"}
httpMetrics.requestInFlight = promauto.NewGaugeVec(prometheus.GaugeOpts{
Namespace: ns,
Subsystem: sub,
Name: "requests_in_flight",
Help: "Number of requests currently handled by this server.",
}, basicLabels)
httpMetrics.requestErrors = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: ns,
Subsystem: sub,
Name: "request_errors_total",
Help: "Number of requests resulting in middleware errors.",
}, basicLabels)
httpMetrics.requestCount = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: ns,
Subsystem: sub,
Name: "requests_total",
Help: "Counter of HTTP(S) requests made.",
}, basicLabels)
// TODO: allow these to be customized in the config
durationBuckets := prometheus.DefBuckets
sizeBuckets := prometheus.ExponentialBuckets(256, 4, 8)
httpLabels := []string{"server", "handler", "code", "method"}
httpMetrics.requestDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
Namespace: ns,
Subsystem: sub,
Name: "request_duration_seconds",
Help: "Histogram of round-trip request durations.",
Buckets: durationBuckets,
}, httpLabels)
httpMetrics.requestSize = promauto.NewHistogramVec(prometheus.HistogramOpts{
Namespace: ns,
Subsystem: sub,
Name: "request_size_bytes",
Help: "Total size of the request. Includes body",
Buckets: sizeBuckets,
}, httpLabels)
httpMetrics.responseSize = promauto.NewHistogramVec(prometheus.HistogramOpts{
Namespace: ns,
Subsystem: sub,
Name: "response_size_bytes",
Help: "Size of the returned response.",
Buckets: sizeBuckets,
}, httpLabels)
httpMetrics.responseDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
Namespace: ns,
Subsystem: sub,
Name: "response_duration_seconds",
Help: "Histogram of times to first byte in response bodies.",
Buckets: durationBuckets,
}, httpLabels)
}
// serverNameFromContext extracts the current server name from the context.
// Returns "UNKNOWN" if none is available (should probably never happen).
func serverNameFromContext(ctx context.Context) string {
srv, ok := ctx.Value(ServerCtxKey).(*Server)
if !ok || srv == nil || srv.name == "" {
return "UNKNOWN"
}
return srv.name
}
type metricsInstrumentedHandler struct {
handler string
mh MiddlewareHandler
}
func newMetricsInstrumentedHandler(handler string, mh MiddlewareHandler) *metricsInstrumentedHandler {
httpMetrics.init.Do(func() {
initHTTPMetrics()
})
return &metricsInstrumentedHandler{handler, mh}
}
func (h *metricsInstrumentedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request, next Handler) error {
server := serverNameFromContext(r.Context())
labels := prometheus.Labels{"server": server, "handler": h.handler}
statusLabels := prometheus.Labels{"server": server, "handler": h.handler, "method": r.Method}
inFlight := httpMetrics.requestInFlight.With(labels)
inFlight.Inc()
defer inFlight.Dec()
start := time.Now()
// This is a _bit_ of a hack - it depends on the ShouldBufferFunc always
// being called when the headers are written.
// Effectively the same behaviour as promhttp.InstrumentHandlerTimeToWriteHeader.
writeHeaderRecorder := ShouldBufferFunc(func(status int, header http.Header) bool {
statusLabels["code"] = sanitizeCode(status)
ttfb := time.Since(start).Seconds()
httpMetrics.responseDuration.With(statusLabels).Observe(ttfb)
return false
})
wrec := NewResponseRecorder(w, nil, writeHeaderRecorder)
err := h.mh.ServeHTTP(wrec, r, next)
dur := time.Since(start).Seconds()
httpMetrics.requestCount.With(labels).Inc()
if err != nil {
httpMetrics.requestErrors.With(labels).Inc()
return err
}
httpMetrics.requestDuration.With(statusLabels).Observe(dur)
httpMetrics.requestSize.With(statusLabels).Observe(float64(computeApproximateRequestSize(r)))
httpMetrics.responseSize.With(statusLabels).Observe(float64(wrec.Size()))
return nil
}
func sanitizeCode(code int) string {
if code == 0 {
return "200"
}
return strconv.Itoa(code)
}
// taken from https://github.com/prometheus/client_golang/blob/6007b2b5cae01203111de55f753e76d8dac1f529/prometheus/promhttp/instrument_server.go#L298
func computeApproximateRequestSize(r *http.Request) int {
s := 0
if r.URL != nil {
s += len(r.URL.String())
}
s += len(r.Method)
s += len(r.Proto)
for name, values := range r.Header {
s += len(name)
for _, value := range values {
s += len(value)
}
}
s += len(r.Host)
// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.
if r.ContentLength != -1 {
s += int(r.ContentLength)
}
return s
}
+66
View File
@@ -0,0 +1,66 @@
package caddyhttp
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"testing"
"github.com/prometheus/client_golang/prometheus/testutil"
)
func TestServerNameFromContext(t *testing.T) {
ctx := context.Background()
expected := "UNKNOWN"
if actual := serverNameFromContext(ctx); actual != expected {
t.Errorf("Not equal: expected %q, but got %q", expected, actual)
}
in := "foo"
ctx = context.WithValue(ctx, ServerCtxKey, &Server{name: in})
if actual := serverNameFromContext(ctx); actual != in {
t.Errorf("Not equal: expected %q, but got %q", in, actual)
}
}
func TestMetricsInstrumentedHandler(t *testing.T) {
handlerErr := errors.New("oh noes")
response := []byte("hello world!")
h := HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
if actual := testutil.ToFloat64(httpMetrics.requestInFlight); actual != 1.0 {
t.Errorf("Not same: expected %#v, but got %#v", 1.0, actual)
}
if handlerErr == nil {
w.Write(response)
}
return handlerErr
})
mh := middlewareHandlerFunc(func(w http.ResponseWriter, r *http.Request, h Handler) error {
return h.ServeHTTP(w, r)
})
ih := newMetricsInstrumentedHandler("bar", mh)
r := httptest.NewRequest("GET", "/", nil)
w := httptest.NewRecorder()
if actual := ih.ServeHTTP(w, r, h); actual != handlerErr {
t.Errorf("Not same: expected %#v, but got %#v", handlerErr, actual)
}
if actual := testutil.ToFloat64(httpMetrics.requestInFlight); actual != 0.0 {
t.Errorf("Not same: expected %#v, but got %#v", 0.0, actual)
}
handlerErr = nil
if err := ih.ServeHTTP(w, r, h); err != nil {
t.Errorf("Received unexpected error: %w", err)
}
}
type middlewareHandlerFunc func(http.ResponseWriter, *http.Request, Handler) error
func (f middlewareHandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request, h Handler) error {
return f(w, r, h)
}
+4
View File
@@ -25,6 +25,7 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"fmt"
"io"
"io/ioutil"
@@ -343,6 +344,9 @@ func getReqTLSReplacement(req *http.Request, key string) (interface{}, bool) {
return cert.SerialNumber, true
case "client.subject":
return cert.Subject, true
case "client.certificate_pem":
block := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
return pem.EncodeToMemory(&block), true
default:
return nil, false
}
+4
View File
@@ -171,6 +171,10 @@ eqp31wM9il1n+guTNyxJd+FzVAH+hCZE5K+tCgVDdVFUlDEHHbS/wqb2PSIoouLV
input: "{http.request.tls.client.san.ips.0}",
expect: "127.0.0.1",
},
{
input: "{http.request.tls.client.certificate_pem}",
expect: string(clientCert) + "\n", // returned value comes with a newline appended to it
},
} {
actual := repl.ReplaceAll(tc.input, "<empty>")
if actual != tc.expect {
@@ -467,6 +467,12 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
h.FlushInterval = caddy.Duration(dur)
}
case "buffer_requests":
if d.NextArg() {
return d.ArgErr()
}
h.BufferRequests = true
case "header_up":
if h.Headers == nil {
h.Headers = new(headers.Handler)
@@ -249,7 +249,7 @@ func DisabledTest(t *testing.T) {
log.Println("test:", "post data (more than 60KB)")
data := ""
for i := 0x00; i < 0xff; i++ {
v0 := strings.Repeat(string(i), 256)
v0 := strings.Repeat(fmt.Sprint(i), 256)
h := md5.New()
_, _ = io.WriteString(h, v0)
k0 := fmt.Sprintf("%x", h.Sum(nil))
@@ -266,7 +266,7 @@ func DisabledTest(t *testing.T) {
log.Println("test:", "post forms (256 keys, more than 1MB)")
p1 := make(map[string]string, 1)
for i := 0x00; i < 0xff; i++ {
v0 := strings.Repeat(string(i), 4096)
v0 := strings.Repeat(fmt.Sprint(i), 4096)
h := md5.New()
_, _ = io.WriteString(h, v0)
k0 := fmt.Sprintf("%x", h.Sum(nil))
+16 -22
View File
@@ -153,32 +153,27 @@ func (h *Handler) doActiveHealthCheckForAllHosts() {
log.Printf("[PANIC] active health check: %v\n%s", err, debug.Stack())
}
}()
networkAddr := upstream.Dial
addr, err := caddy.ParseNetworkAddress(networkAddr)
if err != nil {
h.HealthChecks.Active.logger.Error("bad network address",
zap.String("address", networkAddr),
zap.Error(err),
)
return
}
if addr.PortRangeSize() != 1 {
h.HealthChecks.Active.logger.Error("multiple addresses (upstream must map to only one address)",
zap.String("address", networkAddr),
)
return
}
hostAddr := addr.JoinHostPort(0)
if addr.IsUnixNetwork() {
portStr := strconv.Itoa(upstream.activeHealthCheckPort)
hostAddr := net.JoinHostPort(upstream.networkAddress.Host, portStr)
if upstream.networkAddress.IsUnixNetwork() {
// this will be used as the Host portion of a http.Request URL, and
// paths to socket files would produce an error when creating URL,
// so use a fake Host value instead; unix sockets are usually local
hostAddr = "localhost"
}
err = h.doActiveHealthCheck(DialInfo{Network: addr.Network, Address: hostAddr}, hostAddr, upstream.Host)
dialInfo := DialInfo{
Upstream: upstream,
Network: upstream.networkAddress.Network,
Host: upstream.networkAddress.Host,
Port: portStr,
Address: hostAddr,
}
err := h.doActiveHealthCheck(dialInfo, hostAddr, upstream.Host)
if err != nil {
h.HealthChecks.Active.logger.Error("active health check failed",
zap.String("address", networkAddr),
zap.String("address", hostAddr),
zap.Error(err),
)
}
@@ -216,9 +211,8 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, host H
u.Host = net.JoinHostPort(host, portStr)
}
// attach dialing information to this request - TODO: use caddy.Context's context
// so it can be canceled on config reload
ctx := context.Background()
// attach dialing information to this request
ctx := h.ctx.Context
ctx = context.WithValue(ctx, caddy.ReplacerCtxKey, caddy.NewReplacer())
ctx = context.WithValue(ctx, caddyhttp.VarsCtxKey, map[string]interface{}{
dialInfoVarKey: dialInfo,
+4 -2
View File
@@ -92,8 +92,10 @@ type Upstream struct {
// HeaderAffinity string
// IPAffinity string
healthCheckPolicy *PassiveHealthChecks
cb CircuitBreaker
networkAddress caddy.NetworkAddress
activeHealthCheckPort int
healthCheckPolicy *PassiveHealthChecks
cb CircuitBreaker
}
func (u Upstream) String() string {
@@ -182,6 +182,9 @@ func (h *HTTPTransport) NewTransport(ctx caddy.Context) (*http.Transport, error)
if dialInfo, ok := GetDialInfo(ctx); ok {
network = dialInfo.Network
address = dialInfo.Address
if dialInfo.Upstream.networkAddress.IsUnixNetwork() {
address = dialInfo.Host
}
}
conn, err := dialer.DialContext(ctx, network, address)
if err != nil {
@@ -204,6 +204,17 @@ func (h *Handler) Provision(ctx caddy.Context) error {
// set up upstreams
for _, upstream := range h.Upstreams {
addr, err := caddy.ParseNetworkAddress(upstream.Dial)
if err != nil {
return err
}
if addr.PortRangeSize() != 1 {
return fmt.Errorf("multiple addresses (upstream must map to only one address): %v", addr)
}
upstream.networkAddress = addr
// create or get the host representation for this upstream
var host Host = new(upstreamHost)
existingHost, loaded := hosts.LoadOrStore(upstream.String(), host)
@@ -260,6 +271,16 @@ func (h *Handler) Provision(ctx caddy.Context) error {
Transport: h.Transport,
}
for _, upstream := range h.Upstreams {
// if there's an alternative port for health-check provided in the config,
// then use it, otherwise use the port of upstream.
if h.HealthChecks.Active.Port != 0 {
upstream.activeHealthCheckPort = h.HealthChecks.Active.Port
} else {
upstream.activeHealthCheckPort = int(upstream.networkAddress.StartPort)
}
}
if h.HealthChecks.Active.Interval == 0 {
h.HealthChecks.Active.Interval = caddy.Duration(30 * time.Second)
}
@@ -362,6 +362,13 @@ func (s HeaderHashSelection) Select(pool UpstreamPool, req *http.Request) *Upstr
if s.Field == "" {
return nil
}
// The Host header should be obtained from the req.Host field
// since net/http removes it from the header map.
if s.Field == "Host" && req.Host != "" {
return hostByHashing(pool, req.Host)
}
val := req.Header.Get(s.Field)
if val == "" {
return RandomSelection{}.Select(pool, req)
+4 -2
View File
@@ -145,7 +145,7 @@ func (h Handler) copyResponse(dst io.Writer, src io.Reader, flushInterval time.D
// of bytes written.
func (h Handler) copyBuffer(dst io.Writer, src io.Reader, buf []byte) (int64, error) {
if len(buf) == 0 {
buf = make([]byte, 32*1024)
buf = make([]byte, defaultBufferSize)
}
var written int64
for {
@@ -252,6 +252,8 @@ func (c switchProtocolCopier) copyToBackend(errc chan<- error) {
var streamingBufPool = sync.Pool{
New: func() interface{} {
return make([]byte, 32*1024)
return make([]byte, defaultBufferSize)
},
}
const defaultBufferSize = 32 * 1024
+6 -3
View File
@@ -157,7 +157,7 @@ func (routes RouteList) ProvisionHandlers(ctx caddy.Context) error {
// pre-compile the middleware handler chain
for _, midhandler := range routes[i].Handlers {
routes[i].middleware = append(routes[i].middleware, wrapMiddleware(midhandler))
routes[i].middleware = append(routes[i].middleware, wrapMiddleware(ctx, midhandler))
}
}
return nil
@@ -242,7 +242,10 @@ func wrapRoute(route Route) Middleware {
// we need to pull this particular MiddlewareHandler
// pointer into its own stack frame to preserve it so it
// won't be overwritten in future loop iterations.
func wrapMiddleware(mh MiddlewareHandler) Middleware {
func wrapMiddleware(ctx caddy.Context, mh MiddlewareHandler) Middleware {
// wrap the middleware with metrics instrumentation
metricsHandler := newMetricsInstrumentedHandler(caddy.GetModuleName(mh), mh)
return func(next Handler) Handler {
// copy the next handler (it's an interface, so it's
// just a very lightweight copy of a pointer); this
@@ -253,7 +256,7 @@ func wrapMiddleware(mh MiddlewareHandler) Middleware {
return HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
// TODO: This is where request tracing could be implemented
// TODO: see what the std lib gives us in terms of stack tracing too
return mh.ServeHTTP(w, r, nextCopy)
return metricsHandler.ServeHTTP(w, r, nextCopy)
})
}
}
+2
View File
@@ -122,6 +122,8 @@ type Server struct {
// ⚠️ Experimental feature; subject to change or removal.
AllowH2C bool `json:"allow_h2c,omitempty"`
name string
primaryHandlerChain Handler
errorHandlerChain Handler
listenerWrappers []caddy.ListenerWrapper
+40
View File
@@ -24,6 +24,7 @@ import (
"time"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/certmagic"
"github.com/mholt/acmez"
@@ -112,6 +113,7 @@ func (iss *ACMEIssuer) Provision(ctx caddy.Context) error {
DNSProvider: val.(certmagic.ACMEDNSProvider),
TTL: time.Duration(iss.Challenges.DNS.TTL),
PropagationTimeout: time.Duration(iss.Challenges.DNS.PropagationTimeout),
Resolvers: iss.Challenges.DNS.Resolvers,
}
}
}
@@ -219,6 +221,8 @@ func (iss *ACMEIssuer) GetACMEIssuer() *ACMEIssuer { return iss }
// alt_tlsalpn_port <port>
// eab <key_id> <mac_key>
// trusted_roots <pem_files...>
// dns <provider_name> [<options>]
// resolvers <dns_servers...>
// }
//
func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
@@ -316,6 +320,42 @@ func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
case "trusted_roots":
iss.TrustedRootsPEMFiles = d.RemainingArgs()
case "dns":
if !d.NextArg() {
return d.ArgErr()
}
provName := d.Val()
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.DNS == nil {
iss.Challenges.DNS = new(DNSChallengeConfig)
}
dnsProvModule, err := caddy.GetModule("dns.providers." + provName)
if err != nil {
return d.Errf("getting DNS provider module named '%s': %v", provName, err)
}
dnsProvModuleInstance := dnsProvModule.New()
if unm, ok := dnsProvModuleInstance.(caddyfile.Unmarshaler); ok {
err = unm.UnmarshalCaddyfile(d.NewFromNextSegment())
if err != nil {
return err
}
}
iss.Challenges.DNS.ProviderRaw = caddyconfig.JSONModuleObject(dnsProvModuleInstance, "name", provName, nil)
case "resolvers":
if iss.Challenges == nil {
iss.Challenges = new(ChallengesConfig)
}
if iss.Challenges.DNS == nil {
iss.Challenges.DNS = new(DNSChallengeConfig)
}
iss.Challenges.DNS.Resolvers = d.RemainingArgs()
if len(iss.Challenges.DNS.Resolvers) == 0 {
return d.ArgErr()
}
default:
return d.Errf("unrecognized ACME issuer property: %s", d.Val())
}
+4
View File
@@ -295,6 +295,10 @@ type DNSChallengeConfig struct {
// How long to wait for DNS record to propagate.
PropagationTimeout caddy.Duration `json:"propagation_timeout,omitempty"`
// Custom DNS resolvers to prefer over system/built-in defaults.
// Often necessary to configure when using split-horizon DNS.
Resolvers []string `json:"resolvers,omitempty"`
solver acmez.Solver
}
+11 -32
View File
@@ -27,7 +27,6 @@ import (
"github.com/caddyserver/caddy/v2/modules/caddypki"
"github.com/caddyserver/certmagic"
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/cli/crypto/x509util"
)
func init() {
@@ -120,9 +119,7 @@ func (iss InternalIssuer) Issue(ctx context.Context, csr *x509.CertificateReques
lifetime = issuerCert.NotAfter.Sub(time.Now())
}
certChain, err := auth.Sign(csr, provisioner.Options{},
profileDefaultDuration(iss.Lifetime),
)
certChain, err := auth.Sign(csr, provisioner.SignOptions{}, customCertLifetime(iss.Lifetime))
if err != nil {
return nil, err
}
@@ -160,33 +157,14 @@ func (iss *InternalIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
return nil
}
// profileDefaultDuration is a wrapper against x509util.WithOption to conform
// the SignOption interface.
//
// This type is borrowed from the smallstep libraries:
// https://github.com/smallstep/certificates/blob/806abb6232a5691198b891d76b9898ea7f269da0/authority/provisioner/sign_options.go#L191-L211
// as per https://github.com/smallstep/certificates/issues/198.
//
// TODO: In the future, this approach to custom cert lifetimes may not be necessary
type profileDefaultDuration time.Duration
// customCertLifetime allows us to customize certificates that are issued
// by Smallstep libs, particularly the NotBefore & NotAfter dates.
type customCertLifetime time.Duration
func (d profileDefaultDuration) Option(so provisioner.Options) x509util.WithOption {
var backdate time.Duration
notBefore := so.NotBefore.Time()
if notBefore.IsZero() {
notBefore = time.Now().Truncate(time.Second)
backdate = -1 * so.Backdate
}
notAfter := so.NotAfter.RelativeTime(notBefore)
return func(p x509util.Profile) error {
fn := x509util.WithNotBeforeAfterDuration(notBefore, notAfter, time.Duration(d))
if err := fn(p); err != nil {
return err
}
crt := p.Subject()
crt.NotBefore = crt.NotBefore.Add(backdate)
return nil
}
func (d customCertLifetime) Modify(cert *x509.Certificate, _ provisioner.SignOptions) error {
cert.NotBefore = time.Now()
cert.NotAfter = cert.NotBefore.Add(time.Duration(d))
return nil
}
const (
@@ -195,6 +173,7 @@ const (
// Interface guards
var (
_ caddy.Provisioner = (*InternalIssuer)(nil)
_ certmagic.Issuer = (*InternalIssuer)(nil)
_ caddy.Provisioner = (*InternalIssuer)(nil)
_ certmagic.Issuer = (*InternalIssuer)(nil)
_ provisioner.CertificateModifier = (*customCertLifetime)(nil)
)
+77
View File
@@ -20,6 +20,8 @@ import (
"time"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"go.uber.org/zap"
"go.uber.org/zap/buffer"
"go.uber.org/zap/zapcore"
@@ -94,6 +96,80 @@ func (fe *FilterEncoder) Provision(ctx caddy.Context) error {
return nil
}
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. Syntax:
//
// filter {
// wrap <another encoder>
// fields {
// <field> <filter> {
// <filter options>
// }
// }
// }
func (fe *FilterEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
for d.NextBlock(0) {
switch d.Val() {
case "wrap":
if !d.NextArg() {
return d.ArgErr()
}
moduleName := d.Val()
mod, err := caddy.GetModule("caddy.logging.encoders." + moduleName)
if err != nil {
return d.Errf("getting log encoder module named '%s': %v", moduleName, err)
}
unm, ok := mod.New().(caddyfile.Unmarshaler)
if !ok {
return d.Errf("log encoder module '%s' is not a Caddyfile unmarshaler", mod)
}
err = unm.UnmarshalCaddyfile(d.NewFromNextSegment())
if err != nil {
return err
}
enc, ok := unm.(zapcore.Encoder)
if !ok {
return d.Errf("module %s is not a zapcore.Encoder", mod)
}
fe.WrappedRaw = caddyconfig.JSONModuleObject(enc, "format", moduleName, nil)
case "fields":
for d.NextBlock(1) {
field := d.Val()
if !d.NextArg() {
return d.ArgErr()
}
filterName := d.Val()
mod, err := caddy.GetModule("caddy.logging.encoders.filter." + filterName)
if err != nil {
return d.Errf("getting log filter module named '%s': %v", filterName, err)
}
unm, ok := mod.New().(caddyfile.Unmarshaler)
if !ok {
return d.Errf("log encoder module '%s' is not a Caddyfile unmarshaler", mod)
}
err = unm.UnmarshalCaddyfile(d.NewFromNextSegment())
if err != nil {
return err
}
f, ok := unm.(LogFieldFilter)
if !ok {
return d.Errf("module %s is not a LogFieldFilter", mod)
}
if fe.FieldsRaw == nil {
fe.FieldsRaw = make(map[string]json.RawMessage)
}
fe.FieldsRaw[field] = caddyconfig.JSONModuleObject(f, "filter", filterName, nil)
}
default:
return d.Errf("unrecognized subdirective %s", d.Val())
}
}
}
return nil
}
// AddArray is part of the zapcore.ObjectEncoder interface.
// Array elements do not get filtered.
func (fe FilterEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error {
@@ -330,4 +406,5 @@ func (mom logObjectMarshalerWrapper) MarshalLogObject(_ zapcore.ObjectEncoder) e
var (
_ zapcore.Encoder = (*FilterEncoder)(nil)
_ zapcore.ObjectMarshaler = (*logObjectMarshalerWrapper)(nil)
_ caddyfile.Unmarshaler = (*FilterEncoder)(nil)
)
+79 -9
View File
@@ -16,8 +16,10 @@ package logging
import (
"net"
"strconv"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"go.uber.org/zap/zapcore"
)
@@ -44,6 +46,11 @@ func (DeleteFilter) CaddyModule() caddy.ModuleInfo {
}
}
// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (DeleteFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
return nil
}
// Filter filters the input field.
func (DeleteFilter) Filter(in zapcore.Field) zapcore.Field {
in.Type = zapcore.SkipType
@@ -53,11 +60,14 @@ func (DeleteFilter) Filter(in zapcore.Field) zapcore.Field {
// IPMaskFilter is a Caddy log field filter that
// masks IP addresses.
type IPMaskFilter struct {
// The IPv4 range in CIDR notation.
IPv4CIDR int `json:"ipv4_cidr,omitempty"`
// The IPv4 mask, as an subnet size CIDR.
IPv4MaskRaw int `json:"ipv4_cidr,omitempty"`
// The IPv6 range in CIDR notation.
IPv6CIDR int `json:"ipv6_cidr,omitempty"`
// The IPv6 mask, as an subnet size CIDR.
IPv6MaskRaw int `json:"ipv6_cidr,omitempty"`
v4Mask net.IPMask
v6Mask net.IPMask
}
// CaddyModule returns the Caddy module information.
@@ -68,6 +78,58 @@ func (IPMaskFilter) CaddyModule() caddy.ModuleInfo {
}
}
// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (m *IPMaskFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
for d.NextBlock(0) {
switch d.Val() {
case "ipv4":
if !d.NextArg() {
return d.ArgErr()
}
val, err := strconv.Atoi(d.Val())
if err != nil {
return d.Errf("error parsing %s: %v", d.Val(), err)
}
m.IPv4MaskRaw = val
case "ipv6":
if !d.NextArg() {
return d.ArgErr()
}
val, err := strconv.Atoi(d.Val())
if err != nil {
return d.Errf("error parsing %s: %v", d.Val(), err)
}
m.IPv6MaskRaw = val
default:
return d.Errf("unrecognized subdirective %s", d.Val())
}
}
}
return nil
}
// Provision parses m's IP masks, from integers.
func (m *IPMaskFilter) Provision(ctx caddy.Context) error {
parseRawToMask := func(rawField int, bitLen int) net.IPMask {
if rawField == 0 {
return nil
}
// we assume the int is a subnet size CIDR
// e.g. "16" being equivalent to masking the last
// two bytes of an ipv4 address, like "255.255.0.0"
return net.CIDRMask(rawField, bitLen)
}
m.v4Mask = parseRawToMask(m.IPv4MaskRaw, 32)
m.v6Mask = parseRawToMask(m.IPv6MaskRaw, 128)
return nil
}
// Filter filters the input field.
func (m IPMaskFilter) Filter(in zapcore.Field) zapcore.Field {
host, port, err := net.SplitHostPort(in.String)
@@ -78,13 +140,10 @@ func (m IPMaskFilter) Filter(in zapcore.Field) zapcore.Field {
if ipAddr == nil {
return in
}
bitLen := 32
cidrPrefix := m.IPv4CIDR
mask := m.v4Mask
if ipAddr.To16() != nil {
bitLen = 128
cidrPrefix = m.IPv6CIDR
mask = m.v6Mask
}
mask := net.CIDRMask(cidrPrefix, bitLen)
masked := ipAddr.Mask(mask)
if port == "" {
in.String = masked.String()
@@ -93,3 +152,14 @@ func (m IPMaskFilter) Filter(in zapcore.Field) zapcore.Field {
}
return in
}
// Interface guards
var (
_ LogFieldFilter = (*DeleteFilter)(nil)
_ LogFieldFilter = (*IPMaskFilter)(nil)
_ caddyfile.Unmarshaler = (*DeleteFilter)(nil)
_ caddyfile.Unmarshaler = (*IPMaskFilter)(nil)
_ caddy.Provisioner = (*IPMaskFilter)(nil)
)
+55
View File
@@ -0,0 +1,55 @@
// Copyright 2020 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 metrics
import (
"net/http"
"github.com/caddyserver/caddy/v2"
)
func init() {
caddy.RegisterModule(AdminMetrics{})
}
// AdminMetrics is a module that serves a metrics endpoint so that any gathered
// metrics can be exposed for scraping. This module is not configurable, and
// is permanently mounted to the admin API endpoint at "/metrics".
// See the Metrics module for a configurable endpoint that is usable if the
// Admin API is disabled.
type AdminMetrics struct{}
// CaddyModule returns the Caddy module information.
func (AdminMetrics) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "admin.api.metrics",
New: func() caddy.Module { return new(AdminMetrics) },
}
}
// Routes returns a route for the /metrics endpoint.
func (m *AdminMetrics) Routes() []caddy.AdminRoute {
metricsHandler := createMetricsHandler(nil)
h := caddy.AdminHandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
metricsHandler.ServeHTTP(w, r)
return nil
})
return []caddy.AdminRoute{{Pattern: "/metrics", Handler: h}}
}
// Interface guards
var (
_ caddy.AdminRouter = (*AdminMetrics)(nil)
)
+109
View File
@@ -0,0 +1,109 @@
// Copyright 2020 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 metrics
import (
"net/http"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"go.uber.org/zap"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func init() {
caddy.RegisterModule(Metrics{})
httpcaddyfile.RegisterHandlerDirective("metrics", parseCaddyfile)
}
// Metrics is a module that serves a /metrics endpoint so that any gathered
// metrics can be exposed for scraping. This module is configurable by end-users
// unlike AdminMetrics.
type Metrics struct {
metricsHandler http.Handler
}
// CaddyModule returns the Caddy module information.
func (Metrics) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.handlers.metrics",
New: func() caddy.Module { return new(Metrics) },
}
}
type zapLogger struct {
zl *zap.Logger
}
func (l *zapLogger) Println(v ...interface{}) {
l.zl.Sugar().Error(v...)
}
// Provision sets up m.
func (m *Metrics) Provision(ctx caddy.Context) error {
log := ctx.Logger(m)
m.metricsHandler = createMetricsHandler(&zapLogger{log})
return nil
}
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
var m Metrics
err := m.UnmarshalCaddyfile(h.Dispenser)
return m, err
}
// UnmarshalCaddyfile sets up the handler from Caddyfile tokens. Syntax:
//
// metrics <matcher>
//
func (m *Metrics) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
args := d.RemainingArgs()
if len(args) > 0 {
return d.ArgErr()
}
}
return nil
}
func (m Metrics) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
m.metricsHandler.ServeHTTP(w, r)
return next.ServeHTTP(w, r)
}
// Interface guards
var (
_ caddy.Provisioner = (*Metrics)(nil)
_ caddyhttp.MiddlewareHandler = (*Metrics)(nil)
_ caddyfile.Unmarshaler = (*Metrics)(nil)
)
func createMetricsHandler(logger promhttp.Logger) http.Handler {
return promhttp.InstrumentMetricHandler(prometheus.DefaultRegisterer,
promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{
// will only log errors if logger is non-nil
ErrorLog: logger,
// Allow OpenMetrics format to be negotiated - largely compatible,
// except quantile/le label values always have a decimal.
EnableOpenMetrics: true,
}),
)
}
+1
View File
@@ -11,4 +11,5 @@ import (
_ "github.com/caddyserver/caddy/v2/modules/caddytls/standardstek"
_ "github.com/caddyserver/caddy/v2/modules/filestorage"
_ "github.com/caddyserver/caddy/v2/modules/logging"
_ "github.com/caddyserver/caddy/v2/modules/metrics"
)