mirror of
https://github.com/caddyserver/caddy.git
synced 2026-05-25 16:22:36 -04:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f5592eb4fc | |||
| e1f4b83ffa | |||
| 185ed6fe7c | |||
| 4a0492f3e1 | |||
| 654a3bb090 | |||
| f4840cfeb8 | |||
| a4a64a6f6e | |||
| 88d65967b5 | |||
| 1c4a807667 | |||
| 45132c5b24 | |||
| 1217449609 | |||
| e0bf179c1a | |||
| 7b48ce0e7e | |||
| 924010cd3d | |||
| 74949fb091 | |||
| ddb1d2c2b1 | |||
| 7f227b9d39 | |||
| 0dd0487eba | |||
| db9d167354 | |||
| 29f57faa86 | |||
| 0c01547037 | |||
| e7336cc3bf | |||
| 97a56d860a | |||
| d13258423d | |||
| 32f7dd44ae | |||
| 63d597c09d | |||
| e65b97f55b | |||
| a9768d2fde | |||
| 52822a41cb | |||
| 5b5f8feaf7 | |||
| c93e30454f | |||
| 1bd598e90c | |||
| e698ec5139 | |||
| c27425ef5d | |||
| 434d4bba24 | |||
| 258d906140 | |||
| 69290d232d | |||
| 277472d081 | |||
| 5a4374bea0 | |||
| 0d44e3ecba | |||
| 2a78c9c5e4 | |||
| 01d5568b20 | |||
| 1f4a6fa7e7 | |||
| 5ed8689629 | |||
| 3ae07a73dc | |||
| e473ae6803 | |||
| 72ce78d9af | |||
| 8f8204708a | |||
| 46c5db92da | |||
| de4959fe7b | |||
| 03f703a00e | |||
| ba1d2a8124 | |||
| eba47a3de4 | |||
| a9933aace1 | |||
| 37c6f1c5b6 | |||
| 5610ff9db4 | |||
| f49d2c5b02 | |||
| 5b09e7df3d |
@@ -33,7 +33,7 @@ jobs:
|
||||
GO_SEMVER: '~1.21.0'
|
||||
|
||||
- go: '1.22'
|
||||
GO_SEMVER: '~1.22.0'
|
||||
GO_SEMVER: '~1.22.1'
|
||||
|
||||
# Set some variables per OS, usable via ${{ matrix.VAR }}
|
||||
# OS_LABEL: the VM label from GitHub Actions (see https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories)
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
# Set the minimum Go patch version for the given Go minor
|
||||
# Usable via ${{ matrix.GO_SEMVER }}
|
||||
- go: '1.22'
|
||||
GO_SEMVER: '~1.22.0'
|
||||
GO_SEMVER: '~1.22.1'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
|
||||
@@ -43,11 +43,11 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '~1.22.0'
|
||||
go-version: '~1.22.1'
|
||||
check-latest: true
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
uses: golangci/golangci-lint-action@v4
|
||||
with:
|
||||
version: v1.55
|
||||
|
||||
@@ -66,5 +66,5 @@ jobs:
|
||||
- name: govulncheck
|
||||
uses: golang/govulncheck-action@v1
|
||||
with:
|
||||
go-version-input: '~1.22.0'
|
||||
go-version-input: '~1.22.1'
|
||||
check-latest: true
|
||||
|
||||
@@ -3,6 +3,7 @@ _gitignore/
|
||||
Caddyfile
|
||||
Caddyfile.*
|
||||
!caddyfile/
|
||||
!caddyfile.go
|
||||
|
||||
# artifacts from pprof tooling
|
||||
*.prof
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
</p>
|
||||
|
||||
|
||||
## [Features](https://caddyserver.com/v2)
|
||||
## [Features](https://caddyserver.com/features)
|
||||
|
||||
- **Easy configuration** with the [Caddyfile](https://caddyserver.com/docs/caddyfile)
|
||||
- **Powerful configuration** with its [native JSON config](https://caddyserver.com/docs/json/)
|
||||
@@ -75,7 +75,7 @@
|
||||
- **Runs anywhere** with **no external dependencies** (not even libc)
|
||||
- Written in Go, a language with higher **memory safety guarantees** than other servers
|
||||
- Actually **fun to use**
|
||||
- So much more to [discover](https://caddyserver.com/v2)
|
||||
- So much more to [discover](https://caddyserver.com/features)
|
||||
|
||||
## Install
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"expvar"
|
||||
"fmt"
|
||||
"hash"
|
||||
"hash/fnv"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -41,6 +40,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
"github.com/cespare/xxhash/v2"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
@@ -946,7 +946,7 @@ func (h adminHandler) originAllowed(origin *url.URL) bool {
|
||||
|
||||
// etagHasher returns a the hasher we used on the config to both
|
||||
// produce and verify ETags.
|
||||
func etagHasher() hash.Hash32 { return fnv.New32a() }
|
||||
func etagHasher() hash.Hash { return xxhash.New() }
|
||||
|
||||
// makeEtag returns an Etag header value (including quotes) for
|
||||
// the given config path and hash of contents at that path.
|
||||
@@ -954,17 +954,28 @@ func makeEtag(path string, hash hash.Hash) string {
|
||||
return fmt.Sprintf(`"%s %x"`, path, hash.Sum(nil))
|
||||
}
|
||||
|
||||
// This buffer pool is used to keep buffers for
|
||||
// reading the config file during eTag header generation
|
||||
var bufferPool = sync.Pool{
|
||||
New: func() any {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
|
||||
func handleConfig(w http.ResponseWriter, r *http.Request) error {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
// Set the ETag as a trailer header.
|
||||
// The alternative is to write the config to a buffer, and
|
||||
// then hash that.
|
||||
w.Header().Set("Trailer", "ETag")
|
||||
|
||||
hash := etagHasher()
|
||||
configWriter := io.MultiWriter(w, hash)
|
||||
|
||||
// Read the config into a buffer instead of writing directly to
|
||||
// the response writer, as we want to set the ETag as the header,
|
||||
// not the trailer.
|
||||
buf := bufferPool.Get().(*bytes.Buffer)
|
||||
buf.Reset()
|
||||
defer bufferPool.Put(buf)
|
||||
|
||||
configWriter := io.MultiWriter(buf, hash)
|
||||
err := readConfig(r.URL.Path, configWriter)
|
||||
if err != nil {
|
||||
return APIError{HTTPStatus: http.StatusBadRequest, Err: err}
|
||||
@@ -973,6 +984,10 @@ func handleConfig(w http.ResponseWriter, r *http.Request) error {
|
||||
// we could consider setting up a sync.Pool for the summed
|
||||
// hashes to reduce GC pressure.
|
||||
w.Header().Set("Etag", makeEtag(r.URL.Path, hash))
|
||||
_, err = w.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
return APIError{HTTPStatus: http.StatusInternalServerError, Err: err}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
|
||||
@@ -715,6 +715,7 @@ func exitProcess(ctx context.Context, logger *zap.Logger) {
|
||||
logger.Warn("exiting; byeee!! 👋")
|
||||
|
||||
exitCode := ExitCodeSuccess
|
||||
lastContext := ActiveContext()
|
||||
|
||||
// stop all apps
|
||||
if err := Stop(); err != nil {
|
||||
@@ -736,6 +737,16 @@ func exitProcess(ctx context.Context, logger *zap.Logger) {
|
||||
}
|
||||
}
|
||||
|
||||
// execute any process-exit callbacks
|
||||
for _, exitFunc := range lastContext.exitFuncs {
|
||||
exitFunc(ctx)
|
||||
}
|
||||
exitFuncsMu.Lock()
|
||||
for _, exitFunc := range exitFuncs {
|
||||
exitFunc(ctx)
|
||||
}
|
||||
exitFuncsMu.Unlock()
|
||||
|
||||
// shut down admin endpoint(s) in goroutines so that
|
||||
// if this function was called from an admin handler,
|
||||
// it has a chance to return gracefully
|
||||
@@ -774,6 +785,23 @@ var exiting = new(int32) // accessed atomically
|
||||
// EXPERIMENTAL API: subject to change or removal.
|
||||
func Exiting() bool { return atomic.LoadInt32(exiting) == 1 }
|
||||
|
||||
// OnExit registers a callback to invoke during process exit.
|
||||
// This registration is PROCESS-GLOBAL, meaning that each
|
||||
// function should only be registered once forever, NOT once
|
||||
// per config load (etc).
|
||||
//
|
||||
// EXPERIMENTAL API: subject to change or removal.
|
||||
func OnExit(f func(context.Context)) {
|
||||
exitFuncsMu.Lock()
|
||||
exitFuncs = append(exitFuncs, f)
|
||||
exitFuncsMu.Unlock()
|
||||
}
|
||||
|
||||
var (
|
||||
exitFuncs []func(context.Context)
|
||||
exitFuncsMu sync.Mutex
|
||||
)
|
||||
|
||||
// Duration can be an integer or a string. An integer is
|
||||
// interpreted as nanoseconds. If a string, it is a Go
|
||||
// time.Duration value such as `300ms`, `1.5h`, or `2h45m`;
|
||||
|
||||
@@ -17,9 +17,8 @@ package caddyfile
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"slices"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// Format formats the input Caddyfile to a standard, nice-looking
|
||||
|
||||
@@ -21,18 +21,18 @@ import (
|
||||
type adjacency map[string][]string
|
||||
|
||||
type importGraph struct {
|
||||
nodes map[string]bool
|
||||
nodes map[string]struct{}
|
||||
edges adjacency
|
||||
}
|
||||
|
||||
func (i *importGraph) addNode(name string) {
|
||||
if i.nodes == nil {
|
||||
i.nodes = make(map[string]bool)
|
||||
i.nodes = make(map[string]struct{})
|
||||
}
|
||||
if _, exists := i.nodes[name]; exists {
|
||||
return
|
||||
}
|
||||
i.nodes[name] = true
|
||||
i.nodes[name] = struct{}{}
|
||||
}
|
||||
|
||||
func (i *importGraph) addNodes(names []string) {
|
||||
@@ -66,7 +66,7 @@ func (i *importGraph) addEdge(from, to string) error {
|
||||
}
|
||||
|
||||
if i.nodes == nil {
|
||||
i.nodes = make(map[string]bool)
|
||||
i.nodes = make(map[string]struct{})
|
||||
}
|
||||
if i.edges == nil {
|
||||
i.edges = make(adjacency)
|
||||
|
||||
@@ -50,7 +50,7 @@ func Parse(filename string, input []byte) ([]ServerBlock, error) {
|
||||
p := parser{
|
||||
Dispenser: NewDispenser(tokens),
|
||||
importGraph: importGraph{
|
||||
nodes: make(map[string]bool),
|
||||
nodes: make(map[string]struct{}),
|
||||
edges: make(adjacency),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -49,7 +49,8 @@ func init() {
|
||||
RegisterDirective("handle_errors", parseHandleErrors)
|
||||
RegisterHandlerDirective("invoke", parseInvoke)
|
||||
RegisterDirective("log", parseLog)
|
||||
RegisterHandlerDirective("skip_log", parseSkipLog)
|
||||
RegisterHandlerDirective("skip_log", parseLogSkip)
|
||||
RegisterHandlerDirective("log_skip", parseLogSkip)
|
||||
}
|
||||
|
||||
// parseBind parses the bind directive. Syntax:
|
||||
@@ -1038,13 +1039,19 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
|
||||
return configValues, nil
|
||||
}
|
||||
|
||||
// parseSkipLog parses the skip_log directive. Syntax:
|
||||
// parseLogSkip parses the log_skip directive. Syntax:
|
||||
//
|
||||
// skip_log [<matcher>]
|
||||
func parseSkipLog(h Helper) (caddyhttp.MiddlewareHandler, error) {
|
||||
// log_skip [<matcher>]
|
||||
func parseLogSkip(h Helper) (caddyhttp.MiddlewareHandler, error) {
|
||||
h.Next() // consume directive name
|
||||
|
||||
// "skip_log" is deprecated, replaced by "log_skip"
|
||||
if h.Val() == "skip_log" {
|
||||
caddy.Log().Named("config.adapter.caddyfile").Warn("the 'skip_log' directive is deprecated, please use 'log_skip' instead!")
|
||||
}
|
||||
|
||||
if h.NextArg() {
|
||||
return nil, h.ArgErr()
|
||||
}
|
||||
return caddyhttp.VarsMiddleware{"skip_log": true}, nil
|
||||
return caddyhttp.VarsMiddleware{"log_skip": true}, nil
|
||||
}
|
||||
|
||||
@@ -27,23 +27,32 @@ import (
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
)
|
||||
|
||||
// directiveOrder specifies the order
|
||||
// to apply directives in HTTP routes.
|
||||
// defaultDirectiveOrder specifies the default order
|
||||
// to apply directives in HTTP routes. This must only
|
||||
// consist of directives that are included in Caddy's
|
||||
// standard distribution.
|
||||
//
|
||||
// The root directive goes first in case rewrites or
|
||||
// redirects depend on existence of files, i.e. the
|
||||
// file matcher, which must know the root first.
|
||||
// e.g. The 'root' directive goes near the start in
|
||||
// case rewrites or redirects depend on existence of
|
||||
// files, i.e. the file matcher, which must know the
|
||||
// root first.
|
||||
//
|
||||
// The header directive goes second so that headers
|
||||
// can be manipulated before doing redirects.
|
||||
var directiveOrder = []string{
|
||||
// e.g. The 'header' directive goes before 'redir' so
|
||||
// that headers can be manipulated before doing redirects.
|
||||
//
|
||||
// e.g. The 'respond' directive is near the end because it
|
||||
// writes a response and terminates the middleware chain.
|
||||
var defaultDirectiveOrder = []string{
|
||||
"tracing",
|
||||
|
||||
// set variables that may be used by other directives
|
||||
"map",
|
||||
"vars",
|
||||
"fs",
|
||||
"root",
|
||||
"skip_log",
|
||||
"log_append",
|
||||
"skip_log", // TODO: deprecated, renamed to log_skip
|
||||
"log_skip",
|
||||
|
||||
"header",
|
||||
"copy_response_headers", // only in reverse_proxy's handle_response
|
||||
@@ -84,6 +93,11 @@ var directiveOrder = []string{
|
||||
"acme_server",
|
||||
}
|
||||
|
||||
// directiveOrder specifies the order to apply directives
|
||||
// in HTTP routes, after being modified by either the
|
||||
// plugins or by the user via the "order" global option.
|
||||
var directiveOrder = defaultDirectiveOrder
|
||||
|
||||
// directiveIsOrdered returns true if dir is
|
||||
// a known, ordered (sorted) directive.
|
||||
func directiveIsOrdered(dir string) bool {
|
||||
@@ -130,6 +144,58 @@ func RegisterHandlerDirective(dir string, setupFunc UnmarshalHandlerFunc) {
|
||||
})
|
||||
}
|
||||
|
||||
// RegisterDirectiveOrder registers the default order for a
|
||||
// directive from a plugin.
|
||||
//
|
||||
// This is useful when a plugin has a well-understood place
|
||||
// it should run in the middleware pipeline, and it allows
|
||||
// users to avoid having to define the order themselves.
|
||||
//
|
||||
// The directive dir may be placed in the position relative
|
||||
// to ('before' or 'after') a directive included in Caddy's
|
||||
// standard distribution. It cannot be relative to another
|
||||
// plugin's directive.
|
||||
//
|
||||
// EXPERIMENTAL: This API may change or be removed.
|
||||
func RegisterDirectiveOrder(dir string, position Positional, standardDir string) {
|
||||
// check if directive was already ordered
|
||||
if directiveIsOrdered(dir) {
|
||||
panic("directive '" + dir + "' already ordered")
|
||||
}
|
||||
|
||||
if position != Before && position != After {
|
||||
panic("the 2nd argument must be either 'before' or 'after', got '" + position + "'")
|
||||
}
|
||||
|
||||
// check if directive exists in standard distribution, since
|
||||
// we can't allow plugins to depend on one another; we can't
|
||||
// guarantee the order that plugins are loaded in.
|
||||
foundStandardDir := false
|
||||
for _, d := range defaultDirectiveOrder {
|
||||
if d == standardDir {
|
||||
foundStandardDir = true
|
||||
}
|
||||
}
|
||||
if !foundStandardDir {
|
||||
panic("the 3rd argument '" + standardDir + "' must be a directive that exists in the standard distribution of Caddy")
|
||||
}
|
||||
|
||||
// insert directive into proper position
|
||||
newOrder := directiveOrder
|
||||
for i, d := range newOrder {
|
||||
if d != standardDir {
|
||||
continue
|
||||
}
|
||||
if position == Before {
|
||||
newOrder = append(newOrder[:i], append([]string{dir}, newOrder[i:]...)...)
|
||||
} else if position == After {
|
||||
newOrder = append(newOrder[:i+1], append([]string{dir}, newOrder[i+1:]...)...)
|
||||
}
|
||||
break
|
||||
}
|
||||
directiveOrder = newOrder
|
||||
}
|
||||
|
||||
// RegisterGlobalOption registers a unique global option opt with
|
||||
// an associated unmarshaling (setup) function. When the global
|
||||
// option opt is encountered in a Caddyfile, setupFunc will be
|
||||
@@ -554,6 +620,16 @@ func (sb serverBlock) isAllHTTP() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Positional are the supported modes for ordering directives.
|
||||
type Positional string
|
||||
|
||||
const (
|
||||
Before Positional = "before"
|
||||
After Positional = "after"
|
||||
First Positional = "first"
|
||||
Last Positional = "last"
|
||||
)
|
||||
|
||||
type (
|
||||
// UnmarshalFunc is a function which can unmarshal Caddyfile
|
||||
// tokens into zero or more config values using a Helper type.
|
||||
|
||||
@@ -19,12 +19,12 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
||||
|
||||
@@ -107,7 +107,7 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
|
||||
if !d.Next() {
|
||||
return nil, d.ArgErr()
|
||||
}
|
||||
pos := d.Val()
|
||||
pos := Positional(d.Val())
|
||||
|
||||
newOrder := directiveOrder
|
||||
|
||||
@@ -121,22 +121,22 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
|
||||
|
||||
// act on the positional
|
||||
switch pos {
|
||||
case "first":
|
||||
case First:
|
||||
newOrder = append([]string{dirName}, newOrder...)
|
||||
if d.NextArg() {
|
||||
return nil, d.ArgErr()
|
||||
}
|
||||
directiveOrder = newOrder
|
||||
return newOrder, nil
|
||||
case "last":
|
||||
case Last:
|
||||
newOrder = append(newOrder, dirName)
|
||||
if d.NextArg() {
|
||||
return nil, d.ArgErr()
|
||||
}
|
||||
directiveOrder = newOrder
|
||||
return newOrder, nil
|
||||
case "before":
|
||||
case "after":
|
||||
case Before:
|
||||
case After:
|
||||
default:
|
||||
return nil, d.Errf("unknown positional '%s'", pos)
|
||||
}
|
||||
@@ -153,9 +153,9 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
|
||||
// insert directive into proper position
|
||||
for i, d := range newOrder {
|
||||
if d == otherDir {
|
||||
if pos == "before" {
|
||||
if pos == Before {
|
||||
newOrder = append(newOrder[:i], append([]string{dirName}, newOrder[i:]...)...)
|
||||
} else if pos == "after" {
|
||||
} else if pos == After {
|
||||
newOrder = append(newOrder[:i+1], append([]string{dirName}, newOrder[i+1:]...)...)
|
||||
}
|
||||
break
|
||||
|
||||
@@ -33,7 +33,7 @@ func NewShorthandReplacer() ShorthandReplacer {
|
||||
{regexp.MustCompile(`{path\.([\w-]*)}`), "{http.request.uri.path.$1}"},
|
||||
{regexp.MustCompile(`{file\.([\w-]*)}`), "{http.request.uri.path.file.$1}"},
|
||||
{regexp.MustCompile(`{query\.([\w-]*)}`), "{http.request.uri.query.$1}"},
|
||||
{regexp.MustCompile(`{re\.([\w-]*)\.([\w-]*)}`), "{http.regexp.$1.$2}"},
|
||||
{regexp.MustCompile(`{re\.([\w-\.]*)}`), "{http.regexp.$1}"},
|
||||
{regexp.MustCompile(`{vars\.([\w-]*)}`), "{http.vars.$1}"},
|
||||
{regexp.MustCompile(`{rp\.([\w-\.]*)}`), "{http.reverse_proxy.$1}"},
|
||||
{regexp.MustCompile(`{err\.([\w-\.]*)}`), "{http.error.$1}"},
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
:80 {
|
||||
log
|
||||
|
||||
vars foo foo
|
||||
|
||||
log_append const bar
|
||||
log_append vars foo
|
||||
log_append placeholder {path}
|
||||
|
||||
log_append /only-for-this-path secret value
|
||||
}
|
||||
----------
|
||||
{
|
||||
"apps": {
|
||||
"http": {
|
||||
"servers": {
|
||||
"srv0": {
|
||||
"listen": [
|
||||
":80"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"foo": "foo",
|
||||
"handler": "vars"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"match": [
|
||||
{
|
||||
"path": [
|
||||
"/only-for-this-path"
|
||||
]
|
||||
}
|
||||
],
|
||||
"handle": [
|
||||
{
|
||||
"handler": "log_append",
|
||||
"key": "secret",
|
||||
"value": "value"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"handler": "log_append",
|
||||
"key": "const",
|
||||
"value": "bar"
|
||||
},
|
||||
{
|
||||
"handler": "log_append",
|
||||
"key": "vars",
|
||||
"value": "foo"
|
||||
},
|
||||
{
|
||||
"handler": "log_append",
|
||||
"key": "placeholder",
|
||||
"value": "{http.request.uri.path}"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"logs": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
log {
|
||||
format append {
|
||||
wrap json
|
||||
fields {
|
||||
wrap "foo"
|
||||
}
|
||||
env {env.EXAMPLE}
|
||||
int 1
|
||||
float 1.1
|
||||
bool true
|
||||
string "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:80 {
|
||||
respond "Hello, World!"
|
||||
}
|
||||
----------
|
||||
{
|
||||
"logging": {
|
||||
"logs": {
|
||||
"default": {
|
||||
"encoder": {
|
||||
"fields": {
|
||||
"bool": true,
|
||||
"env": "{env.EXAMPLE}",
|
||||
"float": 1.1,
|
||||
"int": 1,
|
||||
"string": "string",
|
||||
"wrap": "foo"
|
||||
},
|
||||
"format": "append",
|
||||
"wrap": {
|
||||
"format": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"apps": {
|
||||
"http": {
|
||||
"servers": {
|
||||
"srv0": {
|
||||
"listen": [
|
||||
":80"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"body": "Hello, World!",
|
||||
"handler": "static_response"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
http://localhost:2020 {
|
||||
log
|
||||
skip_log /first-hidden*
|
||||
skip_log /second-hidden*
|
||||
log_skip /first-hidden*
|
||||
log_skip /second-hidden*
|
||||
respond 200
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ http://localhost:2020 {
|
||||
"handle": [
|
||||
{
|
||||
"handler": "vars",
|
||||
"skip_log": true
|
||||
"log_skip": true
|
||||
}
|
||||
],
|
||||
"match": [
|
||||
@@ -49,7 +49,7 @@ http://localhost:2020 {
|
||||
"handle": [
|
||||
{
|
||||
"handler": "vars",
|
||||
"skip_log": true
|
||||
"log_skip": true
|
||||
}
|
||||
],
|
||||
"match": [
|
||||
|
||||
@@ -4,27 +4,31 @@ log {
|
||||
output stdout
|
||||
format filter {
|
||||
wrap console
|
||||
|
||||
# long form, with "fields" wrapper
|
||||
fields {
|
||||
uri query {
|
||||
replace foo REDACTED
|
||||
delete bar
|
||||
hash baz
|
||||
}
|
||||
request>headers>Authorization replace REDACTED
|
||||
request>headers>Server delete
|
||||
request>headers>Cookie cookie {
|
||||
replace foo REDACTED
|
||||
delete bar
|
||||
hash baz
|
||||
}
|
||||
request>remote_ip ip_mask {
|
||||
ipv4 24
|
||||
ipv6 32
|
||||
}
|
||||
request>client_ip ip_mask 16 32
|
||||
request>headers>Regexp regexp secret REDACTED
|
||||
request>headers>Hash hash
|
||||
}
|
||||
|
||||
# short form, flatter structure
|
||||
request>headers>Authorization replace REDACTED
|
||||
request>headers>Server delete
|
||||
request>headers>Cookie cookie {
|
||||
replace foo REDACTED
|
||||
delete bar
|
||||
hash baz
|
||||
}
|
||||
request>remote_ip ip_mask {
|
||||
ipv4 24
|
||||
ipv6 32
|
||||
}
|
||||
request>client_ip ip_mask 16 32
|
||||
request>headers>Regexp regexp secret REDACTED
|
||||
request>headers>Hash hash
|
||||
}
|
||||
}
|
||||
----------
|
||||
|
||||
+19
@@ -1,5 +1,9 @@
|
||||
localhost:80
|
||||
|
||||
respond * "{header.content-type} {labels.0} {query.p} {path.0} {re.name.0}"
|
||||
|
||||
@match path_regexp ^/foo(.*)$
|
||||
respond @match "{re.1}"
|
||||
----------
|
||||
{
|
||||
"apps": {
|
||||
@@ -22,6 +26,21 @@ respond * "{header.content-type} {labels.0} {query.p} {path.0} {re.name.0}"
|
||||
{
|
||||
"handler": "subroute",
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"body": "{http.regexp.1}",
|
||||
"handler": "static_response"
|
||||
}
|
||||
],
|
||||
"match": [
|
||||
{
|
||||
"path_regexp": {
|
||||
"pattern": "^/foo(.*)$"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
localhost
|
||||
|
||||
respond "hello from localhost"
|
||||
tls {
|
||||
client_auth {
|
||||
mode request
|
||||
trusted_ca_cert_file ../caddy.ca.cer
|
||||
verifier dummy
|
||||
}
|
||||
}
|
||||
----------
|
||||
{
|
||||
"apps": {
|
||||
"http": {
|
||||
"servers": {
|
||||
"srv0": {
|
||||
"listen": [
|
||||
":443"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"match": [
|
||||
{
|
||||
"host": [
|
||||
"localhost"
|
||||
]
|
||||
}
|
||||
],
|
||||
"handle": [
|
||||
{
|
||||
"handler": "subroute",
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"body": "hello from localhost",
|
||||
"handler": "static_response"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"terminal": true
|
||||
}
|
||||
],
|
||||
"tls_connection_policies": [
|
||||
{
|
||||
"match": {
|
||||
"sni": [
|
||||
"localhost"
|
||||
]
|
||||
},
|
||||
"client_authentication": {
|
||||
"ca": {
|
||||
"provider": "inline",
|
||||
"trusted_ca_certs": [
|
||||
"MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ=="
|
||||
]
|
||||
},
|
||||
"verifiers": [
|
||||
{
|
||||
"verifier": "dummy"
|
||||
}
|
||||
],
|
||||
"mode": "request"
|
||||
}
|
||||
},
|
||||
{}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
:9080
|
||||
uri query +foo bar
|
||||
uri query -baz
|
||||
uri query taz test
|
||||
uri query key=value example
|
||||
uri query changethis>changed
|
||||
uri query {
|
||||
findme value replacement
|
||||
+foo1 baz
|
||||
}
|
||||
|
||||
respond "{query}"
|
||||
----------
|
||||
{
|
||||
"apps": {
|
||||
"http": {
|
||||
"servers": {
|
||||
"srv0": {
|
||||
"listen": [
|
||||
":9080"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"handler": "rewrite",
|
||||
"query": {
|
||||
"add": [
|
||||
{
|
||||
"key": "foo",
|
||||
"val": "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"handler": "rewrite",
|
||||
"query": {
|
||||
"delete": [
|
||||
"baz"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"handler": "rewrite",
|
||||
"query": {
|
||||
"set": [
|
||||
{
|
||||
"key": "taz",
|
||||
"val": "test"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"handler": "rewrite",
|
||||
"query": {
|
||||
"set": [
|
||||
{
|
||||
"key": "key=value",
|
||||
"val": "example"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"handler": "rewrite",
|
||||
"query": {
|
||||
"rename": [
|
||||
{
|
||||
"key": "changethis",
|
||||
"val": "changed"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"handler": "rewrite",
|
||||
"query": {
|
||||
"add": [
|
||||
{
|
||||
"key": "foo1",
|
||||
"val": "baz"
|
||||
}
|
||||
],
|
||||
"replace": [
|
||||
{
|
||||
"key": "findme",
|
||||
"replace": "replacement",
|
||||
"search_regexp": "value"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"body": "{http.request.uri.query}",
|
||||
"handler": "static_response"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/caddyserver/caddy/v2/caddytest"
|
||||
|
||||
_ "github.com/caddyserver/caddy/v2/internal/testmocks"
|
||||
)
|
||||
|
||||
func TestCaddyfileAdaptToJSON(t *testing.T) {
|
||||
|
||||
@@ -497,6 +497,203 @@ func TestUriReplace(t *testing.T) {
|
||||
tester.AssertGetResponse("http://localhost:9080/endpoint?test={%20content%20}", 200, "test=%7B%20content%20%7D")
|
||||
}
|
||||
|
||||
func TestUriOps(t *testing.T) {
|
||||
tester := caddytest.NewTester(t)
|
||||
|
||||
tester.InitServer(`
|
||||
{
|
||||
admin localhost:2999
|
||||
http_port 9080
|
||||
}
|
||||
:9080
|
||||
uri query +foo bar
|
||||
uri query -baz
|
||||
uri query taz test
|
||||
uri query key=value example
|
||||
uri query changethis>changed
|
||||
|
||||
respond "{query}"`, "caddyfile")
|
||||
|
||||
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar0&baz=buz&taz=nottest&changethis=val", 200, "changed=val&foo=bar0&foo=bar&key%3Dvalue=example&taz=test")
|
||||
}
|
||||
|
||||
// Tests the `http.request.local.port` placeholder.
|
||||
// We don't test the very similar `http.request.local.host` placeholder,
|
||||
// because depending on the host the test is running on, localhost might
|
||||
// refer to 127.0.0.1 or ::1.
|
||||
// TODO: Test each http version separately (especially http/3)
|
||||
func TestHttpRequestLocalPortPlaceholder(t *testing.T) {
|
||||
tester := caddytest.NewTester(t)
|
||||
|
||||
tester.InitServer(`
|
||||
{
|
||||
admin localhost:2999
|
||||
http_port 9080
|
||||
}
|
||||
:9080
|
||||
respond "{http.request.local.port}"`, "caddyfile")
|
||||
|
||||
tester.AssertGetResponse("http://localhost:9080/", 200, "9080")
|
||||
}
|
||||
|
||||
func TestSetThenAddQueryParams(t *testing.T) {
|
||||
tester := caddytest.NewTester(t)
|
||||
|
||||
tester.InitServer(`
|
||||
{
|
||||
admin localhost:2999
|
||||
http_port 9080
|
||||
}
|
||||
:9080
|
||||
uri query foo bar
|
||||
uri query +foo baz
|
||||
|
||||
respond "{query}"`, "caddyfile")
|
||||
|
||||
tester.AssertGetResponse("http://localhost:9080/endpoint", 200, "foo=bar&foo=baz")
|
||||
}
|
||||
|
||||
func TestSetThenDeleteParams(t *testing.T) {
|
||||
tester := caddytest.NewTester(t)
|
||||
|
||||
tester.InitServer(`
|
||||
{
|
||||
admin localhost:2999
|
||||
http_port 9080
|
||||
}
|
||||
:9080
|
||||
uri query bar foo{query.foo}
|
||||
uri query -foo
|
||||
|
||||
respond "{query}"`, "caddyfile")
|
||||
|
||||
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "bar=foobar")
|
||||
}
|
||||
|
||||
func TestRenameAndOtherOps(t *testing.T) {
|
||||
tester := caddytest.NewTester(t)
|
||||
|
||||
tester.InitServer(`
|
||||
{
|
||||
admin localhost:2999
|
||||
http_port 9080
|
||||
}
|
||||
:9080
|
||||
uri query foo>bar
|
||||
uri query bar taz
|
||||
uri query +bar baz
|
||||
|
||||
respond "{query}"`, "caddyfile")
|
||||
|
||||
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "bar=taz&bar=baz")
|
||||
}
|
||||
|
||||
func TestReplaceOps(t *testing.T) {
|
||||
tester := caddytest.NewTester(t)
|
||||
|
||||
tester.InitServer(`
|
||||
{
|
||||
admin localhost:2999
|
||||
http_port 9080
|
||||
}
|
||||
:9080
|
||||
uri query foo bar baz
|
||||
respond "{query}"`, "caddyfile")
|
||||
|
||||
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=baz")
|
||||
}
|
||||
|
||||
func TestReplaceWithReplacementPlaceholder(t *testing.T) {
|
||||
tester := caddytest.NewTester(t)
|
||||
tester.InitServer(`
|
||||
{
|
||||
admin localhost:2999
|
||||
http_port 9080
|
||||
}
|
||||
:9080
|
||||
uri query foo bar {query.placeholder}
|
||||
respond "{query}"`, "caddyfile")
|
||||
|
||||
tester.AssertGetResponse("http://localhost:9080/endpoint?placeholder=baz&foo=bar", 200, "foo=baz&placeholder=baz")
|
||||
|
||||
}
|
||||
|
||||
func TestReplaceWithKeyPlaceholder(t *testing.T) {
|
||||
tester := caddytest.NewTester(t)
|
||||
tester.InitServer(`
|
||||
{
|
||||
admin localhost:2999
|
||||
http_port 9080
|
||||
}
|
||||
:9080
|
||||
uri query {query.placeholder} bar baz
|
||||
respond "{query}"`, "caddyfile")
|
||||
|
||||
tester.AssertGetResponse("http://localhost:9080/endpoint?placeholder=foo&foo=bar", 200, "foo=baz&placeholder=foo")
|
||||
}
|
||||
|
||||
func TestPartialReplacement(t *testing.T) {
|
||||
tester := caddytest.NewTester(t)
|
||||
tester.InitServer(`
|
||||
{
|
||||
admin localhost:2999
|
||||
http_port 9080
|
||||
}
|
||||
:9080
|
||||
uri query foo ar az
|
||||
respond "{query}"`, "caddyfile")
|
||||
|
||||
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=baz")
|
||||
}
|
||||
|
||||
func TestNonExistingSearch(t *testing.T) {
|
||||
tester := caddytest.NewTester(t)
|
||||
tester.InitServer(`
|
||||
{
|
||||
admin localhost:2999
|
||||
http_port 9080
|
||||
}
|
||||
:9080
|
||||
uri query foo var baz
|
||||
respond "{query}"`, "caddyfile")
|
||||
|
||||
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=bar")
|
||||
}
|
||||
|
||||
func TestReplaceAllOps(t *testing.T) {
|
||||
tester := caddytest.NewTester(t)
|
||||
|
||||
tester.InitServer(`
|
||||
{
|
||||
admin localhost:2999
|
||||
http_port 9080
|
||||
}
|
||||
:9080
|
||||
uri query * bar baz
|
||||
respond "{query}"`, "caddyfile")
|
||||
|
||||
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar&baz=bar", 200, "baz=baz&foo=baz")
|
||||
}
|
||||
|
||||
func TestUriOpsBlock(t *testing.T) {
|
||||
tester := caddytest.NewTester(t)
|
||||
|
||||
tester.InitServer(`
|
||||
{
|
||||
admin localhost:2999
|
||||
http_port 9080
|
||||
}
|
||||
:9080
|
||||
uri query {
|
||||
+foo bar
|
||||
-baz
|
||||
taz test
|
||||
}
|
||||
respond "{query}"`, "caddyfile")
|
||||
|
||||
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar0&baz=buz&taz=nottest", 200, "foo=bar0&foo=bar&taz=test")
|
||||
}
|
||||
|
||||
func TestHandleErrorSimpleCodes(t *testing.T) {
|
||||
tester := caddytest.NewTester(t)
|
||||
tester.InitServer(`{
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/caddyserver/caddy/v2/caddytest"
|
||||
)
|
||||
|
||||
func TestLeafCertLoaders(t *testing.T) {
|
||||
tester := caddytest.NewTester(t)
|
||||
tester.InitServer(`
|
||||
{
|
||||
"admin": {
|
||||
"listen": "localhost:2999"
|
||||
},
|
||||
"apps": {
|
||||
"http": {
|
||||
"http_port": 9080,
|
||||
"https_port": 9443,
|
||||
"grace_period": 1,
|
||||
"servers": {
|
||||
"srv0": {
|
||||
"listen": [
|
||||
":9443"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"match": [
|
||||
{
|
||||
"host": [
|
||||
"localhost"
|
||||
]
|
||||
}
|
||||
],
|
||||
"terminal": true
|
||||
}
|
||||
],
|
||||
"tls_connection_policies": [
|
||||
{
|
||||
"client_authentication": {
|
||||
"verifiers": [
|
||||
{
|
||||
"verifier": "leaf",
|
||||
"leaf_certs_loaders": [
|
||||
{
|
||||
"loader": "file",
|
||||
"files": ["../leafcert.pem"]
|
||||
},
|
||||
{
|
||||
"loader": "folder",
|
||||
"folders": ["../"]
|
||||
},
|
||||
{
|
||||
"loader": "storage"
|
||||
},
|
||||
{
|
||||
"loader": "pem"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`, "json")
|
||||
}
|
||||
@@ -350,6 +350,8 @@ func TestReverseProxyHealthCheck(t *testing.T) {
|
||||
health_port 2021
|
||||
health_interval 10ms
|
||||
health_timeout 100ms
|
||||
health_passes 1
|
||||
health_fails 1
|
||||
}
|
||||
}
|
||||
`, "caddyfile")
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICUTCCAfugAwIBAgIBADANBgkqhkiG9w0BAQQFADBXMQswCQYDVQQGEwJDTjEL
|
||||
MAkGA1UECBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMC
|
||||
VU4xFDASBgNVBAMTC0hlcm9uZyBZYW5nMB4XDTA1MDcxNTIxMTk0N1oXDTA1MDgx
|
||||
NDIxMTk0N1owVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlBOMQswCQYDVQQHEwJD
|
||||
TjELMAkGA1UEChMCT04xCzAJBgNVBAsTAlVOMRQwEgYDVQQDEwtIZXJvbmcgWWFu
|
||||
ZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCp5hnG7ogBhtlynpOS21cBewKE/B7j
|
||||
V14qeyslnr26xZUsSVko36ZnhiaO/zbMOoRcKK9vEcgMtcLFuQTWDl3RAgMBAAGj
|
||||
gbEwga4wHQYDVR0OBBYEFFXI70krXeQDxZgbaCQoR4jUDncEMH8GA1UdIwR4MHaA
|
||||
FFXI70krXeQDxZgbaCQoR4jUDncEoVukWTBXMQswCQYDVQQGEwJDTjELMAkGA1UE
|
||||
CBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMCVU4xFDAS
|
||||
BgNVBAMTC0hlcm9uZyBZYW5nggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEE
|
||||
BQADQQA/ugzBrjjK9jcWnDVfGHlk3icNRq0oV7Ri32z/+HQX67aRfgZu7KWdI+Ju
|
||||
Wm7DCfrPNGVwFWUQOmsPue9rZBgO
|
||||
-----END CERTIFICATE-----
|
||||
+1
-1
@@ -117,7 +117,7 @@ func onlyVersionText() string {
|
||||
|
||||
func caddyCmdToCobra(caddyCmd Command) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: caddyCmd.Name,
|
||||
Use: caddyCmd.Name + " " + caddyCmd.Usage,
|
||||
Short: caddyCmd.Short,
|
||||
Long: caddyCmd.Long,
|
||||
}
|
||||
|
||||
+37
-19
@@ -17,6 +17,7 @@ package caddycmd
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
@@ -107,6 +108,12 @@ func LoadConfig(configFile, adapterName string) ([]byte, string, error) {
|
||||
}
|
||||
|
||||
func loadConfigWithLogger(logger *zap.Logger, configFile, adapterName string) ([]byte, string, error) {
|
||||
// if no logger is provided, use a nop logger
|
||||
// just so we don't have to check for nil
|
||||
if logger == nil {
|
||||
logger = zap.NewNop()
|
||||
}
|
||||
|
||||
// specifying an adapter without a config file is ambiguous
|
||||
if adapterName != "" && configFile == "" {
|
||||
return nil, "", fmt.Errorf("cannot adapt config without config file (use --config)")
|
||||
@@ -119,16 +126,16 @@ func loadConfigWithLogger(logger *zap.Logger, configFile, adapterName string) ([
|
||||
if configFile != "" {
|
||||
if configFile == "-" {
|
||||
config, err = io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("reading config from stdin: %v", err)
|
||||
}
|
||||
logger.Info("using config from stdin")
|
||||
} else {
|
||||
config, err = os.ReadFile(configFile)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("reading config file: %v", err)
|
||||
}
|
||||
if logger != nil {
|
||||
logger.Info("using provided configuration",
|
||||
zap.String("config_file", configFile),
|
||||
zap.String("config_adapter", adapterName))
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("reading config from file: %v", err)
|
||||
}
|
||||
logger.Info("using config from file", zap.String("file", configFile))
|
||||
}
|
||||
} else if adapterName == "" {
|
||||
// if the Caddyfile adapter is plugged in, we can try using an
|
||||
@@ -145,18 +152,21 @@ func loadConfigWithLogger(logger *zap.Logger, configFile, adapterName string) ([
|
||||
} else {
|
||||
// success reading default Caddyfile
|
||||
configFile = "Caddyfile"
|
||||
if logger != nil {
|
||||
logger.Info("using adjacent Caddyfile")
|
||||
}
|
||||
logger.Info("using adjacent Caddyfile")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// as a special case, if a config file called "Caddyfile" was
|
||||
// specified, and no adapter is specified, assume caddyfile adapter
|
||||
// for convenience
|
||||
if strings.HasPrefix(filepath.Base(configFile), "Caddyfile") &&
|
||||
filepath.Ext(configFile) != ".json" &&
|
||||
// as a special case, if a config file starts with "caddyfile" or
|
||||
// has a ".caddyfile" extension, and no adapter is specified, and
|
||||
// no adapter module name matches the extension, assume
|
||||
// caddyfile adapter for convenience
|
||||
baseConfig := strings.ToLower(filepath.Base(configFile))
|
||||
baseConfigExt := filepath.Ext(baseConfig)
|
||||
if (strings.HasPrefix(baseConfig, "caddyfile") ||
|
||||
strings.HasSuffix(baseConfig, ".caddyfile")) &&
|
||||
(len(baseConfigExt) == 0 || caddyconfig.GetAdapter(baseConfigExt[1:]) == nil) &&
|
||||
baseConfigExt != ".json" &&
|
||||
adapterName == "" {
|
||||
adapterName = "caddyfile"
|
||||
}
|
||||
@@ -177,16 +187,24 @@ func loadConfigWithLogger(logger *zap.Logger, configFile, adapterName string) ([
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("adapting config using %s: %v", adapterName, err)
|
||||
}
|
||||
logger.Info("adapted config to JSON", zap.String("adapter", adapterName))
|
||||
for _, warn := range warnings {
|
||||
msg := warn.Message
|
||||
if warn.Directive != "" {
|
||||
msg = fmt.Sprintf("%s: %s", warn.Directive, warn.Message)
|
||||
}
|
||||
if logger != nil {
|
||||
logger.Warn(msg, zap.String("adapter", adapterName), zap.String("file", warn.File), zap.Int("line", warn.Line))
|
||||
}
|
||||
logger.Warn(msg,
|
||||
zap.String("adapter", adapterName),
|
||||
zap.String("file", warn.File),
|
||||
zap.Int("line", warn.Line))
|
||||
}
|
||||
config = adaptedConfig
|
||||
} else {
|
||||
// validate that the config is at least valid JSON
|
||||
err = json.Unmarshal(config, new(any))
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("config is not valid JSON: %v; did you mean to use a config adapter (the --adapter flag)?", err)
|
||||
}
|
||||
}
|
||||
|
||||
return config, configFile, nil
|
||||
|
||||
+13
-2
@@ -44,8 +44,9 @@ type Context struct {
|
||||
|
||||
moduleInstances map[string][]Module
|
||||
cfg *Config
|
||||
cleanupFuncs []func()
|
||||
ancestry []Module
|
||||
cleanupFuncs []func() // invoked at every config unload
|
||||
exitFuncs []func(context.Context) // invoked at config unload ONLY IF the process is exiting (EXPERIMENTAL)
|
||||
}
|
||||
|
||||
// NewContext provides a new context derived from the given
|
||||
@@ -86,7 +87,8 @@ func (ctx *Context) OnCancel(f func()) {
|
||||
ctx.cleanupFuncs = append(ctx.cleanupFuncs, f)
|
||||
}
|
||||
|
||||
// Filesystems returns a ref to the FilesystemMap
|
||||
// Filesystems returns a ref to the FilesystemMap.
|
||||
// EXPERIMENTAL: This API is subject to change.
|
||||
func (ctx *Context) Filesystems() FileSystems {
|
||||
// if no config is loaded, we use a default filesystemmap, which includes the osfs
|
||||
if ctx.cfg == nil {
|
||||
@@ -95,6 +97,15 @@ func (ctx *Context) Filesystems() FileSystems {
|
||||
return ctx.cfg.filesystems
|
||||
}
|
||||
|
||||
// OnExit executes f when the process exits gracefully.
|
||||
// The function is only executed if the process is gracefully
|
||||
// shut down while this context is active.
|
||||
//
|
||||
// EXPERIMENTAL API: subject to change or removal.
|
||||
func (ctx *Context) OnExit(f func(context.Context)) {
|
||||
ctx.exitFuncs = append(ctx.exitFuncs, f)
|
||||
}
|
||||
|
||||
// LoadModule loads the Caddy module(s) from the specified field of the parent struct
|
||||
// pointer and returns the loaded module(s). The struct pointer and its field name as
|
||||
// a string are necessary so that reflection can be used to read the struct tag on the
|
||||
|
||||
@@ -7,26 +7,26 @@ toolchain go1.21.4
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.3.2
|
||||
github.com/Masterminds/sprig/v3 v3.2.3
|
||||
github.com/alecthomas/chroma/v2 v2.12.1-0.20240220090827-381050ba0001
|
||||
github.com/alecthomas/chroma/v2 v2.13.0
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b
|
||||
github.com/caddyserver/certmagic v0.20.0
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/go-chi/chi/v5 v5.0.10
|
||||
github.com/google/cel-go v0.15.1
|
||||
github.com/google/uuid v1.3.1
|
||||
github.com/go-chi/chi/v5 v5.0.12
|
||||
github.com/google/cel-go v0.20.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/klauspost/compress v1.17.0
|
||||
github.com/klauspost/cpuid/v2 v2.2.5
|
||||
github.com/mholt/acmez v1.2.0
|
||||
github.com/prometheus/client_golang v1.18.0
|
||||
github.com/quic-go/quic-go v0.41.0
|
||||
github.com/smallstep/certificates v0.25.0
|
||||
github.com/prometheus/client_golang v1.19.0
|
||||
github.com/quic-go/quic-go v0.42.0
|
||||
github.com/smallstep/certificates v0.25.3-rc5
|
||||
github.com/smallstep/nosql v0.6.0
|
||||
github.com/smallstep/truststore v0.12.1
|
||||
github.com/smallstep/truststore v0.13.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046
|
||||
github.com/yuin/goldmark v1.5.6
|
||||
github.com/yuin/goldmark v1.7.1
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1
|
||||
go.opentelemetry.io/contrib/propagators/autoprop v0.42.0
|
||||
@@ -34,87 +34,89 @@ require (
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0
|
||||
go.opentelemetry.io/otel/sdk v1.21.0
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
go.uber.org/zap v1.26.0
|
||||
go.uber.org/zap v1.27.0
|
||||
go.uber.org/zap/exp v0.2.0
|
||||
golang.org/x/crypto v0.18.0
|
||||
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611
|
||||
golang.org/x/net v0.19.0
|
||||
golang.org/x/sync v0.5.0
|
||||
golang.org/x/term v0.16.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f
|
||||
golang.org/x/crypto v0.22.0
|
||||
golang.org/x/net v0.24.0
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/term v0.19.0
|
||||
golang.org/x/time v0.5.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/iam v1.1.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.5.0 // indirect
|
||||
github.com/golang/glog v1.1.2 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
github.com/golang/glog v1.2.0 // indirect
|
||||
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 // indirect
|
||||
github.com/google/go-tpm v0.9.0 // indirect
|
||||
github.com/google/go-tspi v0.3.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20231212022811-ec68065c825e // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.13.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/smallstep/go-attestation v0.4.4-0.20230627102604-cf579e53cbd2 // indirect
|
||||
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 // indirect
|
||||
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81 // indirect
|
||||
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/aws v1.17.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.17.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/ot v1.17.0 // indirect
|
||||
go.uber.org/mock v0.3.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.0.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/dgraph-io/badger v1.6.2 // indirect
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.0 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
||||
github.com/dlclark/regexp2 v1.10.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-kit/kit v0.10.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||
github.com/go-logr/logr v1.3.0 // indirect
|
||||
github.com/go-kit/kit v0.13.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/huandu/xstrings v1.3.3 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.14.0 // indirect
|
||||
github.com/jackc/pgconn v1.14.3 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.2 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgtype v1.14.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.18.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.18.3 // indirect
|
||||
github.com/libdns/libdns v0.2.1 // indirect
|
||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/micromdm/scep/v2 v2.1.0 // indirect
|
||||
github.com/miekg/dns v1.1.55 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
@@ -122,7 +124,7 @@ require (
|
||||
github.com/pires/go-proxyproto v0.7.0
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.45.0 // indirect
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/rs/xid v1.5.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
@@ -134,21 +136,19 @@ require (
|
||||
github.com/stoewer/go-strcase v1.2.0 // indirect
|
||||
github.com/urfave/cli v1.22.14 // indirect
|
||||
go.etcd.io/bbolt v1.3.8 // indirect
|
||||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.21.0
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||
go.step.sm/cli-utils v0.8.0 // indirect
|
||||
go.step.sm/crypto v0.35.1
|
||||
go.step.sm/crypto v0.42.1
|
||||
go.step.sm/linkedca v0.20.1 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
golang.org/x/sys v0.16.0
|
||||
golang.org/x/mod v0.16.0 // indirect
|
||||
golang.org/x/sys v0.19.0
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.16.1 // indirect
|
||||
google.golang.org/grpc v1.60.1 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
golang.org/x/tools v0.19.0 // indirect
|
||||
google.golang.org/grpc v1.62.1 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
howett.net/plist v1.0.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o=
|
||||
cloud.google.com/go v0.111.0 h1:YHLKNupSD1KqjDbQ3+LVdQ81h/UJbJyZG203cEfnQgM=
|
||||
cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
|
||||
cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/iam v1.1.2 h1:gacbrBdWcoVmGLozRuStX45YKvJtzIjJdAolzUs1sm4=
|
||||
cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU=
|
||||
cloud.google.com/go/kms v1.15.2 h1:lh6qra6oC4AyWe5fUUUBe/S27k12OHAleOOOw6KakdE=
|
||||
cloud.google.com/go/kms v1.15.2/go.mod h1:3hopT4+7ooWRCjc2DxgnpESFxhIraaI2IpAVUEhbT/w=
|
||||
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
|
||||
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||
cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI=
|
||||
cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8=
|
||||
cloud.google.com/go/kms v1.15.5 h1:pj1sRfut2eRbD9pFRjNnPNg/CzJPuQAzUujMIM1vVeM=
|
||||
cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
@@ -28,53 +25,53 @@ github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2y
|
||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
|
||||
github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
|
||||
github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU=
|
||||
github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
|
||||
github.com/alecthomas/chroma/v2 v2.9.1 h1:0O3lTQh9FxazJ4BYE/MOi/vDGuHn7B+6Bu902N2UZvU=
|
||||
github.com/alecthomas/chroma/v2 v2.9.1/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
|
||||
github.com/alecthomas/chroma/v2 v2.12.1-0.20240220090827-381050ba0001 h1:Nl5Om7AhgtN3tML9kLn2/lr8IDVKxHT2t2+xWc4Q6Fs=
|
||||
github.com/alecthomas/chroma/v2 v2.12.1-0.20240220090827-381050ba0001/go.mod h1:b6DmXsg5hSmn0AcHaTsU+UH0vO73VzhR+JrpFihjsXM=
|
||||
github.com/alecthomas/chroma/v2 v2.13.0 h1:VP72+99Fb2zEcYM0MeaWJmV+xQvz5v5cxRHd+ooU1lI=
|
||||
github.com/alecthomas/chroma/v2 v2.13.0/go.mod h1:BUGjjsD+ndS6eX37YgTchSEG+Jg9Jv1GiZs9sqPqztk=
|
||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
|
||||
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18=
|
||||
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw=
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.46.4 h1:48tKgtm9VMPkb6y7HuYlsfhQmoIRAsTEXTsWLVlty4M=
|
||||
github.com/aws/aws-sdk-go v1.46.4/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.6 h1:Z/7w9bUqlRI0FFQpetVuFYEsjzE3h7fpU6HuGmfPL/o=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.6/go.mod h1:uKU6cnDmYCvJ+pxO9S4cWDb2yWWIH5hra+32hVh1MI4=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 h1:n3GDfwqF2tzEkXlv5cuy4iy7LpKDtqDMcNLfZDu9rls=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.27.9 h1:W9PbZAZAEcelhhjb7KuwUtf+Lbc+i7ByYJRuWLlnxyQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.27.9/go.mod h1:2tFmR7fQnOdQlM2ZCEPpFnBIQD1U8wmXmduBgZbOag0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U=
|
||||
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
|
||||
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
|
||||
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
|
||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@@ -89,21 +86,14 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
|
||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
@@ -119,166 +109,100 @@ github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70d
|
||||
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
|
||||
github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
|
||||
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE=
|
||||
github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
|
||||
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
|
||||
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||
github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo=
|
||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||
github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU=
|
||||
github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
|
||||
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
|
||||
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
|
||||
github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/cel-go v0.15.1 h1:iTgVZor2x9okXtmTrqO8cg4uvqIeaBcWhXtruaWFMYQ=
|
||||
github.com/google/cel-go v0.15.1/go.mod h1:YzWEoI07MC/a/wj9in8GeVatqfypkldgBlwXh9bCwqY=
|
||||
github.com/google/cel-go v0.20.0 h1:h4n6DOCppEMpWERzllyNkntl7JrDyxoE543KWS6BLpc=
|
||||
github.com/google/cel-go v0.20.0/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg=
|
||||
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 h1:heyoXNxkRT155x4jTAiSv5BVSVkueifPUm+Q8LUXMRo=
|
||||
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745/go.mod h1:zN0wUQgV9LjwLZeFHnrAbQi8hzMVvEWePyk+MhPOk7k=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk=
|
||||
github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU=
|
||||
github.com/google/go-tpm-tools v0.4.1 h1:gYU6iwRo0tY3V6NDnS6m+XYog+b3g6YFhHQl3sYaUL4=
|
||||
github.com/google/go-tpm-tools v0.4.1/go.mod h1:w03m0jynhTo7puXTYoyfpNOMqyQ9SB7sixnKWsS/1L0=
|
||||
github.com/google/go-tpm-tools v0.4.2 h1:iyaCPKt2N5Rd0yz0G8ANa022SgCNZkMpp+db6QELtvI=
|
||||
github.com/google/go-tpm-tools v0.4.2/go.mod h1:fGUDZu4tw3V4hUVuFHmiYgRd0c58/IXivn9v3Ea/ck4=
|
||||
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
|
||||
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20231212022811-ec68065c825e h1:bwOy7hAFd0C91URzMIEBfr6BAz29yk7Qj0cy6S7DJlU=
|
||||
github.com/google/pprof v0.0.0-20231212022811-ec68065c825e/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
|
||||
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.4.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk=
|
||||
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
||||
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
|
||||
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
@@ -289,8 +213,8 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU
|
||||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||
github.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q=
|
||||
github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E=
|
||||
github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
|
||||
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
|
||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||
@@ -306,8 +230,8 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0=
|
||||
github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
|
||||
github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
@@ -321,23 +245,12 @@ github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||
github.com/jackc/pgx/v4 v4.18.0 h1:Ltaa1ePvc7msFGALnCrqKJVEByu/qYh5jJBYcDtAno4=
|
||||
github.com/jackc/pgx/v4 v4.18.0/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE=
|
||||
github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA=
|
||||
github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
|
||||
@@ -347,7 +260,6 @@ github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/q
|
||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
@@ -365,135 +277,63 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
|
||||
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
||||
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
|
||||
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
|
||||
github.com/micromdm/scep/v2 v2.1.0 h1:2fS9Rla7qRR266hvUoEauBJ7J6FhgssEiq2OkSKXmaU=
|
||||
github.com/micromdm/scep/v2 v2.1.0/go.mod h1:BkF7TkPPhmgJAMtHfP+sFTKXmgzNJgLQlvvGoOExBcc=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
|
||||
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
||||
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
|
||||
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
|
||||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs=
|
||||
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
||||
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||
github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU=
|
||||
github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o=
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
|
||||
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
|
||||
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
|
||||
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k=
|
||||
github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM=
|
||||
github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
@@ -503,21 +343,16 @@ github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E=
|
||||
github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
@@ -527,18 +362,18 @@ github.com/slackhq/nebula v1.6.1 h1:/OCTR3abj0Sbf2nGoLUrdDXImrCv0ZVFpVPP5qa0DsM=
|
||||
github.com/slackhq/nebula v1.6.1/go.mod h1:UmkqnXe4O53QwToSl/gG7sM4BroQwAB7dd4hUaT6MlI=
|
||||
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY=
|
||||
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc=
|
||||
github.com/smallstep/certificates v0.25.0 h1:WWihtjQ7SprnRxDV44mBp8t5SMsNO5EWsQaEwy1rgFg=
|
||||
github.com/smallstep/certificates v0.25.0/go.mod h1:thJmekMKUplKYip+la99Lk4IwQej/oVH/zS9PVMagEE=
|
||||
github.com/smallstep/go-attestation v0.4.4-0.20230627102604-cf579e53cbd2 h1:UIAS8DTWkeclraEGH2aiJPyNPu16VbT41w4JoBlyFfU=
|
||||
github.com/smallstep/go-attestation v0.4.4-0.20230627102604-cf579e53cbd2/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4=
|
||||
github.com/smallstep/certificates v0.25.3-rc5 h1:a5ALBerbePSIxcDrrzDd4Q4iggfJt8qy1t2WIL/26RU=
|
||||
github.com/smallstep/certificates v0.25.3-rc5/go.mod h1:PI/5pMaKYcnufMK2eVmsHZOS3IAzezYeUIWu7/I2ILs=
|
||||
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 h1:kjYvkvS/Wdy0PVRDUAA0gGJIVSEZYhiAJtfwYgOYoGA=
|
||||
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4=
|
||||
github.com/smallstep/nosql v0.6.0 h1:ur7ysI8s9st0cMXnTvB8tA3+x5Eifmkb6hl4uqNV5jc=
|
||||
github.com/smallstep/nosql v0.6.0/go.mod h1:jOXwLtockXORUPPZ2MCUcIkGR6w0cN1QGZniY9DITQA=
|
||||
github.com/smallstep/truststore v0.12.1 h1:guLUKkc1UlsXeS3t6BuVMa4leOOpdiv02PCRTiy1WdY=
|
||||
github.com/smallstep/truststore v0.12.1/go.mod h1:M4mebeNy28KusGX3lJxpLARIktLcyqBOrj3ZiZ46pqw=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81 h1:B6cED3iLJTgxpdh4tuqByDjRRKan2EvtnOfHr2zHJVg=
|
||||
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81/go.mod h1:SoUAr/4M46rZ3WaLstHxGhLEgoYIDRqxQEXLOmOEB0Y=
|
||||
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d h1:06LUHn4Ia2X6syjIaCMNaXXDNdU+1N/oOHynJbWgpXw=
|
||||
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d/go.mod h1:4d0ub42ut1mMtvGyMensjuHYEUpRrASvkzLEJvoRQcU=
|
||||
github.com/smallstep/truststore v0.13.0 h1:90if9htAOblavbMeWlqNLnO9bsjjgVv2hQeQJCi/py4=
|
||||
github.com/smallstep/truststore v0.13.0/go.mod h1:3tmMp2aLKZ/OA/jnFUB0cYPcho402UG2knuJoPh4j7A=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
@@ -547,21 +382,16 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
|
||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
@@ -575,25 +405,21 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046 h1:8rUlviSVOEe7TMk7W0gIPrW8MqEzYfZHpsNWSf8s2vg=
|
||||
github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046/go.mod h1:kNGUQ3VESx3VZwRwA9MSCUegIl6+saPL8Noq82ozCaU=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
|
||||
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.5.6 h1:COmQAWTCcGetChm3Ig7G/t8AFAN00t+o8Mt4cf7JpwA=
|
||||
github.com/yuin/goldmark v1.5.6/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
|
||||
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
|
||||
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
|
||||
@@ -603,16 +429,8 @@ github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvv
|
||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
|
||||
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.mozilla.org/pkcs7 v0.0.0-20210730143726-725912489c62/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
|
||||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak=
|
||||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE=
|
||||
@@ -645,8 +463,8 @@ go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lI
|
||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||
go.step.sm/cli-utils v0.8.0 h1:b/Tc1/m3YuQq+u3ghTFP7Dz5zUekZj6GUmd5pCvkEXQ=
|
||||
go.step.sm/cli-utils v0.8.0/go.mod h1:S77aISrC0pKuflqiDfxxJlUbiXcAanyJ4POOnzFSxD4=
|
||||
go.step.sm/crypto v0.35.1 h1:QAZZ7Q8xaM4TdungGSAYw/zxpyH4fMYTkfaXVV9H7pY=
|
||||
go.step.sm/crypto v0.35.1/go.mod h1:vn8Vkx/Mbqgoe7AG8btC0qZ995Udm3e+JySuDS1LCJA=
|
||||
go.step.sm/crypto v0.42.1 h1:OmwHm3GJO8S4VGWL3k4+I+Q4P/F2s+j8msvTyGnh1Vg=
|
||||
go.step.sm/crypto v0.42.1/go.mod h1:yNcTLFQBnYCA75fC5bklBoTAT7y0dRZsB1TkinB8JMs=
|
||||
go.step.sm/linkedca v0.20.1 h1:bHDn1+UG1NgRrERkWbbCiAIvv4lD5NOFaswPDTyO5vU=
|
||||
go.step.sm/linkedca v0.20.1/go.mod h1:Vaq4+Umtjh7DLFI1KuIxeo598vfBzgSYZUjgVJ7Syxw=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
@@ -657,8 +475,8 @@ go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
|
||||
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
@@ -668,17 +486,14 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.uber.org/zap/exp v0.2.0 h1:FtGenNNeCATRB3CmB/yEUnjEFeJWpB/pMcy7e2bKPYs=
|
||||
go.uber.org/zap/exp v0.2.0/go.mod h1:t0gqAIdh1MfKv9EwN/dLwfZnJxe9ITAZN78HEWPFWDQ=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@@ -687,66 +502,37 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 h1:qCEDpW1G+vcj3Y7Fy52pEM1AWm3abj8WimGYejI3SC4=
|
||||
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
|
||||
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20170726083632-f5079bd7f6f7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
||||
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
|
||||
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
|
||||
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -754,13 +540,9 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -773,15 +555,20 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -790,22 +577,14 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@@ -813,69 +592,38 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
|
||||
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
|
||||
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/api v0.154.0 h1:X7QkVKZBskztmpPKWQXgjJRPA2dJYrL6r+sYPRLj050=
|
||||
google.golang.org/api v0.154.0/go.mod h1:qhSMkM85hgqiokIYsrRyKxrjfBeIhgl4Z2JmeRkYylc=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/api v0.157.0 h1:ORAeqmbrrozeyw5NjnMxh7peHO0UzV4wWYSwZeCUb20=
|
||||
google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f h1:Vn+VyHU5guc9KjB5KrjI2q0wCOWEOIh0OEsleqakHJg=
|
||||
google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f/go.mod h1:nWSwAFPb+qfNJXsoeO3Io7zf4tMSfN8EA8RlDA04GhY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f h1:2yNACc1O40tTnrsbk9Cv6oxiW8pxI/pXj0wRtdlYmgY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 h1:DC7wcm+i+P1rN3Ff07vL+OndGg5OhNddHyTA+ocPqYE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
|
||||
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 h1:rIo7ocm2roD9DcFIX67Ym8icoGCKSARAiPljFhh5suQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c h1:lfpJ/2rWPa/kJgxyyXM8PrNnfCzcmxJ265mADgwmvLI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
@@ -883,11 +631,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
|
||||
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package testmocks
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddytls"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterModule(new(dummyVerifier))
|
||||
}
|
||||
|
||||
type dummyVerifier struct{}
|
||||
|
||||
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
|
||||
func (dummyVerifier) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaddyModule implements caddy.Module.
|
||||
func (dummyVerifier) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: "tls.client_auth.verifier.dummy",
|
||||
New: func() caddy.Module {
|
||||
return new(dummyVerifier)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// VerifyClientCertificate implements ClientCertificateVerifier.
|
||||
func (dummyVerifier) VerifyClientCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
_ caddy.Module = dummyVerifier{}
|
||||
_ caddytls.ClientCertificateVerifier = dummyVerifier{}
|
||||
_ caddyfile.Unmarshaler = dummyVerifier{}
|
||||
)
|
||||
+26
-114
@@ -28,11 +28,11 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/time/rate"
|
||||
|
||||
"github.com/caddyserver/caddy/v2/internal"
|
||||
)
|
||||
@@ -406,48 +406,12 @@ func JoinNetworkAddress(network, host, port string) string {
|
||||
return a
|
||||
}
|
||||
|
||||
// DEPRECATED: Use NetworkAddress.Listen instead. This function will likely be changed or removed in the future.
|
||||
func Listen(network, addr string) (net.Listener, error) {
|
||||
// a 0 timeout means Go uses its default
|
||||
return ListenTimeout(network, addr, 0)
|
||||
}
|
||||
|
||||
// DEPRECATED: Use NetworkAddress.Listen instead. This function will likely be changed or removed in the future.
|
||||
func ListenTimeout(network, addr string, keepalivePeriod time.Duration) (net.Listener, error) {
|
||||
netAddr, err := ParseNetworkAddress(JoinNetworkAddress(network, addr, ""))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ln, err := netAddr.Listen(context.TODO(), 0, net.ListenConfig{KeepAlive: keepalivePeriod})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ln.(net.Listener), nil
|
||||
}
|
||||
|
||||
// DEPRECATED: Use NetworkAddress.Listen instead. This function will likely be changed or removed in the future.
|
||||
func ListenPacket(network, addr string) (net.PacketConn, error) {
|
||||
netAddr, err := ParseNetworkAddress(JoinNetworkAddress(network, addr, ""))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ln, err := netAddr.Listen(context.TODO(), 0, net.ListenConfig{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ln.(net.PacketConn), nil
|
||||
}
|
||||
|
||||
// ListenQUIC returns a quic.EarlyListener suitable for use in a Caddy module.
|
||||
// The network will be transformed into a QUIC-compatible type (if unix, then
|
||||
// unixgram will be used; otherwise, udp will be used).
|
||||
//
|
||||
// NOTE: This API is EXPERIMENTAL and may be changed or removed.
|
||||
func (na NetworkAddress) ListenQUIC(ctx context.Context, portOffset uint, config net.ListenConfig, tlsConf *tls.Config, activeRequests *int64) (http3.QUICEarlyListener, error) {
|
||||
func (na NetworkAddress) ListenQUIC(ctx context.Context, portOffset uint, config net.ListenConfig, tlsConf *tls.Config) (http3.QUICEarlyListener, error) {
|
||||
lnKey := listenerKey("quic"+na.Network, na.JoinHostPort(portOffset))
|
||||
|
||||
sharedEarlyListener, _, err := listenerPool.LoadOrNew(lnKey, func() (Destructor, error) {
|
||||
@@ -468,20 +432,22 @@ func (na NetworkAddress) ListenQUIC(ctx context.Context, portOffset uint, config
|
||||
}
|
||||
}
|
||||
|
||||
sqs := newSharedQUICState(tlsConf, activeRequests)
|
||||
sqs := newSharedQUICState(tlsConf)
|
||||
// http3.ConfigureTLSConfig only uses this field and tls App sets this field as well
|
||||
//nolint:gosec
|
||||
quicTlsConfig := &tls.Config{GetConfigForClient: sqs.getConfigForClient}
|
||||
earlyLn, err := quic.ListenEarly(h3ln, http3.ConfigureTLSConfig(quicTlsConfig), &quic.Config{
|
||||
Allow0RTT: true,
|
||||
RequireAddressValidation: func(clientAddr net.Addr) bool {
|
||||
// TODO: make tunable?
|
||||
return sqs.getActiveRequests() > 1000
|
||||
},
|
||||
})
|
||||
// Require clients to verify their source address when we're handling more than 1000 handshakes per second.
|
||||
// TODO: make tunable?
|
||||
limiter := rate.NewLimiter(1000, 1000)
|
||||
tr := &quic.Transport{
|
||||
Conn: h3ln,
|
||||
VerifySourceAddress: func(addr net.Addr) bool { return !limiter.Allow() },
|
||||
}
|
||||
earlyLn, err := tr.ListenEarly(http3.ConfigureTLSConfig(quicTlsConfig), &quic.Config{Allow0RTT: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: figure out when to close the listener and the transport
|
||||
// using the original net.PacketConn to close them properly
|
||||
return &sharedQuicListener{EarlyListener: earlyLn, packetConn: ln, sqs: sqs, key: lnKey}, nil
|
||||
})
|
||||
@@ -490,47 +456,8 @@ func (na NetworkAddress) ListenQUIC(ctx context.Context, portOffset uint, config
|
||||
}
|
||||
|
||||
sql := sharedEarlyListener.(*sharedQuicListener)
|
||||
// add current tls.Config to sqs, so GetConfigForClient will always return the latest tls.Config in case of context cancellation,
|
||||
// and the request counter will reflect current http server
|
||||
ctx, cancel := sql.sqs.addState(tlsConf, activeRequests)
|
||||
|
||||
return &fakeCloseQuicListener{
|
||||
sharedQuicListener: sql,
|
||||
context: ctx,
|
||||
contextCancel: cancel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DEPRECATED: Use NetworkAddress.ListenQUIC instead. This function will likely be changed or removed in the future.
|
||||
// TODO: See if we can find a more elegant solution closer to the new NetworkAddress.Listen API.
|
||||
func ListenQUIC(ln net.PacketConn, tlsConf *tls.Config, activeRequests *int64) (http3.QUICEarlyListener, error) {
|
||||
lnKey := listenerKey("quic+"+ln.LocalAddr().Network(), ln.LocalAddr().String())
|
||||
|
||||
sharedEarlyListener, _, err := listenerPool.LoadOrNew(lnKey, func() (Destructor, error) {
|
||||
sqs := newSharedQUICState(tlsConf, activeRequests)
|
||||
// http3.ConfigureTLSConfig only uses this field and tls App sets this field as well
|
||||
//nolint:gosec
|
||||
quicTlsConfig := &tls.Config{GetConfigForClient: sqs.getConfigForClient}
|
||||
earlyLn, err := quic.ListenEarly(ln, http3.ConfigureTLSConfig(quicTlsConfig), &quic.Config{
|
||||
Allow0RTT: true,
|
||||
RequireAddressValidation: func(clientAddr net.Addr) bool {
|
||||
// TODO: make tunable?
|
||||
return sqs.getActiveRequests() > 1000
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &sharedQuicListener{EarlyListener: earlyLn, sqs: sqs, key: lnKey}, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sql := sharedEarlyListener.(*sharedQuicListener)
|
||||
// add current tls.Config and request counter to sqs, so GetConfigForClient will always return the latest tls.Config in case of context cancellation,
|
||||
// and the request counter will reflect current http server
|
||||
ctx, cancel := sql.sqs.addState(tlsConf, activeRequests)
|
||||
// add current tls.Config to sqs, so GetConfigForClient will always return the latest tls.Config in case of context cancellation
|
||||
ctx, cancel := sql.sqs.addState(tlsConf)
|
||||
|
||||
return &fakeCloseQuicListener{
|
||||
sharedQuicListener: sql,
|
||||
@@ -551,25 +478,21 @@ type contextAndCancelFunc struct {
|
||||
context.CancelFunc
|
||||
}
|
||||
|
||||
// sharedQUICState manages GetConfigForClient and current number of active requests
|
||||
// sharedQUICState manages GetConfigForClient
|
||||
// see issue: https://github.com/caddyserver/caddy/pull/4849
|
||||
type sharedQUICState struct {
|
||||
rmu sync.RWMutex
|
||||
tlsConfs map[*tls.Config]contextAndCancelFunc
|
||||
requestCounters map[*tls.Config]*int64
|
||||
activeTlsConf *tls.Config
|
||||
activeRequestsCounter *int64
|
||||
rmu sync.RWMutex
|
||||
tlsConfs map[*tls.Config]contextAndCancelFunc
|
||||
activeTlsConf *tls.Config
|
||||
}
|
||||
|
||||
// newSharedQUICState creates a new sharedQUICState
|
||||
func newSharedQUICState(tlsConfig *tls.Config, activeRequests *int64) *sharedQUICState {
|
||||
func newSharedQUICState(tlsConfig *tls.Config) *sharedQUICState {
|
||||
sqtc := &sharedQUICState{
|
||||
tlsConfs: make(map[*tls.Config]contextAndCancelFunc),
|
||||
requestCounters: make(map[*tls.Config]*int64),
|
||||
activeTlsConf: tlsConfig,
|
||||
activeRequestsCounter: activeRequests,
|
||||
tlsConfs: make(map[*tls.Config]contextAndCancelFunc),
|
||||
activeTlsConf: tlsConfig,
|
||||
}
|
||||
sqtc.addState(tlsConfig, activeRequests)
|
||||
sqtc.addState(tlsConfig)
|
||||
return sqtc
|
||||
}
|
||||
|
||||
@@ -580,17 +503,9 @@ func (sqs *sharedQUICState) getConfigForClient(ch *tls.ClientHelloInfo) (*tls.Co
|
||||
return sqs.activeTlsConf.GetConfigForClient(ch)
|
||||
}
|
||||
|
||||
// getActiveRequests returns the number of active requests
|
||||
func (sqs *sharedQUICState) getActiveRequests() int64 {
|
||||
// Prevent a race when a context is cancelled and active request counter is being changed
|
||||
sqs.rmu.RLock()
|
||||
defer sqs.rmu.RUnlock()
|
||||
return atomic.LoadInt64(sqs.activeRequestsCounter)
|
||||
}
|
||||
|
||||
// addState adds tls.Config and activeRequests to the map if not present and returns the corresponding context and its cancelFunc
|
||||
// so that when cancelled, the active tls.Config and request counter will change
|
||||
func (sqs *sharedQUICState) addState(tlsConfig *tls.Config, activeRequests *int64) (context.Context, context.CancelFunc) {
|
||||
// so that when cancelled, the active tls.Config will change
|
||||
func (sqs *sharedQUICState) addState(tlsConfig *tls.Config) (context.Context, context.CancelFunc) {
|
||||
sqs.rmu.Lock()
|
||||
defer sqs.rmu.Unlock()
|
||||
|
||||
@@ -606,19 +521,16 @@ func (sqs *sharedQUICState) addState(tlsConfig *tls.Config, activeRequests *int6
|
||||
defer sqs.rmu.Unlock()
|
||||
|
||||
delete(sqs.tlsConfs, tlsConfig)
|
||||
delete(sqs.requestCounters, tlsConfig)
|
||||
if sqs.activeTlsConf == tlsConfig {
|
||||
// select another tls.Config and request counter, if there is none,
|
||||
// select another tls.Config, if there is none,
|
||||
// related sharedQuicListener will be destroyed anyway
|
||||
for tc, counter := range sqs.requestCounters {
|
||||
for tc := range sqs.tlsConfs {
|
||||
sqs.activeTlsConf = tc
|
||||
sqs.activeRequestsCounter = counter
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
sqs.tlsConfs[tlsConfig] = contextAndCancelFunc{ctx, wrappedCancel}
|
||||
sqs.requestCounters[tlsConfig] = activeRequests
|
||||
// there should be at most 2 tls.Configs
|
||||
if len(sqs.tlsConfs) > 2 {
|
||||
Log().Warn("quic listener tls configs are more than 2", zap.Int("number of configs", len(sqs.tlsConfs)))
|
||||
|
||||
@@ -68,6 +68,9 @@ func init() {
|
||||
// `{http.request.orig_uri.query}` | The request's original query string (without `?`)
|
||||
// `{http.request.port}` | The port part of the request's Host header
|
||||
// `{http.request.proto}` | The protocol of the request
|
||||
// `{http.request.local.host}` | The host (IP) part of the local address the connection arrived on
|
||||
// `{http.request.local.port}` | The port part of the local address the connection arrived on
|
||||
// `{http.request.local}` | The local address the connection arrived on
|
||||
// `{http.request.remote.host}` | The host (IP) part of the remote client's address
|
||||
// `{http.request.remote.port}` | The port part of the remote client's address
|
||||
// `{http.request.remote}` | The address of the remote client
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
|
||||
"github.com/google/cel-go/cel"
|
||||
"github.com/google/cel-go/common"
|
||||
"github.com/google/cel-go/common/ast"
|
||||
"github.com/google/cel-go/common/operators"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
@@ -36,7 +37,6 @@ import (
|
||||
"github.com/google/cel-go/interpreter/functions"
|
||||
"github.com/google/cel-go/parser"
|
||||
"go.uber.org/zap"
|
||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
@@ -66,7 +66,7 @@ type MatchExpression struct {
|
||||
|
||||
expandedExpr string
|
||||
prg cel.Program
|
||||
ta ref.TypeAdapter
|
||||
ta types.Adapter
|
||||
|
||||
log *zap.Logger
|
||||
}
|
||||
@@ -227,7 +227,7 @@ func (m MatchExpression) caddyPlaceholderFunc(lhs, rhs ref.Val) ref.Val {
|
||||
}
|
||||
|
||||
// httpRequestCELType is the type representation of a native HTTP request.
|
||||
var httpRequestCELType = types.NewTypeValue("http.Request", traits.ReceiverType)
|
||||
var httpRequestCELType = cel.ObjectType("http.Request", traits.ReceiverType)
|
||||
|
||||
// celHTTPRequest wraps an http.Request with ref.Val interface methods.
|
||||
//
|
||||
@@ -263,7 +263,7 @@ func (cr celHTTPRequest) Equal(other ref.Val) ref.Val {
|
||||
func (celHTTPRequest) Type() ref.Type { return httpRequestCELType }
|
||||
func (cr celHTTPRequest) Value() any { return cr }
|
||||
|
||||
var pkixNameCELType = types.NewTypeValue("pkix.Name", traits.ReceiverType)
|
||||
var pkixNameCELType = cel.ObjectType("pkix.Name", traits.ReceiverType)
|
||||
|
||||
// celPkixName wraps an pkix.Name with
|
||||
// methods to satisfy the ref.Val interface.
|
||||
@@ -381,24 +381,24 @@ func CELMatcherImpl(macroName, funcName string, matcherDataTypes []*cel.Type, fa
|
||||
type CELMatcherFactory func(data ref.Val) (RequestMatcher, error)
|
||||
|
||||
// matcherCELLibrary is a simplistic configurable cel.Library implementation.
|
||||
type matcherCELLibary struct {
|
||||
type matcherCELLibrary struct {
|
||||
envOptions []cel.EnvOption
|
||||
programOptions []cel.ProgramOption
|
||||
}
|
||||
|
||||
// NewMatcherCELLibrary creates a matcherLibrary from option setes.
|
||||
func NewMatcherCELLibrary(envOptions []cel.EnvOption, programOptions []cel.ProgramOption) cel.Library {
|
||||
return &matcherCELLibary{
|
||||
return &matcherCELLibrary{
|
||||
envOptions: envOptions,
|
||||
programOptions: programOptions,
|
||||
}
|
||||
}
|
||||
|
||||
func (lib *matcherCELLibary) CompileOptions() []cel.EnvOption {
|
||||
func (lib *matcherCELLibrary) CompileOptions() []cel.EnvOption {
|
||||
return lib.envOptions
|
||||
}
|
||||
|
||||
func (lib *matcherCELLibary) ProgramOptions() []cel.ProgramOption {
|
||||
func (lib *matcherCELLibrary) ProgramOptions() []cel.ProgramOption {
|
||||
return lib.programOptions
|
||||
}
|
||||
|
||||
@@ -472,25 +472,20 @@ func CELMatcherRuntimeFunction(funcName string, fac CELMatcherFactory) functions
|
||||
//
|
||||
// The arguments are collected into a single list argument the following
|
||||
// function call returned: <funcName>(request, [args])
|
||||
func celMatcherStringListMacroExpander(funcName string) parser.MacroExpander {
|
||||
return func(eh parser.ExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
|
||||
matchArgs := []*exprpb.Expr{}
|
||||
func celMatcherStringListMacroExpander(funcName string) cel.MacroFactory {
|
||||
return func(eh cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
|
||||
matchArgs := []ast.Expr{}
|
||||
if len(args) == 0 {
|
||||
return nil, &common.Error{
|
||||
Message: "matcher requires at least one argument",
|
||||
}
|
||||
return nil, eh.NewError(0, "matcher requires at least one argument")
|
||||
}
|
||||
for _, arg := range args {
|
||||
if isCELStringExpr(arg) {
|
||||
matchArgs = append(matchArgs, arg)
|
||||
} else {
|
||||
return nil, &common.Error{
|
||||
Location: eh.OffsetLocation(arg.GetId()),
|
||||
Message: "matcher arguments must be string constants",
|
||||
}
|
||||
return nil, eh.NewError(arg.ID(), "matcher arguments must be string constants")
|
||||
}
|
||||
}
|
||||
return eh.GlobalCall(funcName, eh.Ident("request"), eh.NewList(matchArgs...)), nil
|
||||
return eh.NewCall(funcName, eh.NewIdent("request"), eh.NewList(matchArgs...)), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,19 +494,14 @@ func celMatcherStringListMacroExpander(funcName string) parser.MacroExpander {
|
||||
//
|
||||
// The following function call is returned: <funcName>(request, arg)
|
||||
func celMatcherStringMacroExpander(funcName string) parser.MacroExpander {
|
||||
return func(eh parser.ExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
|
||||
return func(eh cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
|
||||
if len(args) != 1 {
|
||||
return nil, &common.Error{
|
||||
Message: "matcher requires one argument",
|
||||
}
|
||||
return nil, eh.NewError(0, "matcher requires one argument")
|
||||
}
|
||||
if isCELStringExpr(args[0]) {
|
||||
return eh.GlobalCall(funcName, eh.Ident("request"), args[0]), nil
|
||||
}
|
||||
return nil, &common.Error{
|
||||
Location: eh.OffsetLocation(args[0].GetId()),
|
||||
Message: "matcher argument must be a string literal",
|
||||
return eh.NewCall(funcName, eh.NewIdent("request"), args[0]), nil
|
||||
}
|
||||
return nil, eh.NewError(args[0].ID(), "matcher argument must be a string literal")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,49 +510,35 @@ func celMatcherStringMacroExpander(funcName string) parser.MacroExpander {
|
||||
//
|
||||
// The following function call is returned: <funcName>(request, arg)
|
||||
func celMatcherJSONMacroExpander(funcName string) parser.MacroExpander {
|
||||
return func(eh parser.ExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
|
||||
return func(eh cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
|
||||
if len(args) != 1 {
|
||||
return nil, &common.Error{
|
||||
Message: "matcher requires a map literal argument",
|
||||
}
|
||||
return nil, eh.NewError(0, "matcher requires a map literal argument")
|
||||
}
|
||||
arg := args[0]
|
||||
switch arg.GetExprKind().(type) {
|
||||
case *exprpb.Expr_StructExpr:
|
||||
structExpr := arg.GetStructExpr()
|
||||
if structExpr.GetMessageName() != "" {
|
||||
return nil, &common.Error{
|
||||
Location: eh.OffsetLocation(arg.GetId()),
|
||||
Message: fmt.Sprintf(
|
||||
"matcher input must be a map literal, not a %s",
|
||||
structExpr.GetMessageName(),
|
||||
),
|
||||
}
|
||||
}
|
||||
for _, entry := range structExpr.GetEntries() {
|
||||
isStringPlaceholder := isCELStringExpr(entry.GetMapKey())
|
||||
|
||||
switch arg.Kind() {
|
||||
case ast.StructKind:
|
||||
return nil, eh.NewError(arg.ID(),
|
||||
fmt.Sprintf("matcher input must be a map literal, not a %s", arg.AsStruct().TypeName()))
|
||||
case ast.MapKind:
|
||||
mapExpr := arg.AsMap()
|
||||
for _, entry := range mapExpr.Entries() {
|
||||
isStringPlaceholder := isCELStringExpr(entry.AsMapEntry().Key())
|
||||
if !isStringPlaceholder {
|
||||
return nil, &common.Error{
|
||||
Location: eh.OffsetLocation(entry.GetId()),
|
||||
Message: "matcher map keys must be string literals",
|
||||
}
|
||||
return nil, eh.NewError(entry.ID(), "matcher map keys must be string literals")
|
||||
}
|
||||
isStringListPlaceholder := isCELStringExpr(entry.GetValue()) ||
|
||||
isCELStringListLiteral(entry.GetValue())
|
||||
isStringListPlaceholder := isCELStringExpr(entry.AsMapEntry().Value()) ||
|
||||
isCELStringListLiteral(entry.AsMapEntry().Value())
|
||||
if !isStringListPlaceholder {
|
||||
return nil, &common.Error{
|
||||
Location: eh.OffsetLocation(entry.GetValue().GetId()),
|
||||
Message: "matcher map values must be string or list literals",
|
||||
}
|
||||
return nil, eh.NewError(entry.AsMapEntry().Value().ID(), "matcher map values must be string or list literals")
|
||||
}
|
||||
}
|
||||
return eh.GlobalCall(funcName, eh.Ident("request"), arg), nil
|
||||
return eh.NewCall(funcName, eh.NewIdent("request"), arg), nil
|
||||
case ast.UnspecifiedExprKind, ast.CallKind, ast.ComprehensionKind, ast.IdentKind, ast.ListKind, ast.LiteralKind, ast.SelectKind:
|
||||
// appeasing the linter :)
|
||||
}
|
||||
|
||||
return nil, &common.Error{
|
||||
Location: eh.OffsetLocation(arg.GetId()),
|
||||
Message: "matcher requires a map literal argument",
|
||||
}
|
||||
return nil, eh.NewError(arg.ID(), "matcher requires a map literal argument")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,69 +583,77 @@ func CELValueToMapStrList(data ref.Val) (map[string][]string, error) {
|
||||
}
|
||||
|
||||
// isCELStringExpr indicates whether the expression is a supported string expression
|
||||
func isCELStringExpr(e *exprpb.Expr) bool {
|
||||
func isCELStringExpr(e ast.Expr) bool {
|
||||
return isCELStringLiteral(e) || isCELCaddyPlaceholderCall(e) || isCELConcatCall(e)
|
||||
}
|
||||
|
||||
// isCELStringLiteral returns whether the expression is a CEL string literal.
|
||||
func isCELStringLiteral(e *exprpb.Expr) bool {
|
||||
switch e.GetExprKind().(type) {
|
||||
case *exprpb.Expr_ConstExpr:
|
||||
constant := e.GetConstExpr()
|
||||
switch constant.GetConstantKind().(type) {
|
||||
case *exprpb.Constant_StringValue:
|
||||
func isCELStringLiteral(e ast.Expr) bool {
|
||||
switch e.Kind() {
|
||||
case ast.LiteralKind:
|
||||
constant := e.AsLiteral()
|
||||
switch constant.Type() {
|
||||
case types.StringType:
|
||||
return true
|
||||
}
|
||||
case ast.UnspecifiedExprKind, ast.CallKind, ast.ComprehensionKind, ast.IdentKind, ast.ListKind, ast.MapKind, ast.SelectKind, ast.StructKind:
|
||||
// appeasing the linter :)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isCELCaddyPlaceholderCall returns whether the expression is a caddy placeholder call.
|
||||
func isCELCaddyPlaceholderCall(e *exprpb.Expr) bool {
|
||||
switch e.GetExprKind().(type) {
|
||||
case *exprpb.Expr_CallExpr:
|
||||
call := e.GetCallExpr()
|
||||
if call.GetFunction() == "caddyPlaceholder" {
|
||||
func isCELCaddyPlaceholderCall(e ast.Expr) bool {
|
||||
switch e.Kind() {
|
||||
case ast.CallKind:
|
||||
call := e.AsCall()
|
||||
if call.FunctionName() == "caddyPlaceholder" {
|
||||
return true
|
||||
}
|
||||
case ast.UnspecifiedExprKind, ast.ComprehensionKind, ast.IdentKind, ast.ListKind, ast.LiteralKind, ast.MapKind, ast.SelectKind, ast.StructKind:
|
||||
// appeasing the linter :)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isCELConcatCall tests whether the expression is a concat function (+) with string, placeholder, or
|
||||
// other concat call arguments.
|
||||
func isCELConcatCall(e *exprpb.Expr) bool {
|
||||
switch e.GetExprKind().(type) {
|
||||
case *exprpb.Expr_CallExpr:
|
||||
call := e.GetCallExpr()
|
||||
if call.GetTarget() != nil {
|
||||
func isCELConcatCall(e ast.Expr) bool {
|
||||
switch e.Kind() {
|
||||
case ast.CallKind:
|
||||
call := e.AsCall()
|
||||
if call.Target().Kind() != ast.UnspecifiedExprKind {
|
||||
return false
|
||||
}
|
||||
if call.GetFunction() != operators.Add {
|
||||
if call.FunctionName() != operators.Add {
|
||||
return false
|
||||
}
|
||||
for _, arg := range call.GetArgs() {
|
||||
for _, arg := range call.Args() {
|
||||
if !isCELStringExpr(arg) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case ast.UnspecifiedExprKind, ast.ComprehensionKind, ast.IdentKind, ast.ListKind, ast.LiteralKind, ast.MapKind, ast.SelectKind, ast.StructKind:
|
||||
// appeasing the linter :)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isCELStringListLiteral returns whether the expression resolves to a list literal
|
||||
// containing only string constants or a placeholder call.
|
||||
func isCELStringListLiteral(e *exprpb.Expr) bool {
|
||||
switch e.GetExprKind().(type) {
|
||||
case *exprpb.Expr_ListExpr:
|
||||
list := e.GetListExpr()
|
||||
for _, elem := range list.GetElements() {
|
||||
func isCELStringListLiteral(e ast.Expr) bool {
|
||||
switch e.Kind() {
|
||||
case ast.ListKind:
|
||||
list := e.AsList()
|
||||
for _, elem := range list.Elements() {
|
||||
if !isCELStringExpr(elem) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case ast.UnspecifiedExprKind, ast.CallKind, ast.ComprehensionKind, ast.IdentKind, ast.LiteralKind, ast.MapKind, ast.SelectKind, ast.StructKind:
|
||||
// appeasing the linter :)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -20,11 +20,9 @@
|
||||
package encode
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -221,6 +219,12 @@ type responseWriter struct {
|
||||
// to actually write the header.
|
||||
func (rw *responseWriter) WriteHeader(status int) {
|
||||
rw.statusCode = status
|
||||
|
||||
// write status immediately when status code is informational
|
||||
// see: https://caddy.community/t/disappear-103-early-hints-response-with-encode-enable-caddy-v2-7-6/23081/5
|
||||
if 100 <= status && status <= 199 {
|
||||
rw.ResponseWriter.WriteHeader(status)
|
||||
}
|
||||
}
|
||||
|
||||
// Match determines, if encoding should be done based on the ResponseMatcher.
|
||||
@@ -228,31 +232,18 @@ func (enc *Encode) Match(rw *responseWriter) bool {
|
||||
return enc.Matcher.Match(rw.statusCode, rw.Header())
|
||||
}
|
||||
|
||||
// Flush implements http.Flusher. It delays the actual Flush of the underlying ResponseWriterWrapper
|
||||
// until headers were written.
|
||||
func (rw *responseWriter) Flush() {
|
||||
// FlushError is an alternative Flush returning an error. It delays the actual Flush of the underlying
|
||||
// ResponseWriterWrapper until headers were written.
|
||||
func (rw *responseWriter) FlushError() error {
|
||||
if !rw.wroteHeader {
|
||||
// flushing the underlying ResponseWriter will write header and status code,
|
||||
// but we need to delay that until we can determine if we must encode and
|
||||
// therefore add the Content-Encoding header; this happens in the first call
|
||||
// to rw.Write (see bug in #4314)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
//nolint:bodyclose
|
||||
http.NewResponseController(rw.ResponseWriter).Flush()
|
||||
}
|
||||
|
||||
// Hijack implements http.Hijacker. It will flush status code if set. We don't track actual hijacked
|
||||
// status assuming http middlewares will track its status.
|
||||
func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
if !rw.wroteHeader {
|
||||
if rw.statusCode != 0 {
|
||||
rw.ResponseWriter.WriteHeader(rw.statusCode)
|
||||
}
|
||||
rw.wroteHeader = true
|
||||
}
|
||||
//nolint:bodyclose
|
||||
return http.NewResponseController(rw.ResponseWriter).Hijack()
|
||||
return http.NewResponseController(rw.ResponseWriter).Flush()
|
||||
}
|
||||
|
||||
// Write writes to the response. If the response qualifies,
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
|
||||
"go.uber.org/zap"
|
||||
@@ -111,13 +112,42 @@ func (fsrv *FileServer) serveBrowse(fileSystem fs.FS, root, dirPath string, w ht
|
||||
|
||||
acceptHeader := strings.ToLower(strings.Join(r.Header["Accept"], ","))
|
||||
|
||||
// write response as either JSON or HTML
|
||||
if strings.Contains(acceptHeader, "application/json") {
|
||||
switch {
|
||||
case strings.Contains(acceptHeader, "application/json"):
|
||||
if err := json.NewEncoder(buf).Encode(listing.Items); err != nil {
|
||||
return caddyhttp.Error(http.StatusInternalServerError, err)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
} else {
|
||||
|
||||
case strings.Contains(acceptHeader, "text/plain"):
|
||||
writer := tabwriter.NewWriter(buf, 0, 8, 1, '\t', tabwriter.AlignRight)
|
||||
|
||||
// Header on top
|
||||
if _, err := fmt.Fprintln(writer, "Name\tSize\tModified"); err != nil {
|
||||
return caddyhttp.Error(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
// Lines to separate the header
|
||||
if _, err := fmt.Fprintln(writer, "----\t----\t--------"); err != nil {
|
||||
return caddyhttp.Error(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
// Actual files
|
||||
for _, item := range listing.Items {
|
||||
if _, err := fmt.Fprintf(writer, "%s\t%s\t%s\n",
|
||||
item.Name, item.HumanSize(), item.HumanModTime("January 2, 2006 at 15:04:05"),
|
||||
); err != nil {
|
||||
return caddyhttp.Error(http.StatusInternalServerError, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := writer.Flush(); err != nil {
|
||||
return caddyhttp.Error(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
|
||||
default:
|
||||
var fs http.FileSystem
|
||||
if fsrv.Root != "" {
|
||||
fs = http.Dir(repl.ReplaceAll(fsrv.Root, "."))
|
||||
|
||||
@@ -113,13 +113,15 @@ func (fsrv *FileServer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
fsrv.Browse = new(Browse)
|
||||
d.Args(&fsrv.Browse.TemplateFile)
|
||||
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||
if d.Val() != "reveal_symlinks" {
|
||||
switch d.Val() {
|
||||
case "reveal_symlinks":
|
||||
if fsrv.Browse.RevealSymlinks {
|
||||
return d.Err("Symlinks path reveal is already enabled")
|
||||
}
|
||||
fsrv.Browse.RevealSymlinks = true
|
||||
default:
|
||||
return d.Errf("unknown subdirective '%s'", d.Val())
|
||||
}
|
||||
if fsrv.Browse.RevealSymlinks {
|
||||
return d.Err("Symlinks path reveal is already enabled")
|
||||
}
|
||||
fsrv.Browse.RevealSymlinks = true
|
||||
}
|
||||
|
||||
case "precompressed":
|
||||
|
||||
@@ -27,11 +27,12 @@ import (
|
||||
|
||||
"github.com/google/cel-go/cel"
|
||||
"github.com/google/cel-go/common"
|
||||
"github.com/google/cel-go/common/ast"
|
||||
"github.com/google/cel-go/common/operators"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/parser"
|
||||
"go.uber.org/zap"
|
||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
@@ -220,30 +221,30 @@ func (MatchFile) CELLibrary(ctx caddy.Context) (cel.Library, error) {
|
||||
}
|
||||
|
||||
func celFileMatcherMacroExpander() parser.MacroExpander {
|
||||
return func(eh parser.ExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
|
||||
return func(eh parser.ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
|
||||
if len(args) == 0 {
|
||||
return eh.GlobalCall("file",
|
||||
eh.Ident("request"),
|
||||
return eh.NewCall("file",
|
||||
eh.NewIdent("request"),
|
||||
eh.NewMap(),
|
||||
), nil
|
||||
}
|
||||
if len(args) == 1 {
|
||||
arg := args[0]
|
||||
if isCELStringLiteral(arg) || isCELCaddyPlaceholderCall(arg) {
|
||||
return eh.GlobalCall("file",
|
||||
eh.Ident("request"),
|
||||
return eh.NewCall("file",
|
||||
eh.NewIdent("request"),
|
||||
eh.NewMap(eh.NewMapEntry(
|
||||
eh.LiteralString("try_files"),
|
||||
eh.NewLiteral(types.String("try_files")),
|
||||
eh.NewList(arg),
|
||||
false,
|
||||
)),
|
||||
), nil
|
||||
}
|
||||
if isCELTryFilesLiteral(arg) {
|
||||
return eh.GlobalCall("file", eh.Ident("request"), arg), nil
|
||||
return eh.NewCall("file", eh.NewIdent("request"), arg), nil
|
||||
}
|
||||
return nil, &common.Error{
|
||||
Location: eh.OffsetLocation(arg.GetId()),
|
||||
Location: eh.OffsetLocation(arg.ID()),
|
||||
Message: "matcher requires either a map or string literal argument",
|
||||
}
|
||||
}
|
||||
@@ -251,15 +252,15 @@ func celFileMatcherMacroExpander() parser.MacroExpander {
|
||||
for _, arg := range args {
|
||||
if !(isCELStringLiteral(arg) || isCELCaddyPlaceholderCall(arg)) {
|
||||
return nil, &common.Error{
|
||||
Location: eh.OffsetLocation(arg.GetId()),
|
||||
Location: eh.OffsetLocation(arg.ID()),
|
||||
Message: "matcher only supports repeated string literal arguments",
|
||||
}
|
||||
}
|
||||
}
|
||||
return eh.GlobalCall("file",
|
||||
eh.Ident("request"),
|
||||
return eh.NewCall("file",
|
||||
eh.NewIdent("request"),
|
||||
eh.NewMap(eh.NewMapEntry(
|
||||
eh.LiteralString("try_files"),
|
||||
eh.NewLiteral(types.String("try_files")),
|
||||
eh.NewList(args...),
|
||||
false,
|
||||
)),
|
||||
@@ -569,20 +570,17 @@ func indexFold(haystack, needle string) int {
|
||||
|
||||
// isCELTryFilesLiteral returns whether the expression resolves to a map literal containing
|
||||
// only string keys with or a placeholder call.
|
||||
func isCELTryFilesLiteral(e *exprpb.Expr) bool {
|
||||
switch e.GetExprKind().(type) {
|
||||
case *exprpb.Expr_StructExpr:
|
||||
structExpr := e.GetStructExpr()
|
||||
if structExpr.GetMessageName() != "" {
|
||||
return false
|
||||
}
|
||||
for _, entry := range structExpr.GetEntries() {
|
||||
mapKey := entry.GetMapKey()
|
||||
mapVal := entry.GetValue()
|
||||
func isCELTryFilesLiteral(e ast.Expr) bool {
|
||||
switch e.Kind() {
|
||||
case ast.MapKind:
|
||||
mapExpr := e.AsMap()
|
||||
for _, entry := range mapExpr.Entries() {
|
||||
mapKey := entry.AsMapEntry().Key()
|
||||
mapVal := entry.AsMapEntry().Value()
|
||||
if !isCELStringLiteral(mapKey) {
|
||||
return false
|
||||
}
|
||||
mapKeyStr := mapKey.GetConstExpr().GetStringValue()
|
||||
mapKeyStr := mapKey.AsLiteral().ConvertToType(types.StringType).Value()
|
||||
if mapKeyStr == "try_files" || mapKeyStr == "split_path" {
|
||||
if !isCELStringListLiteral(mapVal) {
|
||||
return false
|
||||
@@ -596,74 +594,85 @@ func isCELTryFilesLiteral(e *exprpb.Expr) bool {
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
case ast.UnspecifiedExprKind, ast.CallKind, ast.ComprehensionKind, ast.IdentKind, ast.ListKind, ast.LiteralKind, ast.SelectKind, ast.StructKind:
|
||||
// appeasing the linter :)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isCELStringExpr indicates whether the expression is a supported string expression
|
||||
func isCELStringExpr(e *exprpb.Expr) bool {
|
||||
func isCELStringExpr(e ast.Expr) bool {
|
||||
return isCELStringLiteral(e) || isCELCaddyPlaceholderCall(e) || isCELConcatCall(e)
|
||||
}
|
||||
|
||||
// isCELStringLiteral returns whether the expression is a CEL string literal.
|
||||
func isCELStringLiteral(e *exprpb.Expr) bool {
|
||||
switch e.GetExprKind().(type) {
|
||||
case *exprpb.Expr_ConstExpr:
|
||||
constant := e.GetConstExpr()
|
||||
switch constant.GetConstantKind().(type) {
|
||||
case *exprpb.Constant_StringValue:
|
||||
func isCELStringLiteral(e ast.Expr) bool {
|
||||
switch e.Kind() {
|
||||
case ast.LiteralKind:
|
||||
constant := e.AsLiteral()
|
||||
switch constant.Type() {
|
||||
case types.StringType:
|
||||
return true
|
||||
}
|
||||
case ast.UnspecifiedExprKind, ast.CallKind, ast.ComprehensionKind, ast.IdentKind, ast.ListKind, ast.MapKind, ast.SelectKind, ast.StructKind:
|
||||
// appeasing the linter :)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isCELCaddyPlaceholderCall returns whether the expression is a caddy placeholder call.
|
||||
func isCELCaddyPlaceholderCall(e *exprpb.Expr) bool {
|
||||
switch e.GetExprKind().(type) {
|
||||
case *exprpb.Expr_CallExpr:
|
||||
call := e.GetCallExpr()
|
||||
if call.GetFunction() == "caddyPlaceholder" {
|
||||
func isCELCaddyPlaceholderCall(e ast.Expr) bool {
|
||||
switch e.Kind() {
|
||||
case ast.CallKind:
|
||||
call := e.AsCall()
|
||||
if call.FunctionName() == "caddyPlaceholder" {
|
||||
return true
|
||||
}
|
||||
case ast.UnspecifiedExprKind, ast.ComprehensionKind, ast.IdentKind, ast.ListKind, ast.LiteralKind, ast.MapKind, ast.SelectKind, ast.StructKind:
|
||||
// appeasing the linter :)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isCELConcatCall tests whether the expression is a concat function (+) with string, placeholder, or
|
||||
// other concat call arguments.
|
||||
func isCELConcatCall(e *exprpb.Expr) bool {
|
||||
switch e.GetExprKind().(type) {
|
||||
case *exprpb.Expr_CallExpr:
|
||||
call := e.GetCallExpr()
|
||||
if call.GetTarget() != nil {
|
||||
func isCELConcatCall(e ast.Expr) bool {
|
||||
switch e.Kind() {
|
||||
case ast.CallKind:
|
||||
call := e.AsCall()
|
||||
if call.Target().Kind() != ast.UnspecifiedExprKind {
|
||||
return false
|
||||
}
|
||||
if call.GetFunction() != operators.Add {
|
||||
if call.FunctionName() != operators.Add {
|
||||
return false
|
||||
}
|
||||
for _, arg := range call.GetArgs() {
|
||||
for _, arg := range call.Args() {
|
||||
if !isCELStringExpr(arg) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case ast.UnspecifiedExprKind, ast.ComprehensionKind, ast.IdentKind, ast.ListKind, ast.LiteralKind, ast.MapKind, ast.SelectKind, ast.StructKind:
|
||||
// appeasing the linter :)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isCELStringListLiteral returns whether the expression resolves to a list literal
|
||||
// containing only string constants or a placeholder call.
|
||||
func isCELStringListLiteral(e *exprpb.Expr) bool {
|
||||
switch e.GetExprKind().(type) {
|
||||
case *exprpb.Expr_ListExpr:
|
||||
list := e.GetListExpr()
|
||||
for _, elem := range list.GetElements() {
|
||||
func isCELStringListLiteral(e ast.Expr) bool {
|
||||
switch e.Kind() {
|
||||
case ast.ListKind:
|
||||
list := e.AsList()
|
||||
for _, elem := range list.Elements() {
|
||||
if !isCELStringExpr(elem) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case ast.UnspecifiedExprKind, ast.CallKind, ast.ComprehensionKind, ast.IdentKind, ast.LiteralKind, ast.MapKind, ast.SelectKind, ast.StructKind:
|
||||
// appeasing the linter :)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -639,12 +639,18 @@ func calculateEtag(d os.FileInfo) string {
|
||||
return `"` + t + s + `"`
|
||||
}
|
||||
|
||||
func redirect(w http.ResponseWriter, r *http.Request, to string) error {
|
||||
for strings.HasPrefix(to, "//") {
|
||||
// redirect performs a redirect to a given path. The 'toPath' parameter
|
||||
// MUST be solely a path, and MUST NOT include a query.
|
||||
func redirect(w http.ResponseWriter, r *http.Request, toPath string) error {
|
||||
for strings.HasPrefix(toPath, "//") {
|
||||
// prevent path-based open redirects
|
||||
to = strings.TrimPrefix(to, "/")
|
||||
toPath = strings.TrimPrefix(toPath, "/")
|
||||
}
|
||||
http.Redirect(w, r, to, http.StatusPermanentRedirect)
|
||||
// preserve the query string if present
|
||||
if r.URL.RawQuery != "" {
|
||||
toPath += "?" + r.URL.RawQuery
|
||||
}
|
||||
http.Redirect(w, r, toPath, http.StatusPermanentRedirect)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -68,12 +68,14 @@ func parseCaddyfile(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
|
||||
if h.NextArg() {
|
||||
hasArgs = true
|
||||
field := h.Val()
|
||||
var value, replacement string
|
||||
var value string
|
||||
var replacement *string
|
||||
if h.NextArg() {
|
||||
value = h.Val()
|
||||
}
|
||||
if h.NextArg() {
|
||||
replacement = h.Val()
|
||||
arg := h.Val()
|
||||
replacement = &arg
|
||||
}
|
||||
err := applyHeaderOp(
|
||||
handler.Response.HeaderOps,
|
||||
@@ -106,12 +108,14 @@ func parseCaddyfile(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
|
||||
// https://caddy.community/t/v2-reverse-proxy-please-add-cors-example-to-the-docs/7349/19
|
||||
field = strings.TrimSuffix(field, ":")
|
||||
|
||||
var value, replacement string
|
||||
var value string
|
||||
var replacement *string
|
||||
if h.NextArg() {
|
||||
value = h.Val()
|
||||
}
|
||||
if h.NextArg() {
|
||||
replacement = h.Val()
|
||||
arg := h.Val()
|
||||
replacement = &arg
|
||||
}
|
||||
|
||||
handlerToUse := handler
|
||||
@@ -170,12 +174,14 @@ func parseReqHdrCaddyfile(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue,
|
||||
// https://caddy.community/t/v2-reverse-proxy-please-add-cors-example-to-the-docs/7349/19
|
||||
field = strings.TrimSuffix(field, ":")
|
||||
|
||||
var value, replacement string
|
||||
var value string
|
||||
var replacement *string
|
||||
if h.NextArg() {
|
||||
value = h.Val()
|
||||
}
|
||||
if h.NextArg() {
|
||||
replacement = h.Val()
|
||||
arg := h.Val()
|
||||
replacement = &arg
|
||||
if h.NextArg() {
|
||||
return nil, h.ArgErr()
|
||||
}
|
||||
@@ -200,15 +206,15 @@ func parseReqHdrCaddyfile(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue,
|
||||
// field, value, and replacement. The field can be prefixed with
|
||||
// "+" or "-" to specify adding or removing; otherwise, the value
|
||||
// will be set (overriding any previous value). If replacement is
|
||||
// non-empty, value will be treated as a regular expression which
|
||||
// non-nil, value will be treated as a regular expression which
|
||||
// will be used to search and then replacement will be used to
|
||||
// complete the substring replacement; in that case, any + or -
|
||||
// prefix to field will be ignored.
|
||||
func CaddyfileHeaderOp(ops *HeaderOps, field, value, replacement string) error {
|
||||
func CaddyfileHeaderOp(ops *HeaderOps, field, value string, replacement *string) error {
|
||||
return applyHeaderOp(ops, nil, field, value, replacement)
|
||||
}
|
||||
|
||||
func applyHeaderOp(ops *HeaderOps, respHeaderOps *RespHeaderOps, field, value, replacement string) error {
|
||||
func applyHeaderOp(ops *HeaderOps, respHeaderOps *RespHeaderOps, field, value string, replacement *string) error {
|
||||
switch {
|
||||
case strings.HasPrefix(field, "+"): // append
|
||||
if ops.Add == nil {
|
||||
@@ -238,7 +244,7 @@ func applyHeaderOp(ops *HeaderOps, respHeaderOps *RespHeaderOps, field, value, r
|
||||
}
|
||||
respHeaderOps.Set.Set(field, value)
|
||||
|
||||
case replacement != "": // replace
|
||||
case replacement != nil: // replace
|
||||
// allow defer shortcut for replace syntax
|
||||
if strings.HasPrefix(field, ">") && respHeaderOps != nil {
|
||||
respHeaderOps.Deferred = true
|
||||
@@ -251,7 +257,7 @@ func applyHeaderOp(ops *HeaderOps, respHeaderOps *RespHeaderOps, field, value, r
|
||||
ops.Replace[field],
|
||||
Replacement{
|
||||
SearchRegexp: value,
|
||||
Replace: replacement,
|
||||
Replace: *replacement,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ func (e *ExtraLogFields) Set(field zap.Field) {
|
||||
const (
|
||||
// Variable name used to indicate that this request
|
||||
// should be omitted from the access logs
|
||||
SkipLogVar string = "skip_log"
|
||||
LogSkipVar string = "log_skip"
|
||||
|
||||
// For adding additional fields to the access logs
|
||||
ExtraLogFieldsCtxKey caddy.CtxKey = "extra_log_fields"
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2015 Matthew Holt and The Caddy Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package logging
|
||||
|
||||
import (
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
httpcaddyfile.RegisterHandlerDirective("log_append", parseCaddyfile)
|
||||
}
|
||||
|
||||
// parseCaddyfile sets up the log_append handler from Caddyfile tokens. Syntax:
|
||||
//
|
||||
// log_append [<matcher>] <key> <value>
|
||||
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
|
||||
handler := new(LogAppend)
|
||||
err := handler.UnmarshalCaddyfile(h.Dispenser)
|
||||
return handler, err
|
||||
}
|
||||
|
||||
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
|
||||
func (h *LogAppend) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
d.Next() // consume directive name
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
h.Key = d.Val()
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
h.Value = d.Val()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Interface guards
|
||||
var (
|
||||
_ caddyfile.Unmarshaler = (*LogAppend)(nil)
|
||||
)
|
||||
@@ -0,0 +1,94 @@
|
||||
// Copyright 2015 Matthew Holt and The Caddy Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package logging
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterModule(LogAppend{})
|
||||
}
|
||||
|
||||
// LogAppend implements a middleware that takes a key and value, where
|
||||
// the key is the name of a log field and the value is a placeholder,
|
||||
// or variable key, or constant value to use for that field.
|
||||
type LogAppend struct {
|
||||
// Key is the name of the log field.
|
||||
Key string `json:"key,omitempty"`
|
||||
|
||||
// Value is the value to use for the log field.
|
||||
// If it is a placeholder (with surrounding `{}`),
|
||||
// it will be evaluated when the log is written.
|
||||
// If the value is a key that exists in the `vars`
|
||||
// map, the value of that key will be used. Otherwise
|
||||
// the value will be used as-is as a constant string.
|
||||
Value string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
func (LogAppend) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: "http.handlers.log_append",
|
||||
New: func() caddy.Module { return new(LogAppend) },
|
||||
}
|
||||
}
|
||||
|
||||
func (h LogAppend) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
|
||||
// Run the next handler in the chain first.
|
||||
// If an error occurs, we still want to add
|
||||
// any extra log fields that we can, so we
|
||||
// hold onto the error and return it later.
|
||||
handlerErr := next.ServeHTTP(w, r)
|
||||
|
||||
// On the way back up the chain, add the extra log field
|
||||
ctx := r.Context()
|
||||
|
||||
vars := ctx.Value(caddyhttp.VarsCtxKey).(map[string]any)
|
||||
repl := ctx.Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||
extra := ctx.Value(caddyhttp.ExtraLogFieldsCtxKey).(*caddyhttp.ExtraLogFields)
|
||||
|
||||
var varValue any
|
||||
if strings.HasPrefix(h.Value, "{") &&
|
||||
strings.HasSuffix(h.Value, "}") &&
|
||||
strings.Count(h.Value, "{") == 1 {
|
||||
// the value looks like a placeholder, so get its value
|
||||
varValue, _ = repl.Get(strings.Trim(h.Value, "{}"))
|
||||
} else if val, ok := vars[h.Value]; ok {
|
||||
// the value is a key in the vars map
|
||||
varValue = val
|
||||
} else {
|
||||
// the value is a constant string
|
||||
varValue = h.Value
|
||||
}
|
||||
|
||||
// Add the field to the extra log fields.
|
||||
// We use zap.Any because it will reflect
|
||||
// to the correct type for us.
|
||||
extra.Add(zap.Any(h.Key, varValue))
|
||||
|
||||
return handlerErr
|
||||
}
|
||||
|
||||
// Interface guards
|
||||
var (
|
||||
_ caddyhttp.MiddlewareHandler = (*LogAppend)(nil)
|
||||
)
|
||||
@@ -75,7 +75,7 @@ func (h LoggableHTTPHeader) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
||||
if !h.ShouldLogCredentials {
|
||||
switch strings.ToLower(key) {
|
||||
case "cookie", "set-cookie", "authorization", "proxy-authorization":
|
||||
val = []string{}
|
||||
val = []string{"REDACTED"} // see #5669. I still think ▒▒▒▒ would be cool.
|
||||
}
|
||||
}
|
||||
enc.AddArray(key, LoggableStringArray(val))
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -33,7 +34,6 @@ import (
|
||||
"github.com/google/cel-go/cel"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
@@ -456,8 +456,7 @@ func (m MatchPath) Match(r *http.Request) bool {
|
||||
// treat it as a fast substring match
|
||||
if strings.Count(matchPattern, "*") == 2 &&
|
||||
strings.HasPrefix(matchPattern, "*") &&
|
||||
strings.HasSuffix(matchPattern, "*") &&
|
||||
strings.Count(matchPattern, "*") == 2 {
|
||||
strings.HasSuffix(matchPattern, "*") {
|
||||
if strings.Contains(reqPathForPattern, matchPattern[1:len(matchPattern)-1]) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -73,11 +73,11 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
|
||||
|
||||
switch len(args) {
|
||||
case 1:
|
||||
err = headers.CaddyfileHeaderOp(&handler.Headers.HeaderOps, args[0], "", "")
|
||||
err = headers.CaddyfileHeaderOp(&handler.Headers.HeaderOps, args[0], "", nil)
|
||||
case 2:
|
||||
err = headers.CaddyfileHeaderOp(&handler.Headers.HeaderOps, args[0], args[1], "")
|
||||
err = headers.CaddyfileHeaderOp(&handler.Headers.HeaderOps, args[0], args[1], nil)
|
||||
case 3:
|
||||
err = headers.CaddyfileHeaderOp(&handler.Headers.HeaderOps, args[0], args[1], args[2])
|
||||
err = headers.CaddyfileHeaderOp(&handler.Headers.HeaderOps, args[0], args[1], &args[2])
|
||||
default:
|
||||
return nil, h.ArgErr()
|
||||
}
|
||||
|
||||
@@ -119,11 +119,38 @@ func addHTTPVarsToReplacer(repl *caddy.Replacer, req *http.Request, w http.Respo
|
||||
return port, true
|
||||
case "http.request.hostport":
|
||||
return req.Host, true
|
||||
case "http.request.local":
|
||||
localAddr, _ := req.Context().Value(http.LocalAddrContextKey).(net.Addr)
|
||||
return localAddr.String(), true
|
||||
case "http.request.local.host":
|
||||
localAddr, _ := req.Context().Value(http.LocalAddrContextKey).(net.Addr)
|
||||
host, _, err := net.SplitHostPort(localAddr.String())
|
||||
if err != nil {
|
||||
// localAddr is host:port for tcp and udp sockets and /unix/socket.path
|
||||
// for unix sockets. net.SplitHostPort only operates on tcp and udp sockets,
|
||||
// not unix sockets and will fail with the latter.
|
||||
// We assume when net.SplitHostPort fails, localAddr is a unix socket and thus
|
||||
// already "split" and save to return.
|
||||
return localAddr, true
|
||||
}
|
||||
return host, true
|
||||
case "http.request.local.port":
|
||||
localAddr, _ := req.Context().Value(http.LocalAddrContextKey).(net.Addr)
|
||||
_, port, _ := net.SplitHostPort(localAddr.String())
|
||||
if portNum, err := strconv.Atoi(port); err == nil {
|
||||
return portNum, true
|
||||
}
|
||||
return port, true
|
||||
case "http.request.remote":
|
||||
return req.RemoteAddr, true
|
||||
case "http.request.remote.host":
|
||||
host, _, err := net.SplitHostPort(req.RemoteAddr)
|
||||
if err != nil {
|
||||
// req.RemoteAddr is host:port for tcp and udp sockets and /unix/socket.path
|
||||
// for unix sockets. net.SplitHostPort only operates on tcp and udp sockets,
|
||||
// not unix sockets and will fail with the latter.
|
||||
// We assume when net.SplitHostPort fails, req.RemoteAddr is a unix socket
|
||||
// and thus already "split" and save to return.
|
||||
return req.RemoteAddr, true
|
||||
}
|
||||
return host, true
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
@@ -29,7 +30,9 @@ import (
|
||||
func TestHTTPVarReplacement(t *testing.T) {
|
||||
req, _ := http.NewRequest(http.MethodGet, "/foo/bar.tar.gz", nil)
|
||||
repl := caddy.NewReplacer()
|
||||
localAddr, _ := net.ResolveTCPAddr("tcp", "192.168.159.1:80")
|
||||
ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl)
|
||||
ctx = context.WithValue(ctx, http.LocalAddrContextKey, localAddr)
|
||||
req = req.WithContext(ctx)
|
||||
req.Host = "example.com:80"
|
||||
req.RemoteAddr = "192.168.159.32:1234"
|
||||
@@ -95,6 +98,18 @@ eqp31wM9il1n+guTNyxJd+FzVAH+hCZE5K+tCgVDdVFUlDEHHbS/wqb2PSIoouLV
|
||||
get: "http.request.hostport",
|
||||
expect: "example.com:80",
|
||||
},
|
||||
{
|
||||
get: "http.request.local.host",
|
||||
expect: "192.168.159.1",
|
||||
},
|
||||
{
|
||||
get: "http.request.local.port",
|
||||
expect: "80",
|
||||
},
|
||||
{
|
||||
get: "http.request.local",
|
||||
expect: "192.168.159.1:80",
|
||||
},
|
||||
{
|
||||
get: "http.request.remote.host",
|
||||
expect: "192.168.159.32",
|
||||
|
||||
@@ -230,6 +230,16 @@ func (rr *responseRecorder) WriteResponse() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// FlushError will suppress actual flushing if the response is buffered. See:
|
||||
// https://github.com/caddyserver/caddy/issues/6144
|
||||
func (rr *responseRecorder) FlushError() error {
|
||||
if rr.stream {
|
||||
//nolint:bodyclose
|
||||
return http.NewResponseController(rr.ResponseWriterWrapper).Flush()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rr *responseRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
//nolint:bodyclose
|
||||
conn, brw, err := http.NewResponseController(rr.ResponseWriterWrapper).Hijack()
|
||||
|
||||
@@ -32,7 +32,7 @@ func init() {
|
||||
// reverse proxy upstreams in the pool.
|
||||
type adminUpstreams struct{}
|
||||
|
||||
// upstreamResults holds the status of a particular upstream
|
||||
// upstreamStatus holds the status of a particular upstream
|
||||
type upstreamStatus struct {
|
||||
Address string `json:"address"`
|
||||
NumRequests int `json:"num_requests"`
|
||||
|
||||
@@ -69,6 +69,8 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
|
||||
// health_uri <uri>
|
||||
// health_port <port>
|
||||
// health_interval <interval>
|
||||
// health_passes <num>
|
||||
// health_fails <num>
|
||||
// health_timeout <duration>
|
||||
// health_status <status>
|
||||
// health_body <regexp>
|
||||
@@ -447,6 +449,38 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
}
|
||||
h.HealthChecks.Active.ExpectBody = d.Val()
|
||||
|
||||
case "health_passes":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
if h.HealthChecks == nil {
|
||||
h.HealthChecks = new(HealthChecks)
|
||||
}
|
||||
if h.HealthChecks.Active == nil {
|
||||
h.HealthChecks.Active = new(ActiveHealthChecks)
|
||||
}
|
||||
passes, err := strconv.Atoi(d.Val())
|
||||
if err != nil {
|
||||
return d.Errf("invalid passes count '%s': %v", d.Val(), err)
|
||||
}
|
||||
h.HealthChecks.Active.Passes = passes
|
||||
|
||||
case "health_fails":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
if h.HealthChecks == nil {
|
||||
h.HealthChecks = new(HealthChecks)
|
||||
}
|
||||
if h.HealthChecks.Active == nil {
|
||||
h.HealthChecks.Active = new(ActiveHealthChecks)
|
||||
}
|
||||
fails, err := strconv.Atoi(d.Val())
|
||||
if err != nil {
|
||||
return d.Errf("invalid fails count '%s': %v", d.Val(), err)
|
||||
}
|
||||
h.HealthChecks.Active.Fails = fails
|
||||
|
||||
case "max_fails":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
@@ -649,7 +683,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
|
||||
switch len(args) {
|
||||
case 1:
|
||||
err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], "", "")
|
||||
err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], "", nil)
|
||||
case 2:
|
||||
// some lint checks, I guess
|
||||
if strings.EqualFold(args[0], "host") && (args[1] == "{hostport}" || args[1] == "{http.request.hostport}") {
|
||||
@@ -664,9 +698,9 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
if strings.EqualFold(args[0], "x-forwarded-host") && (args[1] == "{host}" || args[1] == "{http.request.host}" || args[1] == "{hostport}" || args[1] == "{http.request.hostport}") {
|
||||
caddy.Log().Named("caddyfile").Warn("Unnecessary header_up X-Forwarded-Host: the reverse proxy's default behavior is to pass headers to the upstream")
|
||||
}
|
||||
err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], args[1], "")
|
||||
err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], args[1], nil)
|
||||
case 3:
|
||||
err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], args[1], args[2])
|
||||
err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], args[1], &args[2])
|
||||
default:
|
||||
return d.ArgErr()
|
||||
}
|
||||
@@ -687,13 +721,14 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
}
|
||||
}
|
||||
args := d.RemainingArgs()
|
||||
|
||||
switch len(args) {
|
||||
case 1:
|
||||
err = headers.CaddyfileHeaderOp(h.Headers.Response.HeaderOps, args[0], "", "")
|
||||
err = headers.CaddyfileHeaderOp(h.Headers.Response.HeaderOps, args[0], "", nil)
|
||||
case 2:
|
||||
err = headers.CaddyfileHeaderOp(h.Headers.Response.HeaderOps, args[0], args[1], "")
|
||||
err = headers.CaddyfileHeaderOp(h.Headers.Response.HeaderOps, args[0], args[1], nil)
|
||||
case 3:
|
||||
err = headers.CaddyfileHeaderOp(h.Headers.Response.HeaderOps, args[0], args[1], args[2])
|
||||
err = headers.CaddyfileHeaderOp(h.Headers.Response.HeaderOps, args[0], args[1], &args[2])
|
||||
default:
|
||||
return d.ArgErr()
|
||||
}
|
||||
@@ -907,6 +942,7 @@ func (h *Handler) FinalizeUnmarshalCaddyfile(helper httpcaddyfile.Helper) error
|
||||
// read_buffer <size>
|
||||
// write_buffer <size>
|
||||
// max_response_header <size>
|
||||
// forward_proxy_url <url>
|
||||
// dial_timeout <duration>
|
||||
// dial_fallback_delay <duration>
|
||||
// response_header_timeout <duration>
|
||||
@@ -994,6 +1030,12 @@ func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
return d.Errf("invalid proxy protocol version '%s'", proxyProtocol)
|
||||
}
|
||||
|
||||
case "forward_proxy_url":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
h.ForwardProxyURL = d.Val()
|
||||
|
||||
case "dial_timeout":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
|
||||
@@ -89,6 +89,14 @@ type ActiveHealthChecks struct {
|
||||
// considering it unhealthy (default 5s).
|
||||
Timeout caddy.Duration `json:"timeout,omitempty"`
|
||||
|
||||
// Number of consecutive health check passes before marking
|
||||
// a previously unhealthy backend as healthy again (default 1).
|
||||
Passes int `json:"passes,omitempty"`
|
||||
|
||||
// Number of consecutive health check failures before marking
|
||||
// a previously healthy backend as unhealthy (default 1).
|
||||
Fails int `json:"fails,omitempty"`
|
||||
|
||||
// The maximum response body to download from the backend
|
||||
// during a health check.
|
||||
MaxSize int64 `json:"max_size,omitempty"`
|
||||
@@ -167,6 +175,14 @@ func (a *ActiveHealthChecks) Provision(ctx caddy.Context, h *Handler) error {
|
||||
}
|
||||
}
|
||||
|
||||
if a.Passes < 1 {
|
||||
a.Passes = 1
|
||||
}
|
||||
|
||||
if a.Fails < 1 {
|
||||
a.Fails = 1
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -373,9 +389,37 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
|
||||
}
|
||||
|
||||
markUnhealthy := func() {
|
||||
// dispatch an event that the host newly became unhealthy
|
||||
if upstream.setHealthy(false) {
|
||||
h.events.Emit(h.ctx, "unhealthy", map[string]any{"host": hostAddr})
|
||||
// increment failures and then check if it has reached the threshold to mark unhealthy
|
||||
err := upstream.Host.countHealthFail(1)
|
||||
if err != nil {
|
||||
h.HealthChecks.Active.logger.Error("could not count active health failure",
|
||||
zap.String("host", upstream.Dial),
|
||||
zap.Error(err))
|
||||
return
|
||||
}
|
||||
if upstream.Host.activeHealthFails() >= h.HealthChecks.Active.Fails {
|
||||
// dispatch an event that the host newly became unhealthy
|
||||
if upstream.setHealthy(false) {
|
||||
h.events.Emit(h.ctx, "unhealthy", map[string]any{"host": hostAddr})
|
||||
upstream.Host.resetHealth()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
markHealthy := func() {
|
||||
// increment passes and then check if it has reached the threshold to be healthy
|
||||
err := upstream.Host.countHealthPass(1)
|
||||
if err != nil {
|
||||
h.HealthChecks.Active.logger.Error("could not count active health pass",
|
||||
zap.String("host", upstream.Dial),
|
||||
zap.Error(err))
|
||||
return
|
||||
}
|
||||
if upstream.Host.activeHealthPasses() >= h.HealthChecks.Active.Passes {
|
||||
if upstream.setHealthy(true) {
|
||||
h.events.Emit(h.ctx, "healthy", map[string]any{"host": hostAddr})
|
||||
upstream.Host.resetHealth()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,10 +483,8 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
|
||||
}
|
||||
|
||||
// passed health check parameters, so mark as healthy
|
||||
if upstream.setHealthy(true) {
|
||||
h.HealthChecks.Active.logger.Info("host is up", zap.String("host", hostAddr))
|
||||
h.events.Emit(h.ctx, "healthy", map[string]any{"host": hostAddr})
|
||||
}
|
||||
h.HealthChecks.Active.logger.Info("host is up", zap.String("host", hostAddr))
|
||||
markHealthy()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -136,8 +136,10 @@ func (u *Upstream) fillHost() {
|
||||
// Host is the basic, in-memory representation of the state of a remote host.
|
||||
// Its fields are accessed atomically and Host values must not be copied.
|
||||
type Host struct {
|
||||
numRequests int64 // must be 64-bit aligned on 32-bit systems (see https://golang.org/pkg/sync/atomic/#pkg-note-BUG)
|
||||
fails int64
|
||||
numRequests int64 // must be 64-bit aligned on 32-bit systems (see https://golang.org/pkg/sync/atomic/#pkg-note-BUG)
|
||||
fails int64
|
||||
activePasses int64
|
||||
activeFails int64
|
||||
}
|
||||
|
||||
// NumRequests returns the number of active requests to the upstream.
|
||||
@@ -150,6 +152,16 @@ func (h *Host) Fails() int {
|
||||
return int(atomic.LoadInt64(&h.fails))
|
||||
}
|
||||
|
||||
// activeHealthPasses returns the number of consecutive active health check passes with the upstream.
|
||||
func (h *Host) activeHealthPasses() int {
|
||||
return int(atomic.LoadInt64(&h.activePasses))
|
||||
}
|
||||
|
||||
// activeHealthFails returns the number of consecutive active health check failures with the upstream.
|
||||
func (h *Host) activeHealthFails() int {
|
||||
return int(atomic.LoadInt64(&h.activeFails))
|
||||
}
|
||||
|
||||
// countRequest mutates the active request count by
|
||||
// delta. It returns an error if the adjustment fails.
|
||||
func (h *Host) countRequest(delta int) error {
|
||||
@@ -170,6 +182,32 @@ func (h *Host) countFail(delta int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// countHealthPass mutates the recent passes count by
|
||||
// delta. It returns an error if the adjustment fails.
|
||||
func (h *Host) countHealthPass(delta int) error {
|
||||
result := atomic.AddInt64(&h.activePasses, int64(delta))
|
||||
if result < 0 {
|
||||
return fmt.Errorf("count below 0: %d", result)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// countHealthFail mutates the recent failures count by
|
||||
// delta. It returns an error if the adjustment fails.
|
||||
func (h *Host) countHealthFail(delta int) error {
|
||||
result := atomic.AddInt64(&h.activeFails, int64(delta))
|
||||
if result < 0 {
|
||||
return fmt.Errorf("count below 0: %d", result)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// resetHealth resets the health check counters.
|
||||
func (h *Host) resetHealth() {
|
||||
atomic.StoreInt64(&h.activePasses, 0)
|
||||
atomic.StoreInt64(&h.activeFails, 0)
|
||||
}
|
||||
|
||||
// healthy returns true if the upstream is not actively marked as unhealthy.
|
||||
// (This returns the status only from the "active" health checks.)
|
||||
func (u *Upstream) healthy() bool {
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
weakrand "math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -71,6 +72,22 @@ type HTTPTransport struct {
|
||||
// connecting to an upstream. Default: off.
|
||||
ProxyProtocol string `json:"proxy_protocol,omitempty"`
|
||||
|
||||
// URL to the server that the HTTP transport will use to proxy
|
||||
// requests to the upstream. See http.Transport.Proxy for
|
||||
// information regarding supported protocols. This value takes
|
||||
// precedence over `HTTP_PROXY`, etc.
|
||||
//
|
||||
// Providing a value to this parameter results in
|
||||
// requests flowing through the reverse_proxy in the following
|
||||
// way:
|
||||
//
|
||||
// User Agent ->
|
||||
// reverse_proxy ->
|
||||
// forward_proxy_url -> upstream
|
||||
//
|
||||
// Default: http.ProxyFromEnvironment
|
||||
ForwardProxyURL string `json:"forward_proxy_url,omitempty"`
|
||||
|
||||
// How long to wait before timing out trying to connect to
|
||||
// an upstream. Default: `3s`.
|
||||
DialTimeout caddy.Duration `json:"dial_timeout,omitempty"`
|
||||
@@ -265,8 +282,21 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// negotiate any HTTP/SOCKS proxy for the HTTP transport
|
||||
var proxy func(*http.Request) (*url.URL, error)
|
||||
if h.ForwardProxyURL != "" {
|
||||
pUrl, err := url.Parse(h.ForwardProxyURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse transport proxy url: %v", err)
|
||||
}
|
||||
caddyCtx.Logger().Info("setting transport proxy url", zap.String("url", h.ForwardProxyURL))
|
||||
proxy = http.ProxyURL(pUrl)
|
||||
} else {
|
||||
proxy = http.ProxyFromEnvironment
|
||||
}
|
||||
|
||||
rt := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Proxy: proxy,
|
||||
DialContext: dialContext,
|
||||
MaxConnsPerHost: h.MaxConnsPerHost,
|
||||
ResponseHeaderTimeout: time.Duration(h.ResponseHeaderTimeout),
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
weakrand "math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -28,6 +27,8 @@ import (
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
@@ -807,7 +808,7 @@ func hostByHashing(pool []*Upstream, s string) *Upstream {
|
||||
// see https://medium.com/i0exception/rendezvous-hashing-8c00e2fb58b0,
|
||||
// https://randorithms.com/2020/12/26/rendezvous-hashing.html,
|
||||
// and https://en.wikipedia.org/wiki/Rendezvous_hashing.
|
||||
var highestHash uint32
|
||||
var highestHash uint64
|
||||
var upstream *Upstream
|
||||
for _, up := range pool {
|
||||
if !up.Available() {
|
||||
@@ -823,10 +824,10 @@ func hostByHashing(pool []*Upstream, s string) *Upstream {
|
||||
}
|
||||
|
||||
// hash calculates a fast hash based on s.
|
||||
func hash(s string) uint32 {
|
||||
h := fnv.New32a()
|
||||
func hash(s string) uint64 {
|
||||
h := xxhash.New()
|
||||
_, _ = h.Write([]byte(s))
|
||||
return h.Sum32()
|
||||
return h.Sum64()
|
||||
}
|
||||
|
||||
func loadFallbackPolicy(d *caddyfile.Dispenser) (json.RawMessage, error) {
|
||||
|
||||
@@ -120,7 +120,7 @@ func TestWeightedRoundRobinPolicy(t *testing.T) {
|
||||
|
||||
h = wrrPolicy.Select(pool, req, nil)
|
||||
if h != pool[0] {
|
||||
t.Error("Expected to select first host on availablity.")
|
||||
t.Error("Expected to select first host on availability.")
|
||||
}
|
||||
// mark host as full
|
||||
pool[1].countRequest(1)
|
||||
@@ -157,8 +157,8 @@ func TestIPHashPolicy(t *testing.T) {
|
||||
// We should be able to predict where every request is routed.
|
||||
req.RemoteAddr = "172.0.0.1:80"
|
||||
h := ipHash.Select(pool, req, nil)
|
||||
if h != pool[1] {
|
||||
t.Error("Expected ip hash policy host to be the second host.")
|
||||
if h != pool[0] {
|
||||
t.Error("Expected ip hash policy host to be the first host.")
|
||||
}
|
||||
req.RemoteAddr = "172.0.0.2:80"
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
@@ -167,8 +167,8 @@ func TestIPHashPolicy(t *testing.T) {
|
||||
}
|
||||
req.RemoteAddr = "172.0.0.3:80"
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
if h != pool[1] {
|
||||
t.Error("Expected ip hash policy host to be the second host.")
|
||||
if h != pool[0] {
|
||||
t.Error("Expected ip hash policy host to be the first host.")
|
||||
}
|
||||
req.RemoteAddr = "172.0.0.4:80"
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
@@ -179,8 +179,8 @@ func TestIPHashPolicy(t *testing.T) {
|
||||
// we should get the same results without a port
|
||||
req.RemoteAddr = "172.0.0.1"
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
if h != pool[1] {
|
||||
t.Error("Expected ip hash policy host to be the second host.")
|
||||
if h != pool[0] {
|
||||
t.Error("Expected ip hash policy host to be the first host.")
|
||||
}
|
||||
req.RemoteAddr = "172.0.0.2"
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
@@ -189,8 +189,8 @@ func TestIPHashPolicy(t *testing.T) {
|
||||
}
|
||||
req.RemoteAddr = "172.0.0.3"
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
if h != pool[1] {
|
||||
t.Error("Expected ip hash policy host to be the second host.")
|
||||
if h != pool[0] {
|
||||
t.Error("Expected ip hash policy host to be the first host.")
|
||||
}
|
||||
req.RemoteAddr = "172.0.0.4"
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
@@ -203,8 +203,8 @@ func TestIPHashPolicy(t *testing.T) {
|
||||
req.RemoteAddr = "172.0.0.4"
|
||||
pool[1].setHealthy(false)
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
if h != pool[0] {
|
||||
t.Error("Expected ip hash policy host to be the first host.")
|
||||
if h != pool[2] {
|
||||
t.Error("Expected ip hash policy host to be the third host.")
|
||||
}
|
||||
|
||||
req.RemoteAddr = "172.0.0.2"
|
||||
@@ -217,8 +217,8 @@ func TestIPHashPolicy(t *testing.T) {
|
||||
req.RemoteAddr = "172.0.0.3"
|
||||
pool[2].setHealthy(false)
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
if h != pool[1] {
|
||||
t.Error("Expected ip hash policy host to be the second host.")
|
||||
if h != pool[0] {
|
||||
t.Error("Expected ip hash policy host to be the first host.")
|
||||
}
|
||||
req.RemoteAddr = "172.0.0.4"
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
@@ -300,8 +300,8 @@ func TestClientIPHashPolicy(t *testing.T) {
|
||||
// We should be able to predict where every request is routed.
|
||||
caddyhttp.SetVar(req.Context(), caddyhttp.ClientIPVarKey, "172.0.0.1:80")
|
||||
h := ipHash.Select(pool, req, nil)
|
||||
if h != pool[1] {
|
||||
t.Error("Expected ip hash policy host to be the second host.")
|
||||
if h != pool[0] {
|
||||
t.Error("Expected ip hash policy host to be the first host.")
|
||||
}
|
||||
caddyhttp.SetVar(req.Context(), caddyhttp.ClientIPVarKey, "172.0.0.2:80")
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
@@ -310,8 +310,8 @@ func TestClientIPHashPolicy(t *testing.T) {
|
||||
}
|
||||
caddyhttp.SetVar(req.Context(), caddyhttp.ClientIPVarKey, "172.0.0.3:80")
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
if h != pool[1] {
|
||||
t.Error("Expected ip hash policy host to be the second host.")
|
||||
if h != pool[0] {
|
||||
t.Error("Expected ip hash policy host to be the first host.")
|
||||
}
|
||||
caddyhttp.SetVar(req.Context(), caddyhttp.ClientIPVarKey, "172.0.0.4:80")
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
@@ -322,8 +322,8 @@ func TestClientIPHashPolicy(t *testing.T) {
|
||||
// we should get the same results without a port
|
||||
caddyhttp.SetVar(req.Context(), caddyhttp.ClientIPVarKey, "172.0.0.1")
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
if h != pool[1] {
|
||||
t.Error("Expected ip hash policy host to be the second host.")
|
||||
if h != pool[0] {
|
||||
t.Error("Expected ip hash policy host to be the first host.")
|
||||
}
|
||||
caddyhttp.SetVar(req.Context(), caddyhttp.ClientIPVarKey, "172.0.0.2")
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
@@ -332,8 +332,8 @@ func TestClientIPHashPolicy(t *testing.T) {
|
||||
}
|
||||
caddyhttp.SetVar(req.Context(), caddyhttp.ClientIPVarKey, "172.0.0.3")
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
if h != pool[1] {
|
||||
t.Error("Expected ip hash policy host to be the second host.")
|
||||
if h != pool[0] {
|
||||
t.Error("Expected ip hash policy host to be the first host.")
|
||||
}
|
||||
caddyhttp.SetVar(req.Context(), caddyhttp.ClientIPVarKey, "172.0.0.4")
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
@@ -346,8 +346,8 @@ func TestClientIPHashPolicy(t *testing.T) {
|
||||
caddyhttp.SetVar(req.Context(), caddyhttp.ClientIPVarKey, "172.0.0.4")
|
||||
pool[1].setHealthy(false)
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
if h != pool[0] {
|
||||
t.Error("Expected ip hash policy host to be the first host.")
|
||||
if h != pool[2] {
|
||||
t.Error("Expected ip hash policy host to be the third host.")
|
||||
}
|
||||
|
||||
caddyhttp.SetVar(req.Context(), caddyhttp.ClientIPVarKey, "172.0.0.2")
|
||||
@@ -360,8 +360,8 @@ func TestClientIPHashPolicy(t *testing.T) {
|
||||
caddyhttp.SetVar(req.Context(), caddyhttp.ClientIPVarKey, "172.0.0.3")
|
||||
pool[2].setHealthy(false)
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
if h != pool[1] {
|
||||
t.Error("Expected ip hash policy host to be the second host.")
|
||||
if h != pool[0] {
|
||||
t.Error("Expected ip hash policy host to be the first host.")
|
||||
}
|
||||
caddyhttp.SetVar(req.Context(), caddyhttp.ClientIPVarKey, "172.0.0.4")
|
||||
h = ipHash.Select(pool, req, nil)
|
||||
@@ -470,21 +470,21 @@ func TestQueryHashPolicy(t *testing.T) {
|
||||
|
||||
request = httptest.NewRequest(http.MethodGet, "/?foo=100000", nil)
|
||||
h = queryPolicy.Select(pool, request, nil)
|
||||
if h != pool[0] {
|
||||
t.Error("Expected query policy host to be the first host.")
|
||||
if h != pool[1] {
|
||||
t.Error("Expected query policy host to be the second host.")
|
||||
}
|
||||
|
||||
request = httptest.NewRequest(http.MethodGet, "/?foo=1", nil)
|
||||
pool[0].setHealthy(false)
|
||||
h = queryPolicy.Select(pool, request, nil)
|
||||
if h != pool[1] {
|
||||
t.Error("Expected query policy host to be the second host.")
|
||||
if h != pool[2] {
|
||||
t.Error("Expected query policy host to be the third host.")
|
||||
}
|
||||
|
||||
request = httptest.NewRequest(http.MethodGet, "/?foo=100000", nil)
|
||||
h = queryPolicy.Select(pool, request, nil)
|
||||
if h != pool[2] {
|
||||
t.Error("Expected query policy host to be the third host.")
|
||||
if h != pool[1] {
|
||||
t.Error("Expected query policy host to be the second host.")
|
||||
}
|
||||
|
||||
// We should be able to resize the host pool and still be able to predict
|
||||
@@ -533,14 +533,14 @@ func TestURIHashPolicy(t *testing.T) {
|
||||
|
||||
request := httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||
h := uriPolicy.Select(pool, request, nil)
|
||||
if h != pool[1] {
|
||||
t.Error("Expected uri policy host to be the second host.")
|
||||
if h != pool[2] {
|
||||
t.Error("Expected uri policy host to be the third host.")
|
||||
}
|
||||
|
||||
pool[2].setHealthy(false)
|
||||
h = uriPolicy.Select(pool, request, nil)
|
||||
if h != pool[1] {
|
||||
t.Error("Expected uri policy host to be the second host.")
|
||||
if h != pool[0] {
|
||||
t.Error("Expected uri policy host to be the first host.")
|
||||
}
|
||||
|
||||
request = httptest.NewRequest(http.MethodGet, "/test_2", nil)
|
||||
@@ -691,54 +691,54 @@ func TestCookieHashPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCookieHashPolicyWithSecureRequest(t *testing.T) {
|
||||
ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
|
||||
defer cancel()
|
||||
cookieHashPolicy := CookieHashSelection{}
|
||||
if err := cookieHashPolicy.Provision(ctx); err != nil {
|
||||
t.Errorf("Provision error: %v", err)
|
||||
t.FailNow()
|
||||
}
|
||||
ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
|
||||
defer cancel()
|
||||
cookieHashPolicy := CookieHashSelection{}
|
||||
if err := cookieHashPolicy.Provision(ctx); err != nil {
|
||||
t.Errorf("Provision error: %v", err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
pool := testPool()
|
||||
pool[0].Dial = "localhost:8080"
|
||||
pool[1].Dial = "localhost:8081"
|
||||
pool[2].Dial = "localhost:8082"
|
||||
pool[0].setHealthy(true)
|
||||
pool[1].setHealthy(false)
|
||||
pool[2].setHealthy(false)
|
||||
pool := testPool()
|
||||
pool[0].Dial = "localhost:8080"
|
||||
pool[1].Dial = "localhost:8081"
|
||||
pool[2].Dial = "localhost:8082"
|
||||
pool[0].setHealthy(true)
|
||||
pool[1].setHealthy(false)
|
||||
pool[2].setHealthy(false)
|
||||
|
||||
// Create a test server that serves HTTPS requests
|
||||
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
h := cookieHashPolicy.Select(pool, r, w)
|
||||
if h != pool[0] {
|
||||
t.Error("Expected cookieHashPolicy host to be the first only available host.")
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
// Create a test server that serves HTTPS requests
|
||||
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
h := cookieHashPolicy.Select(pool, r, w)
|
||||
if h != pool[0] {
|
||||
t.Error("Expected cookieHashPolicy host to be the first only available host.")
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
// Make a new HTTPS request to the test server
|
||||
client := ts.Client()
|
||||
request, err := http.NewRequest(http.MethodGet, ts.URL+"/test", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Make a new HTTPS request to the test server
|
||||
client := ts.Client()
|
||||
request, err := http.NewRequest(http.MethodGet, ts.URL+"/test", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check if the cookie set is Secure and has SameSiteNone mode
|
||||
cookies := response.Cookies()
|
||||
if len(cookies) == 0 {
|
||||
t.Fatal("Expected a cookie to be set")
|
||||
}
|
||||
cookie := cookies[0]
|
||||
if !cookie.Secure {
|
||||
t.Error("Expected cookie Secure attribute to be true when request is secure")
|
||||
}
|
||||
if cookie.SameSite != http.SameSiteNoneMode {
|
||||
t.Error("Expected cookie SameSite attribute to be None when request is secure")
|
||||
}
|
||||
// Check if the cookie set is Secure and has SameSiteNone mode
|
||||
cookies := response.Cookies()
|
||||
if len(cookies) == 0 {
|
||||
t.Fatal("Expected a cookie to be set")
|
||||
}
|
||||
cookie := cookies[0]
|
||||
if !cookie.Secure {
|
||||
t.Error("Expected cookie Secure attribute to be true when request is secure")
|
||||
}
|
||||
if cookie.SameSite != http.SameSiteNoneMode {
|
||||
t.Error("Expected cookie SameSite attribute to be None when request is secure")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCookieHashPolicyWithFirstFallback(t *testing.T) {
|
||||
|
||||
@@ -49,6 +49,13 @@ type SRVUpstreams struct {
|
||||
// Results are cached between lookups. Default: 1m
|
||||
Refresh caddy.Duration `json:"refresh,omitempty"`
|
||||
|
||||
// If > 0 and there is an error with the lookup,
|
||||
// continue to use the cached results for up to
|
||||
// this long before trying again, (even though they
|
||||
// are stale) instead of returning an error to the
|
||||
// client. Default: 0s.
|
||||
GracePeriod caddy.Duration `json:"grace_period,omitempty"`
|
||||
|
||||
// Configures the DNS resolver used to resolve the
|
||||
// SRV address to SRV records.
|
||||
Resolver *UpstreamResolver `json:"resolver,omitempty"`
|
||||
@@ -140,6 +147,12 @@ func (su SRVUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
|
||||
// out and an error will be returned alongside the remaining results, if any." Thus, we
|
||||
// only return an error if no records were also returned.
|
||||
if len(records) == 0 {
|
||||
if su.GracePeriod > 0 {
|
||||
su.logger.Error("SRV lookup failed; using previously cached", zap.Error(err))
|
||||
cached.freshness = time.Now().Add(time.Duration(su.GracePeriod) - time.Duration(su.Refresh))
|
||||
srvs[suAddr] = cached
|
||||
return allNew(cached.upstreams), nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
su.logger.Warn("SRV records filtered", zap.Error(err))
|
||||
|
||||
@@ -98,7 +98,7 @@ func parseCaddyfileURI(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, err
|
||||
h.Next() // consume directive name
|
||||
|
||||
args := h.RemainingArgs()
|
||||
if len(args) < 2 {
|
||||
if len(args) < 1 {
|
||||
return nil, h.ArgErr()
|
||||
}
|
||||
|
||||
@@ -158,12 +158,73 @@ func parseCaddyfileURI(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, err
|
||||
Replace: replace,
|
||||
})
|
||||
|
||||
case "query":
|
||||
if len(args) > 4 {
|
||||
return nil, h.ArgErr()
|
||||
}
|
||||
rewr.Query = &queryOps{}
|
||||
var hasArgs bool
|
||||
if len(args) > 1 {
|
||||
hasArgs = true
|
||||
err := applyQueryOps(h, rewr.Query, args[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for h.NextBlock(0) {
|
||||
if hasArgs {
|
||||
return nil, h.Err("Cannot specify uri query rewrites in both argument and block")
|
||||
}
|
||||
queryArgs := []string{h.Val()}
|
||||
queryArgs = append(queryArgs, h.RemainingArgs()...)
|
||||
err := applyQueryOps(h, rewr.Query, queryArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, h.Errf("unrecognized URI manipulation '%s'", args[0])
|
||||
}
|
||||
return rewr, nil
|
||||
}
|
||||
|
||||
func applyQueryOps(h httpcaddyfile.Helper, qo *queryOps, args []string) error {
|
||||
key := args[0]
|
||||
switch {
|
||||
case strings.HasPrefix(key, "-"):
|
||||
if len(args) != 1 {
|
||||
return h.ArgErr()
|
||||
}
|
||||
qo.Delete = append(qo.Delete, strings.TrimLeft(key, "-"))
|
||||
|
||||
case strings.HasPrefix(key, "+"):
|
||||
if len(args) != 2 {
|
||||
return h.ArgErr()
|
||||
}
|
||||
param := strings.TrimLeft(key, "+")
|
||||
qo.Add = append(qo.Add, queryOpsArguments{Key: param, Val: args[1]})
|
||||
|
||||
case strings.Contains(key, ">"):
|
||||
if len(args) != 1 {
|
||||
return h.ArgErr()
|
||||
}
|
||||
renameValKey := strings.Split(key, ">")
|
||||
qo.Rename = append(qo.Rename, queryOpsArguments{Key: renameValKey[0], Val: renameValKey[1]})
|
||||
|
||||
case len(args) == 3:
|
||||
qo.Replace = append(qo.Replace, &queryOpsReplacement{Key: key, SearchRegexp: args[1], Replace: args[2]})
|
||||
|
||||
default:
|
||||
if len(args) != 2 {
|
||||
return h.ArgErr()
|
||||
}
|
||||
qo.Set = append(qo.Set, queryOpsArguments{Key: key, Val: args[1]})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseCaddyfileHandlePath parses the handle_path directive. Syntax:
|
||||
//
|
||||
// handle_path [<matcher>] {
|
||||
|
||||
@@ -89,6 +89,9 @@ type Rewrite struct {
|
||||
// Performs regular expression replacements on the URI path.
|
||||
PathRegexp []*regexReplacer `json:"path_regexp,omitempty"`
|
||||
|
||||
// Mutates the query string of the URI.
|
||||
Query *queryOps `json:"query,omitempty"`
|
||||
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
@@ -114,6 +117,14 @@ func (rewr *Rewrite) Provision(ctx caddy.Context) error {
|
||||
}
|
||||
rep.re = re
|
||||
}
|
||||
if rewr.Query != nil {
|
||||
for _, replacementOp := range rewr.Query.Replace {
|
||||
err := replacementOp.Provision(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("compiling regular expression %s in query rewrite replace operation: %v", replacementOp.SearchRegexp, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -269,6 +280,11 @@ func (rewr Rewrite) Rewrite(r *http.Request, repl *caddy.Replacer) bool {
|
||||
rep.do(r, repl)
|
||||
}
|
||||
|
||||
// apply query operations
|
||||
if rewr.Query != nil {
|
||||
rewr.Query.do(r, repl)
|
||||
}
|
||||
|
||||
// update the encoded copy of the URI
|
||||
r.RequestURI = r.URL.RequestURI()
|
||||
|
||||
@@ -470,5 +486,133 @@ func changePath(req *http.Request, newVal func(pathOrRawPath string) string) {
|
||||
}
|
||||
}
|
||||
|
||||
// queryOps describes the operations to perform on query keys: add, set, rename and delete.
|
||||
type queryOps struct {
|
||||
// Renames a query key from Key to Val, without affecting the value.
|
||||
Rename []queryOpsArguments `json:"rename,omitempty"`
|
||||
|
||||
// Sets query parameters; overwrites a query key with the given value.
|
||||
Set []queryOpsArguments `json:"set,omitempty"`
|
||||
|
||||
// Adds query parameters; does not overwrite an existing query field,
|
||||
// and only appends an additional value for that key if any already exist.
|
||||
Add []queryOpsArguments `json:"add,omitempty"`
|
||||
|
||||
// Replaces query parameters.
|
||||
Replace []*queryOpsReplacement `json:"replace,omitempty"`
|
||||
|
||||
// Deletes a given query key by name.
|
||||
Delete []string `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
// Provision compiles the query replace operation regex.
|
||||
func (replacement *queryOpsReplacement) Provision(_ caddy.Context) error {
|
||||
if replacement.SearchRegexp != "" {
|
||||
re, err := regexp.Compile(replacement.SearchRegexp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("replacement for query field '%s': %v", replacement.Key, err)
|
||||
}
|
||||
replacement.re = re
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *queryOps) do(r *http.Request, repl *caddy.Replacer) {
|
||||
query := r.URL.Query()
|
||||
for _, renameParam := range q.Rename {
|
||||
key := repl.ReplaceAll(renameParam.Key, "")
|
||||
val := repl.ReplaceAll(renameParam.Val, "")
|
||||
if key == "" || val == "" {
|
||||
continue
|
||||
}
|
||||
query[val] = query[key]
|
||||
delete(query, key)
|
||||
}
|
||||
|
||||
for _, setParam := range q.Set {
|
||||
key := repl.ReplaceAll(setParam.Key, "")
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
val := repl.ReplaceAll(setParam.Val, "")
|
||||
query[key] = []string{val}
|
||||
}
|
||||
|
||||
for _, addParam := range q.Add {
|
||||
key := repl.ReplaceAll(addParam.Key, "")
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
val := repl.ReplaceAll(addParam.Val, "")
|
||||
query[key] = append(query[key], val)
|
||||
}
|
||||
|
||||
for _, replaceParam := range q.Replace {
|
||||
key := repl.ReplaceAll(replaceParam.Key, "")
|
||||
search := repl.ReplaceKnown(replaceParam.Search, "")
|
||||
replace := repl.ReplaceKnown(replaceParam.Replace, "")
|
||||
|
||||
// replace all query keys...
|
||||
if key == "*" {
|
||||
for fieldName, vals := range query {
|
||||
for i := range vals {
|
||||
if replaceParam.re != nil {
|
||||
query[fieldName][i] = replaceParam.re.ReplaceAllString(query[fieldName][i], replace)
|
||||
} else {
|
||||
query[fieldName][i] = strings.ReplaceAll(query[fieldName][i], search, replace)
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
for fieldName, vals := range query {
|
||||
for i := range vals {
|
||||
if replaceParam.re != nil {
|
||||
query[fieldName][i] = replaceParam.re.ReplaceAllString(query[fieldName][i], replace)
|
||||
} else {
|
||||
query[fieldName][i] = strings.ReplaceAll(query[fieldName][i], search, replace)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, deleteParam := range q.Delete {
|
||||
param := repl.ReplaceAll(deleteParam, "")
|
||||
if param == "" {
|
||||
continue
|
||||
}
|
||||
delete(query, param)
|
||||
}
|
||||
|
||||
r.URL.RawQuery = query.Encode()
|
||||
}
|
||||
|
||||
type queryOpsArguments struct {
|
||||
// A key in the query string. Note that query string keys may appear multiple times.
|
||||
Key string `json:"key,omitempty"`
|
||||
|
||||
// The value for the given operation; for add and set, this is
|
||||
// simply the value of the query, and for rename this is the
|
||||
// query key to rename to.
|
||||
Val string `json:"val,omitempty"`
|
||||
}
|
||||
|
||||
type queryOpsReplacement struct {
|
||||
// The key to replace in the query string.
|
||||
Key string `json:"key,omitempty"`
|
||||
|
||||
// The substring to search for.
|
||||
Search string `json:"search,omitempty"`
|
||||
|
||||
// The regular expression to search with.
|
||||
SearchRegexp string `json:"search_regexp,omitempty"`
|
||||
|
||||
// The string with which to replace matches.
|
||||
Replace string `json:"replace,omitempty"`
|
||||
|
||||
re *regexp.Regexp
|
||||
}
|
||||
|
||||
// Interface guard
|
||||
var _ caddyhttp.MiddlewareHandler = (*Rewrite)(nil)
|
||||
|
||||
+51
-18
@@ -27,7 +27,6 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
@@ -43,8 +42,6 @@ import (
|
||||
|
||||
// Server describes an HTTP server.
|
||||
type Server struct {
|
||||
activeRequests int64 // accessed atomically
|
||||
|
||||
// Socket addresses to which to bind listeners. Accepts
|
||||
// [network addresses](/docs/conventions#network-addresses)
|
||||
// that may include port ranges. Listener addresses must
|
||||
@@ -274,10 +271,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// advertise HTTP/3, if enabled
|
||||
if s.h3server != nil {
|
||||
// keep track of active requests for QUIC transport purposes
|
||||
atomic.AddInt64(&s.activeRequests, 1)
|
||||
defer atomic.AddInt64(&s.activeRequests, -1)
|
||||
|
||||
if r.ProtoMajor < 3 {
|
||||
err := s.h3server.SetQuicHeaders(w.Header())
|
||||
if err != nil {
|
||||
@@ -567,7 +560,7 @@ func (s *Server) findLastRouteWithHostMatcher() int {
|
||||
// the listener, with Server s as the handler.
|
||||
func (s *Server) serveHTTP3(addr caddy.NetworkAddress, tlsCfg *tls.Config) error {
|
||||
addr.Network = getHTTP3Network(addr.Network)
|
||||
h3ln, err := addr.ListenQUIC(s.ctx, 0, net.ListenConfig{}, tlsCfg, &s.activeRequests)
|
||||
h3ln, err := addr.ListenQUIC(s.ctx, 0, net.ListenConfig{}, tlsCfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("starting HTTP/3 QUIC listener: %v", err)
|
||||
}
|
||||
@@ -575,12 +568,30 @@ func (s *Server) serveHTTP3(addr caddy.NetworkAddress, tlsCfg *tls.Config) error
|
||||
// create HTTP/3 server if not done already
|
||||
if s.h3server == nil {
|
||||
s.h3server = &http3.Server{
|
||||
Handler: s,
|
||||
// Currently when closing a http3.Server, only listeners are closed. But caddy reuses these listeners
|
||||
// if possible, requests are still read and handled by the old handler. Close these connections manually.
|
||||
// see issue: https://github.com/caddyserver/caddy/issues/6195
|
||||
// Will interrupt ongoing requests.
|
||||
// TODO: remove the handler wrap after http3.Server.CloseGracefully is implemented, see App.Stop
|
||||
Handler: http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
select {
|
||||
case <-s.ctx.Done():
|
||||
if quicConn, ok := request.Context().Value(quicConnCtxKey).(quic.Connection); ok {
|
||||
//nolint:errcheck
|
||||
quicConn.CloseWithError(quic.ApplicationErrorCode(http3.ErrCodeRequestRejected), "")
|
||||
}
|
||||
default:
|
||||
s.ServeHTTP(writer, request)
|
||||
}
|
||||
}),
|
||||
TLSConfig: tlsCfg,
|
||||
MaxHeaderBytes: s.MaxHeaderBytes,
|
||||
// TODO: remove this config when draft versions are no longer supported (we have no need to support drafts)
|
||||
QuicConfig: &quic.Config{
|
||||
Versions: []quic.VersionNumber{quic.Version1, quic.Version2},
|
||||
Versions: []quic.Version{quic.Version1, quic.Version2},
|
||||
},
|
||||
ConnContext: func(ctx context.Context, c quic.Connection) context.Context {
|
||||
return context.WithValue(ctx, quicConnCtxKey, c)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -715,7 +726,7 @@ func (s *Server) logRequest(
|
||||
repl *caddy.Replacer, bodyReader *lengthReader, shouldLogCredentials bool,
|
||||
) {
|
||||
// this request may be flagged as omitted from the logs
|
||||
if skipLog, ok := GetVar(r.Context(), SkipLogVar).(bool); ok && skipLog {
|
||||
if skip, ok := GetVar(r.Context(), LogSkipVar).(bool); ok && skip {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -902,9 +913,18 @@ func trustedRealClientIP(r *http.Request, headers []string, clientIP string) str
|
||||
allValues := strings.Split(strings.Join(values, ","), ",")
|
||||
|
||||
// Get first valid left-most IP address
|
||||
for _, ip := range allValues {
|
||||
ip, _, _ = strings.Cut(strings.TrimSpace(ip), "%")
|
||||
ipAddr, err := netip.ParseAddr(ip)
|
||||
for _, part := range allValues {
|
||||
// Some proxies may retain the port number, so split if possible
|
||||
host, _, err := net.SplitHostPort(part)
|
||||
if err != nil {
|
||||
host = part
|
||||
}
|
||||
|
||||
// Remove any zone identifier from the IP address
|
||||
host, _, _ = strings.Cut(strings.TrimSpace(host), "%")
|
||||
|
||||
// Parse the IP address
|
||||
ipAddr, err := netip.ParseAddr(host)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -921,11 +941,20 @@ func trustedRealClientIP(r *http.Request, headers []string, clientIP string) str
|
||||
// remote address is returned.
|
||||
func strictUntrustedClientIp(r *http.Request, headers []string, trusted []netip.Prefix, clientIP string) string {
|
||||
for _, headerName := range headers {
|
||||
ips := strings.Split(strings.Join(r.Header.Values(headerName), ","), ",")
|
||||
parts := strings.Split(strings.Join(r.Header.Values(headerName), ","), ",")
|
||||
|
||||
for i := len(ips) - 1; i >= 0; i-- {
|
||||
ip, _, _ := strings.Cut(strings.TrimSpace(ips[i]), "%")
|
||||
ipAddr, err := netip.ParseAddr(ip)
|
||||
for i := len(parts) - 1; i >= 0; i-- {
|
||||
// Some proxies may retain the port number, so split if possible
|
||||
host, _, err := net.SplitHostPort(parts[i])
|
||||
if err != nil {
|
||||
host = parts[i]
|
||||
}
|
||||
|
||||
// Remove any zone identifier from the IP address
|
||||
host, _, _ = strings.Cut(strings.TrimSpace(host), "%")
|
||||
|
||||
// Parse the IP address
|
||||
ipAddr, err := netip.ParseAddr(host)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -981,6 +1010,10 @@ const (
|
||||
// For referencing underlying net.Conn
|
||||
ConnCtxKey caddy.CtxKey = "conn"
|
||||
|
||||
// For referencing underlying quic.Connection
|
||||
// TODO: export if needed later
|
||||
quicConnCtxKey caddy.CtxKey = "quic_conn"
|
||||
|
||||
// For tracking whether the client is a trusted proxy
|
||||
TrustedProxyVarKey string = "trusted_proxy"
|
||||
|
||||
|
||||
@@ -188,6 +188,15 @@ func TestServer_TrustedRealClientIP_OneTrustedHeaderValidArray(t *testing.T) {
|
||||
assert.Equal(t, ip, "1.1.1.1")
|
||||
}
|
||||
|
||||
func TestServer_TrustedRealClientIP_IncludesPort(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.RemoteAddr = "192.0.2.1:12345"
|
||||
req.Header.Set("X-Forwarded-For", "1.1.1.1:1234")
|
||||
ip := trustedRealClientIP(req, []string{"X-Forwarded-For"}, "192.0.2.1")
|
||||
|
||||
assert.Equal(t, ip, "1.1.1.1")
|
||||
}
|
||||
|
||||
func TestServer_TrustedRealClientIP_SkipsInvalidIps(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.RemoteAddr = "192.0.2.1:12345"
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
_ "github.com/caddyserver/caddy/v2/modules/caddyhttp/encode/zstd"
|
||||
_ "github.com/caddyserver/caddy/v2/modules/caddyhttp/fileserver"
|
||||
_ "github.com/caddyserver/caddy/v2/modules/caddyhttp/headers"
|
||||
_ "github.com/caddyserver/caddy/v2/modules/caddyhttp/logging"
|
||||
_ "github.com/caddyserver/caddy/v2/modules/caddyhttp/map"
|
||||
_ "github.com/caddyserver/caddy/v2/modules/caddyhttp/proxyprotocol"
|
||||
_ "github.com/caddyserver/caddy/v2/modules/caddyhttp/push"
|
||||
|
||||
@@ -329,7 +329,7 @@ type Templates struct {
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// Customfunctions is the interface for registering custom template functions.
|
||||
// CustomFunctions is the interface for registering custom template functions.
|
||||
type CustomFunctions interface {
|
||||
// CustomTemplateFunctions should return the mapping from custom function names to implementations.
|
||||
CustomTemplateFunctions() template.FuncMap
|
||||
|
||||
@@ -257,7 +257,7 @@ func (TemplateContext) funcEnv(varName string) string {
|
||||
return os.Getenv(varName)
|
||||
}
|
||||
|
||||
// Cookie gets the value of a cookie with name name.
|
||||
// Cookie gets the value of a cookie with name.
|
||||
func (c TemplateContext) Cookie(name string) string {
|
||||
cookies := c.Req.Cookies()
|
||||
for _, cookie := range cookies {
|
||||
|
||||
@@ -57,6 +57,12 @@ func (m VarsMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next H
|
||||
v = repl.ReplaceAll(valStr, "")
|
||||
}
|
||||
vars[keyExpanded] = v
|
||||
|
||||
// Special case: the user ID is in the replacer, pulled from there
|
||||
// for access logs. Allow users to override it with the vars handler.
|
||||
if keyExpanded == "http.auth.user.id" {
|
||||
repl.Set(keyExpanded, v)
|
||||
}
|
||||
}
|
||||
return next.ServeHTTP(w, r)
|
||||
}
|
||||
@@ -184,6 +190,8 @@ func (m VarsMatcher) Match(r *http.Request) bool {
|
||||
varStr = vv.String()
|
||||
case error:
|
||||
varStr = vv.Error()
|
||||
case nil:
|
||||
varStr = ""
|
||||
default:
|
||||
varStr = fmt.Sprintf("%v", vv)
|
||||
}
|
||||
@@ -275,6 +283,8 @@ func (m MatchVarsRE) Match(r *http.Request) bool {
|
||||
varStr = vv.String()
|
||||
case error:
|
||||
varStr = vv.Error()
|
||||
case nil:
|
||||
varStr = ""
|
||||
default:
|
||||
varStr = fmt.Sprintf("%v", vv)
|
||||
}
|
||||
|
||||
@@ -18,8 +18,10 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
@@ -29,6 +31,12 @@ func init() {
|
||||
caddy.RegisterModule(adminAPI{})
|
||||
}
|
||||
|
||||
var (
|
||||
caInfoPathPattern = regexp.MustCompile(`^ca/[^/]+$`)
|
||||
getCertPathPattern = regexp.MustCompile(`^ca/[^/]+/certificates$`)
|
||||
produceCSRPathPattern = regexp.MustCompile(`^ca/[^/]+/csr$`)
|
||||
)
|
||||
|
||||
// adminAPI is a module that serves PKI endpoints to retrieve
|
||||
// information about the CAs being managed by Caddy.
|
||||
type adminAPI struct {
|
||||
@@ -71,12 +79,13 @@ func (a *adminAPI) Routes() []caddy.AdminRoute {
|
||||
// handleAPIEndpoints routes API requests within adminPKIEndpointBase.
|
||||
func (a *adminAPI) handleAPIEndpoints(w http.ResponseWriter, r *http.Request) error {
|
||||
uri := strings.TrimPrefix(r.URL.Path, "/pki/")
|
||||
parts := strings.Split(uri, "/")
|
||||
switch {
|
||||
case len(parts) == 2 && parts[0] == "ca" && parts[1] != "":
|
||||
case caInfoPathPattern.MatchString(uri):
|
||||
return a.handleCAInfo(w, r)
|
||||
case len(parts) == 3 && parts[0] == "ca" && parts[1] != "" && parts[2] == "certificates":
|
||||
case getCertPathPattern.MatchString(uri):
|
||||
return a.handleCACerts(w, r)
|
||||
case produceCSRPathPattern.MatchString(uri):
|
||||
return a.handleCSRGeneration(w, r)
|
||||
}
|
||||
return caddy.APIError{
|
||||
HTTPStatus: http.StatusNotFound,
|
||||
@@ -168,6 +177,67 @@ func (a *adminAPI) handleCACerts(w http.ResponseWriter, r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *adminAPI) handleCSRGeneration(w http.ResponseWriter, r *http.Request) error {
|
||||
if r.Method != http.MethodPost {
|
||||
return caddy.APIError{
|
||||
HTTPStatus: http.StatusMethodNotAllowed,
|
||||
Err: fmt.Errorf("method not allowed: %v", r.Method),
|
||||
}
|
||||
}
|
||||
|
||||
ca, err := a.getCAFromAPIRequestPath(r)
|
||||
if err != nil {
|
||||
return caddy.APIError{
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the CSR request from the request body
|
||||
var csrReq csrRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&csrReq); err != nil {
|
||||
return caddy.APIError{
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
Err: fmt.Errorf("failed to decode CSR request: %v", err),
|
||||
}
|
||||
}
|
||||
csrReq.ID = strings.TrimSpace(csrReq.ID)
|
||||
if len(csrReq.ID) == 0 {
|
||||
csrReq.ID = uuid.New().String()
|
||||
}
|
||||
if err := csrReq.validate(); err != nil {
|
||||
return caddy.APIError{
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
Err: fmt.Errorf("invalid CSR request: %v", err),
|
||||
}
|
||||
}
|
||||
// Generate the CSR
|
||||
csr, err := ca.generateCSR(csrReq)
|
||||
if err != nil {
|
||||
return caddy.APIError{
|
||||
HTTPStatus: http.StatusInternalServerError,
|
||||
Err: fmt.Errorf("failed to generate CSR: %v", err),
|
||||
}
|
||||
}
|
||||
bs, err := pemEncode("CERTIFICATE REQUEST", csr.Raw)
|
||||
if err != nil {
|
||||
return caddy.APIError{
|
||||
HTTPStatus: http.StatusInternalServerError,
|
||||
Err: fmt.Errorf("failed to encode CSR to PEM: %v", err),
|
||||
}
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/pkcs10")
|
||||
w.Header().Set("content-disposition", fmt.Sprintf(`attachment; filename="%s.csr"`, csrReq.ID))
|
||||
|
||||
if _, err := w.Write(bs); err != nil {
|
||||
return caddy.APIError{
|
||||
HTTPStatus: http.StatusInternalServerError,
|
||||
Err: fmt.Errorf("failed to write CSR response: %v", err),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *adminAPI) getCAFromAPIRequestPath(r *http.Request) (*CA, error) {
|
||||
// Grab the CA ID from the request path, it should be the 4th segment (/pki/ca/<ca>)
|
||||
id := strings.Split(r.URL.Path, "/")[3]
|
||||
|
||||
@@ -16,7 +16,9 @@ package caddypki
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -29,6 +31,8 @@ import (
|
||||
"github.com/smallstep/certificates/authority"
|
||||
"github.com/smallstep/certificates/db"
|
||||
"github.com/smallstep/truststore"
|
||||
"go.step.sm/crypto/keyutil"
|
||||
"go.step.sm/crypto/x509util"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
@@ -394,6 +398,10 @@ func (ca CA) storageKeyIntermediateKey() string {
|
||||
return path.Join(ca.storageKeyCAPrefix(), "intermediate.key")
|
||||
}
|
||||
|
||||
func (ca CA) storageKeyCSRKey(id string) string {
|
||||
return path.Join(ca.storageKeyCAPrefix(), id+".csr.key")
|
||||
}
|
||||
|
||||
func (ca CA) newReplacer() *caddy.Replacer {
|
||||
repl := caddy.NewReplacer()
|
||||
repl.Set("pki.ca.name", ca.Name)
|
||||
@@ -421,6 +429,68 @@ func (ca CA) installRoot() error {
|
||||
)
|
||||
}
|
||||
|
||||
func (ca CA) generateCSR(csrReq csrRequest) (csr *x509.CertificateRequest, err error) {
|
||||
var signer crypto.Signer
|
||||
csrKeyPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyCSRKey(csrReq.ID))
|
||||
if err != nil {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return csr, fmt.Errorf("loading csr key '%s': %v", csrReq.ID, err)
|
||||
}
|
||||
if csrReq.Key == nil {
|
||||
signer, err = keyutil.GenerateDefaultSigner()
|
||||
if err != nil {
|
||||
return csr, err
|
||||
}
|
||||
} else {
|
||||
signer, err = keyutil.GenerateSigner(csrReq.Key.Type.String(), csrReq.Key.Curve.String(), csrReq.Key.Size)
|
||||
if err != nil {
|
||||
return csr, err
|
||||
}
|
||||
}
|
||||
|
||||
csrKeyPEM, err = certmagic.PEMEncodePrivateKey(signer)
|
||||
if err != nil {
|
||||
return csr, fmt.Errorf("encoding csr key: %v", err)
|
||||
}
|
||||
if err := ca.storage.Store(ca.ctx, ca.storageKeyCSRKey(csrReq.ID), csrKeyPEM); err != nil {
|
||||
return csr, fmt.Errorf("saving csr key: %v", err)
|
||||
}
|
||||
}
|
||||
if signer == nil {
|
||||
signer, err = certmagic.PEMDecodePrivateKey(csrKeyPEM)
|
||||
if err != nil {
|
||||
return csr, fmt.Errorf("decoding csr key: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var subject pkix.Name
|
||||
if csrReq.Request != nil && csrReq.Request.Subject != nil {
|
||||
subject = pkix.Name{
|
||||
Country: csrReq.Request.Subject.Country,
|
||||
Organization: csrReq.Request.Subject.Organization,
|
||||
OrganizationalUnit: csrReq.Request.Subject.OrganizationalUnit,
|
||||
Locality: csrReq.Request.Subject.Locality,
|
||||
Province: csrReq.Request.Subject.Province,
|
||||
StreetAddress: csrReq.Request.Subject.StreetAddress,
|
||||
PostalCode: csrReq.Request.Subject.PostalCode,
|
||||
CommonName: csrReq.Request.Subject.CommonName,
|
||||
}
|
||||
}
|
||||
dnsNames, ips, emails, uris := x509util.SplitSANs(csrReq.Request.SANs)
|
||||
|
||||
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{
|
||||
Subject: subject,
|
||||
DNSNames: dnsNames,
|
||||
IPAddresses: ips,
|
||||
EmailAddresses: emails,
|
||||
URIs: uris,
|
||||
}, signer)
|
||||
if err != nil {
|
||||
return csr, err
|
||||
}
|
||||
return x509.ParseCertificateRequest(csrBytes)
|
||||
}
|
||||
|
||||
// AuthorityConfig is used to help a CA configure
|
||||
// the underlying signing authority.
|
||||
type AuthorityConfig struct {
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
package caddypki
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// The key type to be used for signing the CSR. The possible types are:
|
||||
// EC, RSA, and OKP.
|
||||
type keyType string
|
||||
|
||||
const (
|
||||
keyTypeEC keyType = "EC"
|
||||
keyTypeRSA keyType = "RSA"
|
||||
keyTypeOKP keyType = "OKP"
|
||||
)
|
||||
|
||||
var stringToKey = map[string]keyType{
|
||||
"EC": keyTypeEC,
|
||||
"RSA": keyTypeRSA,
|
||||
"OKP": keyTypeOKP,
|
||||
}
|
||||
|
||||
func (kt *keyType) UnmarshalJSON(b []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
switch s {
|
||||
case string(keyTypeEC), string(keyTypeRSA), string(keyTypeOKP):
|
||||
*kt = stringToKey[s]
|
||||
default:
|
||||
return fmt.Errorf("unknown key type: %s", s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kt keyType) String() string {
|
||||
return string(kt)
|
||||
}
|
||||
|
||||
// The curve to use with key types EC and OKP.
|
||||
// If the Type is OKP, then acceptable curves are: Ed25519, or X25519
|
||||
// If the Type is EC, then acceptable curves are: P-256, P-384, or P-521
|
||||
type curve string
|
||||
|
||||
const (
|
||||
curveEd25519 curve = "Ed25519"
|
||||
curveX25519 curve = "X25519"
|
||||
curveP256 curve = "P-256"
|
||||
curveP384 curve = "P-384"
|
||||
curveP521 curve = "P-521"
|
||||
)
|
||||
|
||||
var stringToCurve = map[string]curve{
|
||||
"Ed25519": curveEd25519,
|
||||
"X25519": curveX25519,
|
||||
"P-256": curveP256,
|
||||
"P-384": curveP384,
|
||||
"P-521": curveP521,
|
||||
}
|
||||
|
||||
func (c *curve) UnmarshalJSON(b []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
switch s {
|
||||
case string(curveEd25519), string(curveX25519), string(curveP256), string(curveP384), string(curveP521):
|
||||
*c = stringToCurve[s]
|
||||
default:
|
||||
return fmt.Errorf("unknown curve: %s", s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c curve) String() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
type keyParameters struct {
|
||||
// The key type to be used for signing the CSR. The possible types are:
|
||||
// EC, RSA, and OKP.
|
||||
// The value of this field is case-sensitive.
|
||||
Type keyType `json:"type"`
|
||||
|
||||
// The curve to use with key types EC and OKP.
|
||||
// If the Type is OKP, then acceptable curves are: Ed25519, or X25519
|
||||
// If the Type is EC, then acceptable curves are: P-256, P-384, or P-521
|
||||
// The value of this field is case-sensitive.
|
||||
Curve curve `json:"curve,omitempty"`
|
||||
|
||||
// Only used with RSA keys and accepts minimum of 2048.
|
||||
Size int `json:"size,omitempty"`
|
||||
}
|
||||
|
||||
func (kp *keyParameters) validate() error {
|
||||
if kp == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if kp.Type == keyTypeRSA {
|
||||
if kp.Size < 2048 {
|
||||
return fmt.Errorf("minimum RSA key size is 2048 bits: %v", kp.Size)
|
||||
}
|
||||
}
|
||||
if kp.Type == keyTypeEC {
|
||||
switch kp.Curve {
|
||||
case curveP256, curveP384, curveP521:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unrecognized EC curve: %v", kp.Curve)
|
||||
}
|
||||
}
|
||||
if kp.Type == keyTypeOKP {
|
||||
switch kp.Curve {
|
||||
case curveEd25519, curveX25519:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unrecognized OKP curve: %v", kp.Curve)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type csrRequest struct {
|
||||
// Custom name assigned to the CSR key. If empty, UUID is generated and assigned.
|
||||
ID string `json:"id,omitempty"`
|
||||
|
||||
// Customization knobs of the generated/loaded key, if desired. The format is:
|
||||
// {
|
||||
// // Valid values for type are: EC, RSA, and OKP.
|
||||
// "type": "",
|
||||
//
|
||||
// // The curve to use with key types EC and OKP.
|
||||
// // If the Type is OKP, then acceptable curves are: Ed25519, or X25519
|
||||
// // If the Type is EC, then acceptable curves are: P-256, P-384, or P-521
|
||||
// "curve": "",
|
||||
//
|
||||
// // Only used with RSA keys and accepts minimum of 2048.
|
||||
// "size": 0
|
||||
// }
|
||||
//
|
||||
// If empty, sane defaults will be managed internally without exposing their details
|
||||
// to the user. At the moment, the default parameters are:
|
||||
// {
|
||||
// "type": "EC",
|
||||
// "curve": "P-256"
|
||||
// }
|
||||
// The values are case-sensitive.
|
||||
Key *keyParameters `json:"key,omitempty"`
|
||||
|
||||
Request *requestParameters `json:"request,omitempty"`
|
||||
}
|
||||
|
||||
func (c csrRequest) validate() error {
|
||||
if !c.Request.valid() {
|
||||
return errors.New("the 'request' field is not valid")
|
||||
}
|
||||
return c.Key.validate()
|
||||
}
|
||||
|
||||
type requestParameters struct {
|
||||
Subject *subject `json:"subject,omitempty"`
|
||||
|
||||
// SANs is a list of subject alternative names for the certificate.
|
||||
SANs []string `json:"sans,omitempty"`
|
||||
}
|
||||
|
||||
type subject struct {
|
||||
CommonName string `json:"cn,omitempty"`
|
||||
Country []string `json:"c,omitempty"`
|
||||
Organization []string `json:"o,omitempty"`
|
||||
OrganizationalUnit []string `json:"ou,omitempty"`
|
||||
Locality []string `json:"l,omitempty"`
|
||||
Province []string `json:"s,omitempty"`
|
||||
StreetAddress []string `json:"street_address,omitempty"`
|
||||
PostalCode []string `json:"postal_code,omitempty"`
|
||||
}
|
||||
|
||||
func (rp *requestParameters) valid() bool {
|
||||
if rp == nil || (len(rp.SANs) == 0 && rp.Subject == nil) {
|
||||
return false
|
||||
}
|
||||
if len(rp.SANs) > 0 {
|
||||
for _, san := range rp.SANs {
|
||||
if strings.TrimSpace(san) == "" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return rp.Subject == nil || (rp.Subject != nil && len(strings.TrimSpace(rp.Subject.CommonName)) > 0)
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
package caddypki
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseKeyType(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected keyType
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "uppercase EC is recognized",
|
||||
input: `"EC"`,
|
||||
expected: keyTypeEC,
|
||||
},
|
||||
{
|
||||
name: "lowercase EC is rejected",
|
||||
input: `"ec"`,
|
||||
err: "unknown key type: ec",
|
||||
},
|
||||
{
|
||||
name: "mixed case EC is rejected",
|
||||
input: `"eC"`,
|
||||
err: "unknown key type: eC",
|
||||
},
|
||||
{
|
||||
name: "uppercase RSA is recognized",
|
||||
input: `"RSA"`,
|
||||
expected: keyTypeRSA,
|
||||
},
|
||||
{
|
||||
name: "lowercase rsa is rejected",
|
||||
input: `"rsa"`,
|
||||
err: "unknown key type: rsa",
|
||||
},
|
||||
{
|
||||
name: "mixed case RSA is rejected",
|
||||
input: `"RsA"`,
|
||||
err: "unknown key type: RsA",
|
||||
},
|
||||
{
|
||||
name: "uppercase OKP is recognized",
|
||||
input: `"OKP"`,
|
||||
expected: keyTypeOKP,
|
||||
},
|
||||
{
|
||||
name: "lowercase OKP is rejected",
|
||||
input: `"okp"`,
|
||||
err: "unknown key type: okp",
|
||||
},
|
||||
{
|
||||
name: "mixed case OKP is rejected",
|
||||
input: `"OkP"`,
|
||||
err: "unknown key type: OkP",
|
||||
},
|
||||
{
|
||||
name: "unknown key type is rejected",
|
||||
input: `"foo"`,
|
||||
err: "unknown key type: foo",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var kt keyType
|
||||
|
||||
err := json.Unmarshal([]byte(test.input), &kt)
|
||||
if test.err != "" {
|
||||
if err == nil {
|
||||
t.Errorf("expected error %q, but got nil", test.err)
|
||||
}
|
||||
if err.Error() != test.err {
|
||||
t.Errorf("expected error %q, but got %q", test.err, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, but got %q", err.Error())
|
||||
return
|
||||
}
|
||||
if kt != test.expected {
|
||||
t.Errorf("expected %v, but got %v", test.expected, kt)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCSRKeyParameterValidate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key *keyParameters
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty request is valid",
|
||||
key: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "RSA with size 2048 is valid",
|
||||
key: &keyParameters{
|
||||
Type: keyTypeRSA,
|
||||
Size: 2048,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "RSA with size less than 2048 is invalid",
|
||||
key: &keyParameters{
|
||||
Type: keyTypeRSA,
|
||||
Size: 1024,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "EC key with curve P-256 is valid",
|
||||
key: &keyParameters{
|
||||
Type: keyTypeEC,
|
||||
Curve: "P-256",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "EC key with curve P-256 is valid",
|
||||
key: &keyParameters{
|
||||
Type: keyTypeEC,
|
||||
Curve: "P-256",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "EC key with curve P-384 is valid",
|
||||
key: &keyParameters{
|
||||
Type: keyTypeEC,
|
||||
Curve: "P-384",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "EC key with curve P-521 is valid",
|
||||
key: &keyParameters{
|
||||
Type: keyTypeEC,
|
||||
Curve: "P-521",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "EC key with unknown curve is invalid",
|
||||
key: &keyParameters{
|
||||
Type: keyTypeEC,
|
||||
Curve: "foo",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "EC key with Ed25519 curve is invalid",
|
||||
key: &keyParameters{
|
||||
Type: keyTypeEC,
|
||||
Curve: "Ed25519",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "EC key with X25519 curve is invalid",
|
||||
key: &keyParameters{
|
||||
Type: keyTypeEC,
|
||||
Curve: "X25519",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "OKP key with curve Ed25519 is valid",
|
||||
key: &keyParameters{
|
||||
Type: keyTypeOKP,
|
||||
Curve: "Ed25519",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "OKP key with curve X25519 is valid",
|
||||
key: &keyParameters{
|
||||
Type: keyTypeOKP,
|
||||
Curve: "X25519",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "OKP with unknown curve is invalid",
|
||||
key: &keyParameters{
|
||||
Type: keyTypeOKP,
|
||||
Curve: "foo",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "OKP key with curve P-256 is invalid",
|
||||
key: &keyParameters{
|
||||
Type: keyTypeOKP,
|
||||
Curve: "P-256",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "OKP key with curve P-384 is invalid",
|
||||
key: &keyParameters{
|
||||
Type: keyTypeOKP,
|
||||
Curve: "P-384",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "OKP key with curve P-521 is invalid",
|
||||
key: &keyParameters{
|
||||
Type: keyTypeOKP,
|
||||
Curve: "P-521",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := tt.key.validate(); (err != nil) != tt.wantErr {
|
||||
t.Errorf("keyParameter.validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCurve(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected curve
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "Ed25519 is recognized",
|
||||
input: `"Ed25519"`,
|
||||
expected: curveEd25519,
|
||||
},
|
||||
{
|
||||
name: "ed25519 is rejected",
|
||||
input: `"ed25519"`,
|
||||
err: "unknown curve: ed25519",
|
||||
},
|
||||
{
|
||||
name: "eD25519 is rejected",
|
||||
input: `"eD25519"`,
|
||||
err: "unknown curve: eD25519",
|
||||
},
|
||||
{
|
||||
name: "X25519 is recognized",
|
||||
input: `"X25519"`,
|
||||
expected: curveX25519,
|
||||
},
|
||||
{
|
||||
name: "x25519 is rejected",
|
||||
input: `"x25519"`,
|
||||
err: "unknown curve: x25519",
|
||||
},
|
||||
{
|
||||
name: "P-256 is recognized",
|
||||
input: `"P-256"`,
|
||||
expected: curveP256,
|
||||
},
|
||||
{
|
||||
name: "p-256 is rejected",
|
||||
input: `"p-256"`,
|
||||
err: "unknown curve: p-256",
|
||||
},
|
||||
|
||||
{
|
||||
name: "P-384 is recognized",
|
||||
input: `"P-384"`,
|
||||
expected: curveP384,
|
||||
},
|
||||
{
|
||||
name: "p-384 is rejected",
|
||||
input: `"p-384"`,
|
||||
err: "unknown curve: p-384",
|
||||
},
|
||||
|
||||
{
|
||||
name: "P-521 is recognized",
|
||||
input: `"P-521"`,
|
||||
expected: curveP521,
|
||||
},
|
||||
{
|
||||
name: "p-521 is rejected",
|
||||
input: `"p-521"`,
|
||||
err: "unknown curve: p-521",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var kt curve
|
||||
|
||||
err := json.Unmarshal([]byte(test.input), &kt)
|
||||
if test.err != "" {
|
||||
if err == nil {
|
||||
t.Errorf("expected error %q, but got nil", test.err)
|
||||
}
|
||||
if err.Error() != test.err {
|
||||
t.Errorf("expected error %q, but got %q", test.err, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, but got %q", err.Error())
|
||||
return
|
||||
}
|
||||
if kt != test.expected {
|
||||
t.Errorf("expected %v, but got %v", test.expected, kt)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestParametersValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
req *requestParameters
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "nil request is invalid",
|
||||
req: nil,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "empty request is invalid",
|
||||
req: &requestParameters{},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "request containing empty SAN value is invalid",
|
||||
req: &requestParameters{
|
||||
SANs: []string{"example.com", "", "foo.com"},
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "request with SANs is valid",
|
||||
req: &requestParameters{
|
||||
SANs: []string{"example.com"},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "request with non-empty CommonName is valid",
|
||||
req: &requestParameters{
|
||||
Subject: &subject{CommonName: "example.com"},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "request with empty-space CommonName is invalid",
|
||||
req: &requestParameters{
|
||||
Subject: &subject{CommonName: " "},
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.req.valid(); got != tt.want {
|
||||
t.Errorf("requestParameters.valid() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -379,7 +379,7 @@ type ClientAuthentication struct {
|
||||
|
||||
// DEPRECATED: This field is deprecated and will be removed in
|
||||
// a future version. Please use the `validators` field instead
|
||||
// with the tls.client_auth.leaf module instead.
|
||||
// with the tls.client_auth.verifier.leaf module instead.
|
||||
//
|
||||
// A list of base64 DER-encoded client leaf certs
|
||||
// to accept. If this list is not empty, client certs
|
||||
@@ -389,7 +389,7 @@ type ClientAuthentication struct {
|
||||
// Client certificate verification modules. These can perform
|
||||
// custom client authentication checks, such as ensuring the
|
||||
// certificate is not revoked.
|
||||
VerifiersRaw []json.RawMessage `json:"verifiers,omitempty" caddy:"namespace=tls.client_auth inline_key=verifier"`
|
||||
VerifiersRaw []json.RawMessage `json:"verifiers,omitempty" caddy:"namespace=tls.client_auth.verifier inline_key=verifier"`
|
||||
|
||||
verifiers []ClientCertificateVerifier
|
||||
|
||||
@@ -494,6 +494,23 @@ func (ca *ClientAuthentication) UnmarshalCaddyfile(d *caddyfile.Dispenser) error
|
||||
return fmt.Errorf("trust_pool module '%s' is not a certificate pool provider", caMod)
|
||||
}
|
||||
ca.CARaw = caddyconfig.JSONModuleObject(caMod, "provider", modName, nil)
|
||||
case "verifier":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
|
||||
vType := d.Val()
|
||||
modID := "tls.client_auth.verifier." + vType
|
||||
unm, err := caddyfile.UnmarshalModule(d, modID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, ok := unm.(ClientCertificateVerifier)
|
||||
if !ok {
|
||||
return d.Errf("module '%s' is not a caddytls.ClientCertificatVerifier", modID)
|
||||
}
|
||||
ca.VerifiersRaw = append(ca.VerifiersRaw, caddyconfig.JSONModuleObject(unm, "verifier", vType, nil))
|
||||
default:
|
||||
return d.Errf("unknown subdirective for client_auth: %s", subdir)
|
||||
}
|
||||
@@ -566,7 +583,7 @@ func (clientauth *ClientAuthentication) provision(ctx caddy.Context) error {
|
||||
}
|
||||
ca, ok := caRaw.(CA)
|
||||
if !ok {
|
||||
return fmt.Errorf("CARaw module '%s' is not a certificate pool provider", ca)
|
||||
return fmt.Errorf("'ca' module '%s' is not a certificate pool provider", ca)
|
||||
}
|
||||
clientauth.ca = ca
|
||||
|
||||
@@ -634,7 +651,7 @@ func (clientauth *ClientAuthentication) ConfigureTLSConfig(cfg *tls.Config) erro
|
||||
}
|
||||
trustedLeafCerts = append(trustedLeafCerts, clientCert)
|
||||
}
|
||||
clientauth.verifiers = append(clientauth.verifiers, LeafCertClientAuth{TrustedLeafCerts: trustedLeafCerts})
|
||||
clientauth.verifiers = append(clientauth.verifiers, LeafCertClientAuth{trustedLeafCerts: trustedLeafCerts})
|
||||
}
|
||||
|
||||
// if a custom verification function already exists, wrap it
|
||||
@@ -698,17 +715,42 @@ func setDefaultTLSParams(cfg *tls.Config) {
|
||||
|
||||
// LeafCertClientAuth verifies the client's leaf certificate.
|
||||
type LeafCertClientAuth struct {
|
||||
TrustedLeafCerts []*x509.Certificate
|
||||
LeafCertificateLoadersRaw []json.RawMessage `json:"leaf_certs_loaders,omitempty" caddy:"namespace=tls.leaf_cert_loader inline_key=loader"`
|
||||
trustedLeafCerts []*x509.Certificate
|
||||
}
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
func (LeafCertClientAuth) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: "tls.client_auth.leaf",
|
||||
ID: "tls.client_auth.verifier.leaf",
|
||||
New: func() caddy.Module { return new(LeafCertClientAuth) },
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LeafCertClientAuth) Provision(ctx caddy.Context) error {
|
||||
if l.LeafCertificateLoadersRaw == nil {
|
||||
return nil
|
||||
}
|
||||
val, err := ctx.LoadModule(l, "LeafCertificateLoadersRaw")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse leaf certificates loaders: %s", err.Error())
|
||||
}
|
||||
trustedLeafCertloaders := []LeafCertificateLoader{}
|
||||
for _, loader := range val.([]any) {
|
||||
trustedLeafCertloaders = append(trustedLeafCertloaders, loader.(LeafCertificateLoader))
|
||||
}
|
||||
trustedLeafCertificates := []*x509.Certificate{}
|
||||
for _, loader := range trustedLeafCertloaders {
|
||||
certs, err := loader.LoadLeafCertificates()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not load leaf certificates: %s", err.Error())
|
||||
}
|
||||
trustedLeafCertificates = append(trustedLeafCertificates, certs...)
|
||||
}
|
||||
l.trustedLeafCerts = trustedLeafCertificates
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l LeafCertClientAuth) VerifyClientCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error {
|
||||
if len(rawCerts) == 0 {
|
||||
return fmt.Errorf("no client certificate provided")
|
||||
@@ -719,7 +761,7 @@ func (l LeafCertClientAuth) VerifyClientCertificate(rawCerts [][]byte, _ [][]*x5
|
||||
return fmt.Errorf("can't parse the given certificate: %s", err.Error())
|
||||
}
|
||||
|
||||
for _, trustedLeafCert := range l.TrustedLeafCerts {
|
||||
for _, trustedLeafCert := range l.trustedLeafCerts {
|
||||
if remoteLeafCert.Equal(trustedLeafCert) {
|
||||
return nil
|
||||
}
|
||||
@@ -748,6 +790,12 @@ type ConnectionMatcher interface {
|
||||
Match(*tls.ClientHelloInfo) bool
|
||||
}
|
||||
|
||||
// LeafCertificateLoader is a type that loads the trusted leaf certificates
|
||||
// for the tls.leaf_cert_loader modules
|
||||
type LeafCertificateLoader interface {
|
||||
LoadLeafCertificates() ([]*x509.Certificate, error)
|
||||
}
|
||||
|
||||
// ClientCertificateVerifier is a type which verifies client certificates.
|
||||
// It is called during verifyPeerCertificate in the TLS handshake.
|
||||
type ClientCertificateVerifier interface {
|
||||
|
||||
@@ -145,7 +145,7 @@ func TestClientAuthenticationUnmarshalCaddyfileWithDirectiveName(t *testing.T) {
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "using 'trusted_ca_cert' adapts sucessfully",
|
||||
name: "using 'trusted_ca_cert' adapts successfully",
|
||||
args: args{
|
||||
d: caddyfile.NewTestDispenser(fmt.Sprintf(`
|
||||
client_auth {
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright 2015 Matthew Holt and The Caddy Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package caddytls
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterModule(LeafFileLoader{})
|
||||
}
|
||||
|
||||
// LeafFileLoader loads leaf certificates from disk.
|
||||
type LeafFileLoader struct {
|
||||
Files []string `json:"files,omitempty"`
|
||||
}
|
||||
|
||||
// Provision implements caddy.Provisioner.
|
||||
func (fl *LeafFileLoader) Provision(ctx caddy.Context) error {
|
||||
repl, ok := ctx.Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||
if !ok {
|
||||
repl = caddy.NewReplacer()
|
||||
}
|
||||
for k, path := range fl.Files {
|
||||
fl.Files[k] = repl.ReplaceKnown(path, "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
func (LeafFileLoader) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: "tls.leaf_cert_loader.file",
|
||||
New: func() caddy.Module { return new(LeafFileLoader) },
|
||||
}
|
||||
}
|
||||
|
||||
// LoadLeafCertificates returns the certificates to be loaded by fl.
|
||||
func (fl LeafFileLoader) LoadLeafCertificates() ([]*x509.Certificate, error) {
|
||||
certificates := make([]*x509.Certificate, 0, len(fl.Files))
|
||||
for _, path := range fl.Files {
|
||||
ders, err := convertPEMFilesToDERBytes(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certs, err := x509.ParseCertificates(ders)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certificates = append(certificates, certs...)
|
||||
}
|
||||
return certificates, nil
|
||||
}
|
||||
|
||||
func convertPEMFilesToDERBytes(filename string) ([]byte, error) {
|
||||
certDataPEM, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ders []byte
|
||||
// while block is not nil, we have more certificates in the file
|
||||
for block, rest := pem.Decode(certDataPEM); block != nil; block, rest = pem.Decode(rest) {
|
||||
if block.Type != "CERTIFICATE" {
|
||||
return nil, fmt.Errorf("no CERTIFICATE pem block found in %s", filename)
|
||||
}
|
||||
ders = append(
|
||||
ders,
|
||||
block.Bytes...,
|
||||
)
|
||||
}
|
||||
// if we decoded nothing, return an error
|
||||
if len(ders) == 0 {
|
||||
return nil, fmt.Errorf("no CERTIFICATE pem block found in %s", filename)
|
||||
}
|
||||
return ders, nil
|
||||
}
|
||||
|
||||
// Interface guard
|
||||
var (
|
||||
_ LeafCertificateLoader = (*LeafFileLoader)(nil)
|
||||
_ caddy.Provisioner = (*LeafFileLoader)(nil)
|
||||
)
|
||||
@@ -0,0 +1,38 @@
|
||||
package caddytls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/pem"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
)
|
||||
|
||||
func TestLeafFileLoader(t *testing.T) {
|
||||
fl := LeafFileLoader{Files: []string{"../../caddytest/leafcert.pem"}}
|
||||
fl.Provision(caddy.Context{Context: context.Background()})
|
||||
|
||||
out, err := fl.LoadLeafCertificates()
|
||||
if err != nil {
|
||||
t.Errorf("Leaf certs file loading test failed: %v", err)
|
||||
}
|
||||
if len(out) != 1 {
|
||||
t.Errorf("Error loading leaf cert in memory struct")
|
||||
return
|
||||
}
|
||||
pemBytes := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: out[0].Raw})
|
||||
|
||||
pemFileBytes, err := os.ReadFile("../../caddytest/leafcert.pem")
|
||||
if err != nil {
|
||||
t.Errorf("Unable to read the example certificate from the file")
|
||||
}
|
||||
|
||||
// Remove /r because windows.
|
||||
pemFileString := strings.ReplaceAll(string(pemFileBytes), "\r\n", "\n")
|
||||
|
||||
if string(pemBytes) != pemFileString {
|
||||
t.Errorf("Leaf Certificate File Loader: Failed to load the correct certificate")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright 2015 Matthew Holt and The Caddy Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package caddytls
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterModule(LeafFolderLoader{})
|
||||
}
|
||||
|
||||
// LeafFolderLoader loads certificates and their associated keys from disk
|
||||
// by recursively walking the specified directories, looking for PEM
|
||||
// files which contain both a certificate and a key.
|
||||
type LeafFolderLoader struct {
|
||||
Folders []string `json:"folders,omitempty"`
|
||||
}
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
func (LeafFolderLoader) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: "tls.leaf_cert_loader.folder",
|
||||
New: func() caddy.Module { return new(LeafFolderLoader) },
|
||||
}
|
||||
}
|
||||
|
||||
// Provision implements caddy.Provisioner.
|
||||
func (fl *LeafFolderLoader) Provision(ctx caddy.Context) error {
|
||||
repl, ok := ctx.Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||
if !ok {
|
||||
repl = caddy.NewReplacer()
|
||||
}
|
||||
for k, path := range fl.Folders {
|
||||
fl.Folders[k] = repl.ReplaceKnown(path, "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadLeafCertificates loads all the leaf certificates in the directories
|
||||
// listed in fl from all files ending with .pem.
|
||||
func (fl LeafFolderLoader) LoadLeafCertificates() ([]*x509.Certificate, error) {
|
||||
var certs []*x509.Certificate
|
||||
for _, dir := range fl.Folders {
|
||||
err := filepath.Walk(dir, func(fpath string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to traverse into path: %s", fpath)
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if !strings.HasSuffix(strings.ToLower(info.Name()), ".pem") {
|
||||
return nil
|
||||
}
|
||||
|
||||
certData, err := convertPEMFilesToDERBytes(fpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cert, err := x509.ParseCertificate(certData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", fpath, err)
|
||||
}
|
||||
|
||||
certs = append(certs, cert)
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
var (
|
||||
_ LeafCertificateLoader = (*LeafFolderLoader)(nil)
|
||||
_ caddy.Provisioner = (*LeafFolderLoader)(nil)
|
||||
)
|
||||
@@ -0,0 +1,37 @@
|
||||
package caddytls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/pem"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
)
|
||||
|
||||
func TestLeafFolderLoader(t *testing.T) {
|
||||
fl := LeafFolderLoader{Folders: []string{"../../caddytest"}}
|
||||
fl.Provision(caddy.Context{Context: context.Background()})
|
||||
|
||||
out, err := fl.LoadLeafCertificates()
|
||||
if err != nil {
|
||||
t.Errorf("Leaf certs folder loading test failed: %v", err)
|
||||
}
|
||||
if len(out) != 1 {
|
||||
t.Errorf("Error loading leaf cert in memory struct")
|
||||
return
|
||||
}
|
||||
pemBytes := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: out[0].Raw})
|
||||
pemFileBytes, err := os.ReadFile("../../caddytest/leafcert.pem")
|
||||
if err != nil {
|
||||
t.Errorf("Unable to read the example certificate from the file")
|
||||
}
|
||||
|
||||
// Remove /r because windows.
|
||||
pemFileString := strings.ReplaceAll(string(pemFileBytes), "\r\n", "\n")
|
||||
|
||||
if string(pemBytes) != pemFileString {
|
||||
t.Errorf("Leaf Certificate Folder Loader: Failed to load the correct certificate")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright 2015 Matthew Holt and The Caddy Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package caddytls
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterModule(LeafPEMLoader{})
|
||||
}
|
||||
|
||||
// LeafPEMLoader loads leaf certificates by
|
||||
// decoding their PEM blocks directly. This has the advantage
|
||||
// of not needing to store them on disk at all.
|
||||
type LeafPEMLoader struct {
|
||||
Certificates []string `json:"certificates,omitempty"`
|
||||
}
|
||||
|
||||
// Provision implements caddy.Provisioner.
|
||||
func (pl *LeafPEMLoader) Provision(ctx caddy.Context) error {
|
||||
repl, ok := ctx.Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||
if !ok {
|
||||
repl = caddy.NewReplacer()
|
||||
}
|
||||
for i, cert := range pl.Certificates {
|
||||
pl.Certificates[i] = repl.ReplaceKnown(cert, "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
func (LeafPEMLoader) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: "tls.leaf_cert_loader.pem",
|
||||
New: func() caddy.Module { return new(LeafPEMLoader) },
|
||||
}
|
||||
}
|
||||
|
||||
// LoadLeafCertificates returns the certificates contained in pl.
|
||||
func (pl LeafPEMLoader) LoadLeafCertificates() ([]*x509.Certificate, error) {
|
||||
certs := make([]*x509.Certificate, 0, len(pl.Certificates))
|
||||
for i, cert := range pl.Certificates {
|
||||
derBytes, err := convertPEMToDER([]byte(cert))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("PEM leaf certificate loader, cert %d: %v", i, err)
|
||||
}
|
||||
cert, err := x509.ParseCertificate(derBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("PEM cert %d: %v", i, err)
|
||||
}
|
||||
certs = append(certs, cert)
|
||||
}
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
// Interface guard
|
||||
var (
|
||||
_ LeafCertificateLoader = (*LeafPEMLoader)(nil)
|
||||
_ caddy.Provisioner = (*LeafPEMLoader)(nil)
|
||||
)
|
||||
@@ -0,0 +1,54 @@
|
||||
package caddytls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/pem"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
)
|
||||
|
||||
func TestLeafPEMLoader(t *testing.T) {
|
||||
pl := LeafPEMLoader{Certificates: []string{`
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICUTCCAfugAwIBAgIBADANBgkqhkiG9w0BAQQFADBXMQswCQYDVQQGEwJDTjEL
|
||||
MAkGA1UECBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMC
|
||||
VU4xFDASBgNVBAMTC0hlcm9uZyBZYW5nMB4XDTA1MDcxNTIxMTk0N1oXDTA1MDgx
|
||||
NDIxMTk0N1owVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlBOMQswCQYDVQQHEwJD
|
||||
TjELMAkGA1UEChMCT04xCzAJBgNVBAsTAlVOMRQwEgYDVQQDEwtIZXJvbmcgWWFu
|
||||
ZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCp5hnG7ogBhtlynpOS21cBewKE/B7j
|
||||
V14qeyslnr26xZUsSVko36ZnhiaO/zbMOoRcKK9vEcgMtcLFuQTWDl3RAgMBAAGj
|
||||
gbEwga4wHQYDVR0OBBYEFFXI70krXeQDxZgbaCQoR4jUDncEMH8GA1UdIwR4MHaA
|
||||
FFXI70krXeQDxZgbaCQoR4jUDncEoVukWTBXMQswCQYDVQQGEwJDTjELMAkGA1UE
|
||||
CBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMCVU4xFDAS
|
||||
BgNVBAMTC0hlcm9uZyBZYW5nggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEE
|
||||
BQADQQA/ugzBrjjK9jcWnDVfGHlk3icNRq0oV7Ri32z/+HQX67aRfgZu7KWdI+Ju
|
||||
Wm7DCfrPNGVwFWUQOmsPue9rZBgO
|
||||
-----END CERTIFICATE-----
|
||||
`}}
|
||||
pl.Provision(caddy.Context{Context: context.Background()})
|
||||
|
||||
out, err := pl.LoadLeafCertificates()
|
||||
if err != nil {
|
||||
t.Errorf("Leaf certs pem loading test failed: %v", err)
|
||||
}
|
||||
if len(out) != 1 {
|
||||
t.Errorf("Error loading leaf cert in memory struct")
|
||||
return
|
||||
}
|
||||
pemBytes := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: out[0].Raw})
|
||||
|
||||
pemFileBytes, err := os.ReadFile("../../caddytest/leafcert.pem")
|
||||
if err != nil {
|
||||
t.Errorf("Unable to read the example certificate from the file")
|
||||
}
|
||||
|
||||
// Remove /r because windows.
|
||||
pemFileString := strings.ReplaceAll(string(pemFileBytes), "\r\n", "\n")
|
||||
|
||||
if string(pemBytes) != pemFileString {
|
||||
t.Errorf("Leaf Certificate Folder Loader: Failed to load the correct certificate")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
// Copyright 2015 Matthew Holt and The Caddy Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package caddytls
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterModule(LeafStorageLoader{})
|
||||
}
|
||||
|
||||
// LeafStorageLoader loads leaf certificates from the
|
||||
// globally configured storage module.
|
||||
type LeafStorageLoader struct {
|
||||
// A list of certificate file names to be loaded from storage.
|
||||
Certificates []string `json:"certificates,omitempty"`
|
||||
|
||||
// The storage module where the trusted leaf certificates are stored. Absent
|
||||
// explicit storage implies the use of Caddy default storage.
|
||||
StorageRaw json.RawMessage `json:"storage,omitempty" caddy:"namespace=caddy.storage inline_key=module"`
|
||||
|
||||
// Reference to the globally configured storage module.
|
||||
storage certmagic.Storage
|
||||
|
||||
ctx caddy.Context
|
||||
}
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
func (LeafStorageLoader) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: "tls.leaf_cert_loader.storage",
|
||||
New: func() caddy.Module { return new(LeafStorageLoader) },
|
||||
}
|
||||
}
|
||||
|
||||
// Provision loads the storage module for sl.
|
||||
func (sl *LeafStorageLoader) Provision(ctx caddy.Context) error {
|
||||
if sl.StorageRaw != nil {
|
||||
val, err := ctx.LoadModule(sl, "StorageRaw")
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading storage module: %v", err)
|
||||
}
|
||||
cmStorage, err := val.(caddy.StorageConverter).CertMagicStorage()
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating storage configuration: %v", err)
|
||||
}
|
||||
sl.storage = cmStorage
|
||||
}
|
||||
if sl.storage == nil {
|
||||
sl.storage = ctx.Storage()
|
||||
}
|
||||
sl.ctx = ctx
|
||||
|
||||
repl, ok := ctx.Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||
if !ok {
|
||||
repl = caddy.NewReplacer()
|
||||
}
|
||||
for k, path := range sl.Certificates {
|
||||
sl.Certificates[k] = repl.ReplaceKnown(path, "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadLeafCertificates returns the certificates to be loaded by sl.
|
||||
func (sl LeafStorageLoader) LoadLeafCertificates() ([]*x509.Certificate, error) {
|
||||
certificates := make([]*x509.Certificate, 0, len(sl.Certificates))
|
||||
for _, path := range sl.Certificates {
|
||||
certData, err := sl.storage.Load(sl.ctx, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ders, err := convertPEMToDER(certData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certs, err := x509.ParseCertificates(ders)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certificates = append(certificates, certs...)
|
||||
}
|
||||
return certificates, nil
|
||||
}
|
||||
|
||||
func convertPEMToDER(pemData []byte) ([]byte, error) {
|
||||
var ders []byte
|
||||
// while block is not nil, we have more certificates in the file
|
||||
for block, rest := pem.Decode(pemData); block != nil; block, rest = pem.Decode(rest) {
|
||||
if block.Type != "CERTIFICATE" {
|
||||
return nil, fmt.Errorf("no CERTIFICATE pem block found in the given pem data")
|
||||
}
|
||||
ders = append(
|
||||
ders,
|
||||
block.Bytes...,
|
||||
)
|
||||
}
|
||||
// if we decoded nothing, return an error
|
||||
if len(ders) == 0 {
|
||||
return nil, fmt.Errorf("no CERTIFICATE pem block found in the given pem data")
|
||||
}
|
||||
return ders, nil
|
||||
}
|
||||
|
||||
// Interface guard
|
||||
var (
|
||||
_ LeafCertificateLoader = (*LeafStorageLoader)(nil)
|
||||
_ caddy.Provisioner = (*LeafStorageLoader)(nil)
|
||||
)
|
||||
@@ -0,0 +1,357 @@
|
||||
// Copyright 2015 Matthew Holt and The Caddy Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package logging
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/buffer"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"golang.org/x/term"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterModule(AppendEncoder{})
|
||||
}
|
||||
|
||||
// AppendEncoder can be used to add fields to all log entries
|
||||
// that pass through it. It is a wrapper around another
|
||||
// encoder, which it uses to actually encode the log entries.
|
||||
// It is most useful for adding information about the Caddy
|
||||
// instance that is producing the log entries, possibly via
|
||||
// an environment variable.
|
||||
type AppendEncoder struct {
|
||||
// The underlying encoder that actually encodes the
|
||||
// log entries. If not specified, defaults to "json",
|
||||
// unless the output is a terminal, in which case
|
||||
// it defaults to "console".
|
||||
WrappedRaw json.RawMessage `json:"wrap,omitempty" caddy:"namespace=caddy.logging.encoders inline_key=format"`
|
||||
|
||||
// A map of field names to their values. The values
|
||||
// can be global placeholders (e.g. env vars), or constants.
|
||||
// Note that the encoder does not run as part of an HTTP
|
||||
// request context, so request placeholders are not available.
|
||||
Fields map[string]any `json:"fields,omitempty"`
|
||||
|
||||
wrapped zapcore.Encoder
|
||||
repl *caddy.Replacer
|
||||
|
||||
wrappedIsDefault bool
|
||||
ctx caddy.Context
|
||||
}
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
func (AppendEncoder) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: "caddy.logging.encoders.append",
|
||||
New: func() caddy.Module { return new(AppendEncoder) },
|
||||
}
|
||||
}
|
||||
|
||||
// Provision sets up the encoder.
|
||||
func (fe *AppendEncoder) Provision(ctx caddy.Context) error {
|
||||
fe.ctx = ctx
|
||||
fe.repl = caddy.NewReplacer()
|
||||
|
||||
if fe.WrappedRaw == nil {
|
||||
// if wrap is not specified, default to JSON
|
||||
fe.wrapped = &JSONEncoder{}
|
||||
if p, ok := fe.wrapped.(caddy.Provisioner); ok {
|
||||
if err := p.Provision(ctx); err != nil {
|
||||
return fmt.Errorf("provisioning fallback encoder module: %v", err)
|
||||
}
|
||||
}
|
||||
fe.wrappedIsDefault = true
|
||||
} else {
|
||||
// set up wrapped encoder
|
||||
val, err := ctx.LoadModule(fe, "WrappedRaw")
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading fallback encoder module: %v", err)
|
||||
}
|
||||
fe.wrapped = val.(zapcore.Encoder)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigureDefaultFormat will set the default format to "console"
|
||||
// if the writer is a terminal. If already configured, it passes
|
||||
// through the writer so a deeply nested encoder can configure
|
||||
// its own default format.
|
||||
func (fe *AppendEncoder) ConfigureDefaultFormat(wo caddy.WriterOpener) error {
|
||||
if !fe.wrappedIsDefault {
|
||||
if cfd, ok := fe.wrapped.(caddy.ConfiguresFormatterDefault); ok {
|
||||
return cfd.ConfigureDefaultFormat(wo)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if caddy.IsWriterStandardStream(wo) && term.IsTerminal(int(os.Stderr.Fd())) {
|
||||
fe.wrapped = &ConsoleEncoder{}
|
||||
if p, ok := fe.wrapped.(caddy.Provisioner); ok {
|
||||
if err := p.Provision(fe.ctx); err != nil {
|
||||
return fmt.Errorf("provisioning fallback encoder module: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. Syntax:
|
||||
//
|
||||
// append {
|
||||
// wrap <another encoder>
|
||||
// fields {
|
||||
// <field> <value>
|
||||
// }
|
||||
// <field> <value>
|
||||
// }
|
||||
func (fe *AppendEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
d.Next() // consume encoder name
|
||||
|
||||
// parse a field
|
||||
parseField := func() error {
|
||||
if fe.Fields == nil {
|
||||
fe.Fields = make(map[string]any)
|
||||
}
|
||||
field := d.Val()
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
fe.Fields[field] = d.ScalarVal()
|
||||
if d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for d.NextBlock(0) {
|
||||
switch d.Val() {
|
||||
case "wrap":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
moduleName := d.Val()
|
||||
moduleID := "caddy.logging.encoders." + moduleName
|
||||
unm, err := caddyfile.UnmarshalModule(d, moduleID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
enc, ok := unm.(zapcore.Encoder)
|
||||
if !ok {
|
||||
return d.Errf("module %s (%T) is not a zapcore.Encoder", moduleID, unm)
|
||||
}
|
||||
fe.WrappedRaw = caddyconfig.JSONModuleObject(enc, "format", moduleName, nil)
|
||||
|
||||
case "fields":
|
||||
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||
err := parseField()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
// if unknown, assume it's a field so that
|
||||
// the config can be flat
|
||||
err := parseField()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddArray is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error {
|
||||
return fe.wrapped.AddArray(key, marshaler)
|
||||
}
|
||||
|
||||
// AddObject is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddObject(key string, marshaler zapcore.ObjectMarshaler) error {
|
||||
return fe.wrapped.AddObject(key, marshaler)
|
||||
}
|
||||
|
||||
// AddBinary is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddBinary(key string, value []byte) {
|
||||
fe.wrapped.AddBinary(key, value)
|
||||
}
|
||||
|
||||
// AddByteString is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddByteString(key string, value []byte) {
|
||||
fe.wrapped.AddByteString(key, value)
|
||||
}
|
||||
|
||||
// AddBool is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddBool(key string, value bool) {
|
||||
fe.wrapped.AddBool(key, value)
|
||||
}
|
||||
|
||||
// AddComplex128 is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddComplex128(key string, value complex128) {
|
||||
fe.wrapped.AddComplex128(key, value)
|
||||
}
|
||||
|
||||
// AddComplex64 is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddComplex64(key string, value complex64) {
|
||||
fe.wrapped.AddComplex64(key, value)
|
||||
}
|
||||
|
||||
// AddDuration is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddDuration(key string, value time.Duration) {
|
||||
fe.wrapped.AddDuration(key, value)
|
||||
}
|
||||
|
||||
// AddFloat64 is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddFloat64(key string, value float64) {
|
||||
fe.wrapped.AddFloat64(key, value)
|
||||
}
|
||||
|
||||
// AddFloat32 is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddFloat32(key string, value float32) {
|
||||
fe.wrapped.AddFloat32(key, value)
|
||||
}
|
||||
|
||||
// AddInt is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddInt(key string, value int) {
|
||||
fe.wrapped.AddInt(key, value)
|
||||
}
|
||||
|
||||
// AddInt64 is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddInt64(key string, value int64) {
|
||||
fe.wrapped.AddInt64(key, value)
|
||||
}
|
||||
|
||||
// AddInt32 is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddInt32(key string, value int32) {
|
||||
fe.wrapped.AddInt32(key, value)
|
||||
}
|
||||
|
||||
// AddInt16 is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddInt16(key string, value int16) {
|
||||
fe.wrapped.AddInt16(key, value)
|
||||
}
|
||||
|
||||
// AddInt8 is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddInt8(key string, value int8) {
|
||||
fe.wrapped.AddInt8(key, value)
|
||||
}
|
||||
|
||||
// AddString is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddString(key, value string) {
|
||||
fe.wrapped.AddString(key, value)
|
||||
}
|
||||
|
||||
// AddTime is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddTime(key string, value time.Time) {
|
||||
fe.wrapped.AddTime(key, value)
|
||||
}
|
||||
|
||||
// AddUint is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddUint(key string, value uint) {
|
||||
fe.wrapped.AddUint(key, value)
|
||||
}
|
||||
|
||||
// AddUint64 is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddUint64(key string, value uint64) {
|
||||
fe.wrapped.AddUint64(key, value)
|
||||
}
|
||||
|
||||
// AddUint32 is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddUint32(key string, value uint32) {
|
||||
fe.wrapped.AddUint32(key, value)
|
||||
}
|
||||
|
||||
// AddUint16 is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddUint16(key string, value uint16) {
|
||||
fe.wrapped.AddUint16(key, value)
|
||||
}
|
||||
|
||||
// AddUint8 is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddUint8(key string, value uint8) {
|
||||
fe.wrapped.AddUint8(key, value)
|
||||
}
|
||||
|
||||
// AddUintptr is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddUintptr(key string, value uintptr) {
|
||||
fe.wrapped.AddUintptr(key, value)
|
||||
}
|
||||
|
||||
// AddReflected is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) AddReflected(key string, value any) error {
|
||||
return fe.wrapped.AddReflected(key, value)
|
||||
}
|
||||
|
||||
// OpenNamespace is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) OpenNamespace(key string) {
|
||||
fe.wrapped.OpenNamespace(key)
|
||||
}
|
||||
|
||||
// Clone is part of the zapcore.ObjectEncoder interface.
|
||||
func (fe AppendEncoder) Clone() zapcore.Encoder {
|
||||
return AppendEncoder{
|
||||
Fields: fe.Fields,
|
||||
wrapped: fe.wrapped.Clone(),
|
||||
repl: fe.repl,
|
||||
}
|
||||
}
|
||||
|
||||
// EncodeEntry partially implements the zapcore.Encoder interface.
|
||||
func (fe AppendEncoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
|
||||
fe.wrapped = fe.wrapped.Clone()
|
||||
for _, field := range fields {
|
||||
field.AddTo(fe)
|
||||
}
|
||||
|
||||
// append fields from config
|
||||
for key, value := range fe.Fields {
|
||||
// if the value is a string
|
||||
if str, ok := value.(string); ok {
|
||||
isPlaceholder := strings.HasPrefix(str, "{") &&
|
||||
strings.HasSuffix(str, "}") &&
|
||||
strings.Count(str, "{") == 1
|
||||
if isPlaceholder {
|
||||
// and it looks like a placeholder, evaluate it
|
||||
replaced, _ := fe.repl.Get(strings.Trim(str, "{}"))
|
||||
zap.Any(key, replaced).AddTo(fe)
|
||||
} else {
|
||||
// just use the string as-is
|
||||
zap.String(key, str).AddTo(fe)
|
||||
}
|
||||
} else {
|
||||
// not a string, so use the value as any
|
||||
zap.Any(key, value).AddTo(fe)
|
||||
}
|
||||
}
|
||||
|
||||
return fe.wrapped.EncodeEntry(ent, nil)
|
||||
}
|
||||
|
||||
// Interface guards
|
||||
var (
|
||||
_ zapcore.Encoder = (*AppendEncoder)(nil)
|
||||
_ caddyfile.Unmarshaler = (*AppendEncoder)(nil)
|
||||
_ caddy.ConfiguresFormatterDefault = (*AppendEncoder)(nil)
|
||||
)
|
||||
+26
-13
@@ -118,17 +118,26 @@ func (je *JSONEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
|
||||
// LogEncoderConfig holds configuration common to most encoders.
|
||||
type LogEncoderConfig struct {
|
||||
MessageKey *string `json:"message_key,omitempty"`
|
||||
LevelKey *string `json:"level_key,omitempty"`
|
||||
TimeKey *string `json:"time_key,omitempty"`
|
||||
NameKey *string `json:"name_key,omitempty"`
|
||||
CallerKey *string `json:"caller_key,omitempty"`
|
||||
StacktraceKey *string `json:"stacktrace_key,omitempty"`
|
||||
LineEnding *string `json:"line_ending,omitempty"`
|
||||
TimeFormat string `json:"time_format,omitempty"`
|
||||
TimeLocal bool `json:"time_local,omitempty"`
|
||||
DurationFormat string `json:"duration_format,omitempty"`
|
||||
LevelFormat string `json:"level_format,omitempty"`
|
||||
MessageKey *string `json:"message_key,omitempty"`
|
||||
LevelKey *string `json:"level_key,omitempty"`
|
||||
TimeKey *string `json:"time_key,omitempty"`
|
||||
NameKey *string `json:"name_key,omitempty"`
|
||||
CallerKey *string `json:"caller_key,omitempty"`
|
||||
StacktraceKey *string `json:"stacktrace_key,omitempty"`
|
||||
LineEnding *string `json:"line_ending,omitempty"`
|
||||
|
||||
// Recognized values are: unix_seconds_float, unix_milli_float, unix_nano, iso8601, rfc3339, rfc3339_nano, wall, wall_milli, wall_nano, common_log.
|
||||
// The value may also be custom format per the Go `time` package layout specification, as described [here](https://pkg.go.dev/time#pkg-constants).
|
||||
TimeFormat string `json:"time_format,omitempty"`
|
||||
TimeLocal bool `json:"time_local,omitempty"`
|
||||
|
||||
// Recognized values are: s/second/seconds, ns/nano/nanos, ms/milli/millis, string.
|
||||
// Empty and unrecognized value default to seconds.
|
||||
DurationFormat string `json:"duration_format,omitempty"`
|
||||
|
||||
// Recognized values are: lower, upper, color.
|
||||
// Empty and unrecognized value default to lower.
|
||||
LevelFormat string `json:"level_format,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalCaddyfile populates the struct from Caddyfile tokens. Syntax:
|
||||
@@ -260,12 +269,16 @@ func (lec *LogEncoderConfig) ZapcoreEncoderConfig() zapcore.EncoderConfig {
|
||||
// duration format
|
||||
var durFormatter zapcore.DurationEncoder
|
||||
switch lec.DurationFormat {
|
||||
case "", "seconds":
|
||||
case "s", "second", "seconds":
|
||||
durFormatter = zapcore.SecondsDurationEncoder
|
||||
case "nano":
|
||||
case "ns", "nano", "nanos":
|
||||
durFormatter = zapcore.NanosDurationEncoder
|
||||
case "ms", "milli", "millis":
|
||||
durFormatter = zapcore.MillisDurationEncoder
|
||||
case "string":
|
||||
durFormatter = zapcore.StringDurationEncoder
|
||||
default:
|
||||
durFormatter = zapcore.SecondsDurationEncoder
|
||||
}
|
||||
cfg.EncodeDuration = durFormatter
|
||||
|
||||
|
||||
@@ -145,9 +145,36 @@ func (fe *FilterEncoder) ConfigureDefaultFormat(wo caddy.WriterOpener) error {
|
||||
// <filter options>
|
||||
// }
|
||||
// }
|
||||
// <field> <filter> {
|
||||
// <filter options>
|
||||
// }
|
||||
// }
|
||||
func (fe *FilterEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
d.Next() // consume encoder name
|
||||
|
||||
// parse a field
|
||||
parseField := func() error {
|
||||
if fe.FieldsRaw == nil {
|
||||
fe.FieldsRaw = make(map[string]json.RawMessage)
|
||||
}
|
||||
field := d.Val()
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
filterName := d.Val()
|
||||
moduleID := "caddy.logging.encoders.filter." + filterName
|
||||
unm, err := caddyfile.UnmarshalModule(d, moduleID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filter, ok := unm.(LogFieldFilter)
|
||||
if !ok {
|
||||
return d.Errf("module %s (%T) is not a logging.LogFieldFilter", moduleID, unm)
|
||||
}
|
||||
fe.FieldsRaw[field] = caddyconfig.JSONModuleObject(filter, "filter", filterName, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
for d.NextBlock(0) {
|
||||
switch d.Val() {
|
||||
case "wrap":
|
||||
@@ -168,28 +195,19 @@ func (fe *FilterEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
|
||||
case "fields":
|
||||
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||
field := d.Val()
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
filterName := d.Val()
|
||||
moduleID := "caddy.logging.encoders.filter." + filterName
|
||||
unm, err := caddyfile.UnmarshalModule(d, moduleID)
|
||||
err := parseField()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filter, ok := unm.(LogFieldFilter)
|
||||
if !ok {
|
||||
return d.Errf("module %s (%T) is not a logging.LogFieldFilter", moduleID, unm)
|
||||
}
|
||||
if fe.FieldsRaw == nil {
|
||||
fe.FieldsRaw = make(map[string]json.RawMessage)
|
||||
}
|
||||
fe.FieldsRaw[field] = caddyconfig.JSONModuleObject(filter, "filter", filterName, nil)
|
||||
}
|
||||
|
||||
default:
|
||||
return d.Errf("unrecognized subdirective %s", d.Val())
|
||||
// if unknown, assume it's a field so that
|
||||
// the config can be flat
|
||||
err := parseField()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
+14
-3
@@ -22,13 +22,15 @@ import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewReplacer returns a new Replacer.
|
||||
func NewReplacer() *Replacer {
|
||||
rep := &Replacer{
|
||||
static: make(map[string]any),
|
||||
static: make(map[string]any),
|
||||
mapMutex: &sync.RWMutex{},
|
||||
}
|
||||
rep.providers = []ReplacerFunc{
|
||||
globalDefaultReplacements,
|
||||
@@ -41,7 +43,8 @@ func NewReplacer() *Replacer {
|
||||
// without the global default replacements.
|
||||
func NewEmptyReplacer() *Replacer {
|
||||
rep := &Replacer{
|
||||
static: make(map[string]any),
|
||||
static: make(map[string]any),
|
||||
mapMutex: &sync.RWMutex{},
|
||||
}
|
||||
rep.providers = []ReplacerFunc{
|
||||
rep.fromStatic,
|
||||
@@ -54,7 +57,9 @@ func NewEmptyReplacer() *Replacer {
|
||||
// use NewReplacer to make one.
|
||||
type Replacer struct {
|
||||
providers []ReplacerFunc
|
||||
static map[string]any
|
||||
|
||||
static map[string]any
|
||||
mapMutex *sync.RWMutex
|
||||
}
|
||||
|
||||
// Map adds mapFunc to the list of value providers.
|
||||
@@ -65,7 +70,9 @@ func (r *Replacer) Map(mapFunc ReplacerFunc) {
|
||||
|
||||
// Set sets a custom variable to a static value.
|
||||
func (r *Replacer) Set(variable string, value any) {
|
||||
r.mapMutex.Lock()
|
||||
r.static[variable] = value
|
||||
r.mapMutex.Unlock()
|
||||
}
|
||||
|
||||
// Get gets a value from the replacer. It returns
|
||||
@@ -89,11 +96,15 @@ func (r *Replacer) GetString(variable string) (string, bool) {
|
||||
// Delete removes a variable with a static value
|
||||
// that was created using Set.
|
||||
func (r *Replacer) Delete(variable string) {
|
||||
r.mapMutex.Lock()
|
||||
delete(r.static, variable)
|
||||
r.mapMutex.Unlock()
|
||||
}
|
||||
|
||||
// fromStatic provides values from r.static.
|
||||
func (r *Replacer) fromStatic(key string) (any, bool) {
|
||||
r.mapMutex.RLock()
|
||||
defer r.mapMutex.RUnlock()
|
||||
val, ok := r.static[key]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -238,6 +239,7 @@ func TestReplacerSet(t *testing.T) {
|
||||
|
||||
func TestReplacerReplaceKnown(t *testing.T) {
|
||||
rep := Replacer{
|
||||
mapMutex: &sync.RWMutex{},
|
||||
providers: []ReplacerFunc{
|
||||
// split our possible vars to two functions (to test if both functions are called)
|
||||
func(key string) (val any, ok bool) {
|
||||
@@ -310,6 +312,7 @@ func TestReplacerReplaceKnown(t *testing.T) {
|
||||
|
||||
func TestReplacerDelete(t *testing.T) {
|
||||
rep := Replacer{
|
||||
mapMutex: &sync.RWMutex{},
|
||||
static: map[string]any{
|
||||
"key1": "val1",
|
||||
"key2": "val2",
|
||||
@@ -463,5 +466,6 @@ func testReplacer() Replacer {
|
||||
return Replacer{
|
||||
providers: make([]ReplacerFunc, 0),
|
||||
static: make(map[string]any),
|
||||
mapMutex: &sync.RWMutex{},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user