mirror of
https://github.com/caddyserver/caddy.git
synced 2026-05-25 16:22:36 -04:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ec456811bb | |||
| 68cebb28d0 | |||
| a3bdc22234 | |||
| d3383ced2a | |||
| c024ae096d | |||
| 3bee569a8a | |||
| 999ab22b8c | |||
| 9991fdc495 | |||
| f29023bf8f | |||
| 85f5f47f31 | |||
| 6e4132eb89 | |||
| d89ad2fd5b | |||
| d33926b63f | |||
| c5f9227a48 | |||
| 88d391c1f5 | |||
| b4a7d6267f | |||
| e5dc76b054 | |||
| 7dfd69cdc5 | |||
| 28fdf64dc5 | |||
| 0fe98038b6 | |||
| 6e4c688ea7 | |||
| 5110643201 | |||
| 4d9b63d909 | |||
| e30deedcc1 | |||
| fbd9515d35 | |||
| 95f6bd7e5c | |||
| b1ce9d4db7 | |||
| 61679b74f5 | |||
| 2c1b663156 | |||
| 8b2dbc52ec | |||
| 657f0cab17 | |||
| 7be747fbe9 | |||
| 5b355cbed0 | |||
| a3cfe437b1 | |||
| 437d5095a6 | |||
| 145aebbba5 | |||
| 6a32daa225 | |||
| 81cdebf648 | |||
| 84c729e96a | |||
| 346c33b4d5 | |||
| 78717ce5b0 | |||
| 3d6fc1e1b7 | |||
| c7ac7de38a | |||
| 05164c895a | |||
| 1e8af27329 | |||
| b6482e53c1 | |||
| 20f6795413 | |||
| 84f16852ab |
@@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [mholt] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
@@ -8,6 +8,8 @@ linters-settings:
|
||||
linters:
|
||||
enable:
|
||||
- bodyclose
|
||||
- prealloc
|
||||
- unconvert
|
||||
- errcheck
|
||||
- gofmt
|
||||
- goimports
|
||||
|
||||
+4
-11
@@ -23,15 +23,12 @@ builds:
|
||||
- freebsd
|
||||
goarch:
|
||||
- amd64
|
||||
- 386
|
||||
- arm
|
||||
- arm64
|
||||
goarm:
|
||||
- 6
|
||||
- 7
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: 386
|
||||
- goos: darwin
|
||||
goarch: arm
|
||||
flags:
|
||||
@@ -43,11 +40,7 @@ archives:
|
||||
- goos: windows
|
||||
format: zip
|
||||
replacements:
|
||||
darwin: macOS
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
386: i386
|
||||
amd64: x86_64
|
||||
darwin: mac
|
||||
checksum:
|
||||
algorithm: sha512
|
||||
release:
|
||||
@@ -60,8 +53,8 @@ changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^ci:'
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
- '^chore:'
|
||||
- '^ci:'
|
||||
- '^docs?:'
|
||||
- '^tests?:'
|
||||
- '^\w+\s+' # a hack to remove commit messages without colons thus don't correspond to a package
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
- Can coordinate with other Caddy instances in a cluster
|
||||
- **Stays up when other servers go down** due to TLS/OCSP/certificate-related issues
|
||||
- **HTTP/1.1, HTTP/2, and experimental HTTP/3** support
|
||||
- **Highly extensible** [modular architecture](https://caddyserver.com/docs/extending-caddy) lets Caddy do anything without bloat
|
||||
- **Highly extensible** [modular architecture](https://caddyserver.com/docs/architecture) lets Caddy do anything without bloat
|
||||
- **Runs anywhere** with **no external dependencies** (not even libc)
|
||||
- Written in Go, a language with higher **memory safety guarantees** than other servers
|
||||
- Actually **fun to use**
|
||||
@@ -81,7 +81,7 @@ _**Note:** These steps [will not embed proper version information](https://githu
|
||||
Using [our builder tool](https://github.com/caddyserver/xcaddy)...
|
||||
|
||||
```
|
||||
$ xcaddy --version CADDY_VERSION
|
||||
$ xcaddy build <caddy_version>
|
||||
```
|
||||
|
||||
...the following steps are automated:
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"expvar"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
"net/url"
|
||||
@@ -78,27 +79,27 @@ type ConfigSettings struct {
|
||||
|
||||
// listenAddr extracts a singular listen address from ac.Listen,
|
||||
// returning the network and the address of the listener.
|
||||
func (admin AdminConfig) listenAddr() (string, string, error) {
|
||||
func (admin AdminConfig) listenAddr() (NetworkAddress, error) {
|
||||
input := admin.Listen
|
||||
if input == "" {
|
||||
input = DefaultAdminListen
|
||||
}
|
||||
listenAddr, err := ParseNetworkAddress(input)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("parsing admin listener address: %v", err)
|
||||
return NetworkAddress{}, fmt.Errorf("parsing admin listener address: %v", err)
|
||||
}
|
||||
if listenAddr.PortRangeSize() != 1 {
|
||||
return "", "", fmt.Errorf("admin endpoint must have exactly one address; cannot listen on %v", listenAddr)
|
||||
return NetworkAddress{}, fmt.Errorf("admin endpoint must have exactly one address; cannot listen on %v", listenAddr)
|
||||
}
|
||||
return listenAddr.Network, listenAddr.JoinHostPort(0), nil
|
||||
return listenAddr, nil
|
||||
}
|
||||
|
||||
// newAdminHandler reads admin's config and returns an http.Handler suitable
|
||||
// for use in an admin endpoint server, which will be listening on listenAddr.
|
||||
func (admin AdminConfig) newAdminHandler(listenAddr string) adminHandler {
|
||||
func (admin AdminConfig) newAdminHandler(addr NetworkAddress) adminHandler {
|
||||
muxWrap := adminHandler{
|
||||
enforceOrigin: admin.EnforceOrigin,
|
||||
allowedOrigins: admin.allowedOrigins(listenAddr),
|
||||
allowedOrigins: admin.allowedOrigins(addr),
|
||||
mux: http.NewServeMux(),
|
||||
}
|
||||
|
||||
@@ -140,16 +141,32 @@ func (admin AdminConfig) newAdminHandler(listenAddr string) adminHandler {
|
||||
// If admin.Origins is nil (null), the provided listen address
|
||||
// will be used as the default origin. If admin.Origins is
|
||||
// empty, no origins will be allowed, effectively bricking the
|
||||
// endpoint, but whatever.
|
||||
func (admin AdminConfig) allowedOrigins(listen string) []string {
|
||||
// endpoint for non-unix-socket endpoints, but whatever.
|
||||
func (admin AdminConfig) allowedOrigins(addr NetworkAddress) []string {
|
||||
uniqueOrigins := make(map[string]struct{})
|
||||
for _, o := range admin.Origins {
|
||||
uniqueOrigins[o] = struct{}{}
|
||||
}
|
||||
if admin.Origins == nil {
|
||||
uniqueOrigins[listen] = struct{}{}
|
||||
if addr.isLoopback() {
|
||||
if addr.IsUnixNetwork() {
|
||||
// RFC 2616, Section 14.26:
|
||||
// "A client MUST include a Host header field in all HTTP/1.1 request
|
||||
// messages. If the requested URI does not include an Internet host
|
||||
// name for the service being requested, then the Host header field MUST
|
||||
// be given with an empty value."
|
||||
uniqueOrigins[""] = struct{}{}
|
||||
} else {
|
||||
uniqueOrigins[net.JoinHostPort("localhost", addr.port())] = struct{}{}
|
||||
uniqueOrigins[net.JoinHostPort("::1", addr.port())] = struct{}{}
|
||||
uniqueOrigins[net.JoinHostPort("127.0.0.1", addr.port())] = struct{}{}
|
||||
}
|
||||
}
|
||||
if !addr.IsUnixNetwork() {
|
||||
uniqueOrigins[addr.JoinHostPort(0)] = struct{}{}
|
||||
}
|
||||
}
|
||||
var allowed []string
|
||||
allowed := make([]string, 0, len(uniqueOrigins))
|
||||
for origin := range uniqueOrigins {
|
||||
allowed = append(allowed, origin)
|
||||
}
|
||||
@@ -195,14 +212,14 @@ func replaceAdmin(cfg *Config) error {
|
||||
}
|
||||
|
||||
// extract a singular listener address
|
||||
netw, addr, err := adminConfig.listenAddr()
|
||||
addr, err := adminConfig.listenAddr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
handler := adminConfig.newAdminHandler(addr)
|
||||
|
||||
ln, err := Listen(netw, addr)
|
||||
ln, err := Listen(addr.Network, addr.JoinHostPort(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -219,7 +236,7 @@ func replaceAdmin(cfg *Config) error {
|
||||
|
||||
Log().Named("admin").Info(
|
||||
"admin endpoint started",
|
||||
zap.String("address", addr),
|
||||
zap.String("address", addr.String()),
|
||||
zap.Bool("enforce_origin", adminConfig.EnforceOrigin),
|
||||
zap.Strings("origins", handler.allowedOrigins),
|
||||
)
|
||||
@@ -263,6 +280,7 @@ type adminHandler struct {
|
||||
func (h adminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
Log().Named("admin.api").Info("received request",
|
||||
zap.String("method", r.Method),
|
||||
zap.String("host", r.Host),
|
||||
zap.String("uri", r.RequestURI),
|
||||
zap.String("remote_addr", r.RemoteAddr),
|
||||
zap.Reflect("headers", r.Header),
|
||||
@@ -274,14 +292,14 @@ func (h adminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// be called more than once per request, for example if a request
|
||||
// is rewritten (i.e. internal redirect).
|
||||
func (h adminHandler) serveHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if h.enforceOrigin {
|
||||
// DNS rebinding mitigation
|
||||
err := h.checkHost(r)
|
||||
if err != nil {
|
||||
h.handleError(w, r, err)
|
||||
return
|
||||
}
|
||||
// DNS rebinding mitigation
|
||||
err := h.checkHost(r)
|
||||
if err != nil {
|
||||
h.handleError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
if h.enforceOrigin {
|
||||
// cross-site mitigation
|
||||
origin, err := h.checkOrigin(r)
|
||||
if err != nil {
|
||||
|
||||
@@ -568,10 +568,6 @@ func TestSnippets(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, b := range blocks {
|
||||
t.Log(b.Keys)
|
||||
t.Log(b.Segments)
|
||||
}
|
||||
if len(blocks) != 1 {
|
||||
t.Fatalf("Expect exactly one server block. Got %d.", len(blocks))
|
||||
}
|
||||
@@ -616,10 +612,6 @@ func TestImportedFilesIgnoreNonDirectiveImportTokens(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, b := range blocks {
|
||||
t.Log(b.Keys)
|
||||
t.Log(b.Segments)
|
||||
}
|
||||
auth := blocks[0].Segments[0]
|
||||
line := auth[0].Text + " " + auth[1].Text + " " + auth[2].Text + " " + auth[3].Text
|
||||
if line != "basicauth / import password" {
|
||||
@@ -651,10 +643,6 @@ func TestSnippetAcrossMultipleFiles(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, b := range blocks {
|
||||
t.Log(b.Keys)
|
||||
t.Log(b.Segments)
|
||||
}
|
||||
if len(blocks) != 1 {
|
||||
t.Fatalf("Expect exactly one server block. Got %d.", len(blocks))
|
||||
}
|
||||
|
||||
@@ -101,13 +101,14 @@ func JSONIndent(val interface{}) ([]byte, error) {
|
||||
}
|
||||
|
||||
// RegisterAdapter registers a config adapter with the given name.
|
||||
// This should usually be done at init-time.
|
||||
func RegisterAdapter(name string, adapter Adapter) error {
|
||||
// This should usually be done at init-time. It panics if the
|
||||
// adapter cannot be registered successfully.
|
||||
func RegisterAdapter(name string, adapter Adapter) {
|
||||
if _, ok := configAdapters[name]; ok {
|
||||
return fmt.Errorf("%s: already registered", name)
|
||||
panic(fmt.Errorf("%s: already registered", name))
|
||||
}
|
||||
configAdapters[name] = adapter
|
||||
return caddy.RegisterModule(adapterModule{name, adapter})
|
||||
caddy.RegisterModule(adapterModule{name, adapter})
|
||||
}
|
||||
|
||||
// GetAdapter returns the adapter with the given name,
|
||||
|
||||
@@ -138,7 +138,7 @@ func (st *ServerType) mapAddressToServerBlocks(originalServerBlocks []serverBloc
|
||||
// association from multiple addresses to multiple server blocks; i.e. each element of
|
||||
// the returned slice) becomes a server definition in the output JSON.
|
||||
func (st *ServerType) consolidateAddrMappings(addrToServerBlocks map[string][]serverBlock) []sbAddrAssociation {
|
||||
var sbaddrs []sbAddrAssociation
|
||||
sbaddrs := make([]sbAddrAssociation, 0, len(addrToServerBlocks))
|
||||
for addr, sblocks := range addrToServerBlocks {
|
||||
// we start with knowing that at least this address
|
||||
// maps to these server blocks
|
||||
@@ -199,7 +199,7 @@ func (st *ServerType) listenerAddrsForServerBlockKey(sblock serverBlock, key str
|
||||
}
|
||||
|
||||
// the bind directive specifies hosts, but is optional
|
||||
var lnHosts []string
|
||||
lnHosts := make([]string, 0, len(sblock.pile))
|
||||
for _, cfgVal := range sblock.pile["bind"] {
|
||||
lnHosts = append(lnHosts, cfgVal.Value.([]string)...)
|
||||
}
|
||||
@@ -219,7 +219,7 @@ func (st *ServerType) listenerAddrsForServerBlockKey(sblock serverBlock, key str
|
||||
}
|
||||
|
||||
// now turn map into list
|
||||
var listenersList []string
|
||||
listenersList := make([]string, 0, len(listeners))
|
||||
for lnStr := range listeners {
|
||||
listenersList = append(listenersList, lnStr)
|
||||
}
|
||||
|
||||
@@ -393,23 +393,30 @@ type serverBlock struct {
|
||||
}
|
||||
|
||||
// hostsFromKeys returns a list of all the non-empty hostnames found in
|
||||
// the keys of the server block sb, unless allowEmpty is true, in which
|
||||
// case a key with no host (e.g. ":443") will be added to the list as an
|
||||
// empty string. Otherwise, if allowEmpty is false, and if sb has a key
|
||||
// that omits the hostname (i.e. is a catch-all/empty host), then the returned
|
||||
// list is empty, because the server block effectively matches ALL hosts.
|
||||
// The list may not be in a consistent order. If includePorts is true, then
|
||||
// any non-empty, non-standard ports will be included.
|
||||
func (sb serverBlock) hostsFromKeys(allowEmpty, includePorts bool) []string {
|
||||
// first get each unique hostname
|
||||
// the keys of the server block sb. If logger mode is false, a key with
|
||||
// an empty hostname portion will return an empty slice, since that
|
||||
// server block is interpreted to effectively match all hosts. An empty
|
||||
// string is never added to the slice.
|
||||
//
|
||||
// If loggerMode is true, then the non-standard ports of keys will be
|
||||
// joined to the hostnames. This is to effectively match the Host
|
||||
// header of requests that come in for that key.
|
||||
//
|
||||
// The resulting slice is not sorted but will never have duplicates.
|
||||
func (sb serverBlock) hostsFromKeys(loggerMode bool) []string {
|
||||
// ensure each entry in our list is unique
|
||||
hostMap := make(map[string]struct{})
|
||||
for _, addr := range sb.keys {
|
||||
if addr.Host == "" && !allowEmpty {
|
||||
// server block contains a key like ":443", i.e. the host portion
|
||||
// is empty / catch-all, which means to match all hosts
|
||||
return []string{}
|
||||
if addr.Host == "" {
|
||||
if !loggerMode {
|
||||
// server block contains a key like ":443", i.e. the host portion
|
||||
// is empty / catch-all, which means to match all hosts
|
||||
return []string{}
|
||||
}
|
||||
// never append an empty string
|
||||
continue
|
||||
}
|
||||
if includePorts &&
|
||||
if loggerMode &&
|
||||
addr.Port != "" &&
|
||||
addr.Port != strconv.Itoa(caddyhttp.DefaultHTTPPort) &&
|
||||
addr.Port != strconv.Itoa(caddyhttp.DefaultHTTPSPort) {
|
||||
@@ -428,6 +435,17 @@ func (sb serverBlock) hostsFromKeys(allowEmpty, includePorts bool) []string {
|
||||
return sblockHosts
|
||||
}
|
||||
|
||||
// hasHostCatchAllKey returns true if sb has a key that
|
||||
// omits a host portion, i.e. it "catches all" hosts.
|
||||
func (sb serverBlock) hasHostCatchAllKey() bool {
|
||||
for _, addr := range sb.keys {
|
||||
if addr.Host == "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type (
|
||||
// UnmarshalFunc is a function which can unmarshal Caddyfile
|
||||
// tokens into zero or more config values using a Helper type.
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package httpcaddyfile
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHostsFromKeys(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
keys []Address
|
||||
expectNormalMode []string
|
||||
expectLoggerMode []string
|
||||
}{
|
||||
{
|
||||
[]Address{
|
||||
{Original: "foo", Host: "foo"},
|
||||
},
|
||||
[]string{"foo"},
|
||||
[]string{"foo"},
|
||||
},
|
||||
{
|
||||
[]Address{
|
||||
{Original: "foo", Host: "foo"},
|
||||
{Original: "bar", Host: "bar"},
|
||||
},
|
||||
[]string{"bar", "foo"},
|
||||
[]string{"bar", "foo"},
|
||||
},
|
||||
{
|
||||
[]Address{
|
||||
{Original: ":2015", Port: "2015"},
|
||||
},
|
||||
[]string{}, []string{},
|
||||
},
|
||||
{
|
||||
[]Address{
|
||||
{Original: ":443", Port: "443"},
|
||||
},
|
||||
[]string{}, []string{},
|
||||
},
|
||||
{
|
||||
[]Address{
|
||||
{Original: "foo", Host: "foo"},
|
||||
{Original: ":2015", Port: "2015"},
|
||||
},
|
||||
[]string{}, []string{"foo"},
|
||||
},
|
||||
{
|
||||
[]Address{
|
||||
{Original: "example.com:2015", Host: "example.com", Port: "2015"},
|
||||
},
|
||||
[]string{"example.com"},
|
||||
[]string{"example.com:2015"},
|
||||
},
|
||||
{
|
||||
[]Address{
|
||||
{Original: "example.com:80", Host: "example.com", Port: "80"},
|
||||
},
|
||||
[]string{"example.com"},
|
||||
[]string{"example.com"},
|
||||
},
|
||||
{
|
||||
[]Address{
|
||||
{Original: "https://:2015/foo", Scheme: "https", Port: "2015", Path: "/foo"},
|
||||
},
|
||||
[]string{},
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
[]Address{
|
||||
{Original: "https://example.com:2015/foo", Scheme: "https", Host: "example.com", Port: "2015", Path: "/foo"},
|
||||
},
|
||||
[]string{"example.com"},
|
||||
[]string{"example.com:2015"},
|
||||
},
|
||||
} {
|
||||
sb := serverBlock{keys: tc.keys}
|
||||
|
||||
// test in normal mode
|
||||
actual := sb.hostsFromKeys(false)
|
||||
sort.Strings(actual)
|
||||
if !reflect.DeepEqual(tc.expectNormalMode, actual) {
|
||||
t.Errorf("Test %d (loggerMode=false): Expected: %v Actual: %v", i, tc.expectNormalMode, actual)
|
||||
}
|
||||
|
||||
// test in logger mode
|
||||
actual = sb.hostsFromKeys(true)
|
||||
sort.Strings(actual)
|
||||
if !reflect.DeepEqual(tc.expectLoggerMode, actual) {
|
||||
t.Errorf("Test %d (loggerMode=true): Expected: %v Actual: %v", i, tc.expectLoggerMode, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
|
||||
// chosen to handle a request - we actually will make each
|
||||
// server block's route terminal so that only one will run
|
||||
sbKeys := make(map[string]struct{})
|
||||
var serverBlocks []serverBlock
|
||||
serverBlocks := make([]serverBlock, 0, len(originalServerBlocks))
|
||||
for i, sblock := range originalServerBlocks {
|
||||
for j, k := range sblock.Keys {
|
||||
if _, ok := sbKeys[k]; ok {
|
||||
@@ -307,6 +307,8 @@ func (ServerType) evaluateGlobalOptionsBlock(serverBlocks []serverBlock, options
|
||||
val, err = parseOptOnDemand(disp)
|
||||
case "local_certs":
|
||||
val = true
|
||||
case "key_type":
|
||||
val, err = parseOptSingleString(disp)
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized parameter name: %s", dir)
|
||||
}
|
||||
@@ -330,6 +332,11 @@ func (st *ServerType) serversFromPairings(
|
||||
servers := make(map[string]*caddyhttp.Server)
|
||||
defaultSNI := tryString(options["default_sni"], warnings)
|
||||
|
||||
httpPort := strconv.Itoa(caddyhttp.DefaultHTTPPort)
|
||||
if hp, ok := options["http_port"].(int); ok {
|
||||
httpPort = strconv.Itoa(hp)
|
||||
}
|
||||
|
||||
for i, p := range pairings {
|
||||
srv := &caddyhttp.Server{
|
||||
Listen: p.addresses,
|
||||
@@ -367,7 +374,7 @@ func (st *ServerType) serversFromPairings(
|
||||
return specificity(iLongestHost) > specificity(jLongestHost)
|
||||
})
|
||||
|
||||
var hasCatchAllTLSConnPolicy bool
|
||||
var hasCatchAllTLSConnPolicy, usesTLS bool
|
||||
|
||||
// create a subroute for each site in the server block
|
||||
for _, sblock := range p.serverBlocks {
|
||||
@@ -376,7 +383,7 @@ func (st *ServerType) serversFromPairings(
|
||||
return nil, fmt.Errorf("server block %v: compiling matcher sets: %v", sblock.block.Keys, err)
|
||||
}
|
||||
|
||||
hosts := sblock.hostsFromKeys(false, false)
|
||||
hosts := sblock.hostsFromKeys(false)
|
||||
|
||||
// tls: connection policies
|
||||
if cpVals, ok := sblock.pile["tls.connection_policy"]; ok {
|
||||
@@ -417,6 +424,9 @@ func (st *ServerType) serversFromPairings(
|
||||
srv.AutoHTTPS.Skip = append(srv.AutoHTTPS.Skip, addr.Host)
|
||||
}
|
||||
}
|
||||
if addr.Scheme != "http" && addr.Host != "" && addr.Port != httpPort {
|
||||
usesTLS = true
|
||||
}
|
||||
}
|
||||
|
||||
// set up each handler directive, making sure to honor directive order
|
||||
@@ -448,8 +458,14 @@ func (st *ServerType) serversFromPairings(
|
||||
LoggerNames: make(map[string]string),
|
||||
}
|
||||
}
|
||||
for _, h := range sblock.hostsFromKeys(true, true) {
|
||||
srv.Logs.LoggerNames[h] = ncl.name
|
||||
if sblock.hasHostCatchAllKey() {
|
||||
srv.Logs.LoggerName = ncl.name
|
||||
} else {
|
||||
for _, h := range sblock.hostsFromKeys(true) {
|
||||
if ncl.name != "" {
|
||||
srv.Logs.LoggerNames[h] = ncl.name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -473,7 +489,9 @@ func (st *ServerType) serversFromPairings(
|
||||
// TODO: maybe a smarter way to handle this might be to just make the
|
||||
// auto-HTTPS logic at provision-time detect if there is any connection
|
||||
// policy missing for any HTTPS-enabled hosts, if so, add it... maybe?
|
||||
if !hasCatchAllTLSConnPolicy && (len(srv.TLSConnPolicies) > 0 || defaultSNI != "") {
|
||||
if usesTLS &&
|
||||
!hasCatchAllTLSConnPolicy &&
|
||||
(len(srv.TLSConnPolicies) > 0 || defaultSNI != "") {
|
||||
srv.TLSConnPolicies = append(srv.TLSConnPolicies, &caddytls.ConnectionPolicy{DefaultSNI: defaultSNI})
|
||||
}
|
||||
|
||||
@@ -548,10 +566,11 @@ func detectConflictingSchemes(srv *caddyhttp.Server, serverBlocks []serverBlock,
|
||||
return nil
|
||||
}
|
||||
|
||||
// consolidateConnPolicies combines TLS connection policies that are the same,
|
||||
// for a cleaner overall output.
|
||||
// consolidateConnPolicies removes empty TLS connection policies and combines
|
||||
// equivalent ones for a cleaner overall output.
|
||||
func consolidateConnPolicies(cps caddytls.ConnectionPolicies) (caddytls.ConnectionPolicies, error) {
|
||||
for i := 0; i < len(cps); i++ {
|
||||
// compare it to the others
|
||||
for j := 0; j < len(cps); j++ {
|
||||
if j == i {
|
||||
continue
|
||||
@@ -919,7 +938,7 @@ func (st *ServerType) compileEncodedMatcherSets(sblock serverBlock) ([]caddy.Mod
|
||||
}
|
||||
|
||||
// finally, encode each of the matcher sets
|
||||
var matcherSetsEnc []caddy.ModuleMap
|
||||
matcherSetsEnc := make([]caddy.ModuleMap, 0, len(matcherSets))
|
||||
for _, ms := range matcherSets {
|
||||
msEncoded, err := encodeMatcherSet(ms)
|
||||
if err != nil {
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
)
|
||||
|
||||
func TestServerType(t *testing.T) {
|
||||
func TestMatcherSyntax(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
input string
|
||||
expectWarn bool
|
||||
@@ -15,7 +15,7 @@ func TestServerType(t *testing.T) {
|
||||
{
|
||||
input: `http://localhost
|
||||
@debug {
|
||||
query showdebug=1
|
||||
query showdebug=1
|
||||
}
|
||||
`,
|
||||
expectWarn: false,
|
||||
@@ -24,12 +24,32 @@ func TestServerType(t *testing.T) {
|
||||
{
|
||||
input: `http://localhost
|
||||
@debug {
|
||||
query bad format
|
||||
query bad format
|
||||
}
|
||||
`,
|
||||
expectWarn: false,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
input: `http://localhost
|
||||
@debug {
|
||||
not {
|
||||
path /somepath*
|
||||
}
|
||||
}
|
||||
`,
|
||||
expectWarn: false,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
input: `http://localhost
|
||||
@debug {
|
||||
not path /somepath*
|
||||
}
|
||||
`,
|
||||
expectWarn: false,
|
||||
expectError: false,
|
||||
},
|
||||
} {
|
||||
|
||||
adapter := caddyfile.Adapter{
|
||||
|
||||
@@ -16,6 +16,7 @@ package httpcaddyfile
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
@@ -53,7 +54,7 @@ func (st ServerType) buildTLSApp(
|
||||
continue
|
||||
}
|
||||
if otherAddr.Host != "" {
|
||||
hostsSharedWithHostlessKey[addr.Host] = struct{}{}
|
||||
hostsSharedWithHostlessKey[otherAddr.Host] = struct{}{}
|
||||
}
|
||||
}
|
||||
break
|
||||
@@ -72,7 +73,7 @@ func (st ServerType) buildTLSApp(
|
||||
// get values that populate an automation policy for this block
|
||||
var ap *caddytls.AutomationPolicy
|
||||
|
||||
sblockHosts := sblock.hostsFromKeys(false, false)
|
||||
sblockHosts := sblock.hostsFromKeys(false)
|
||||
if len(sblockHosts) == 0 {
|
||||
ap = catchAllAP
|
||||
}
|
||||
@@ -100,15 +101,58 @@ func (st ServerType) buildTLSApp(
|
||||
return nil, warnings, err
|
||||
}
|
||||
}
|
||||
encoded := caddyconfig.JSONModuleObject(issuer, "module", issuer.(caddy.Module).CaddyModule().ID.Name(), &warnings)
|
||||
if ap == catchAllAP && ap.IssuerRaw != nil && !bytes.Equal(ap.IssuerRaw, encoded) {
|
||||
return nil, warnings, fmt.Errorf("conflicting issuer configuration: %s != %s", ap.IssuerRaw, encoded)
|
||||
if ap == catchAllAP && !reflect.DeepEqual(ap.Issuer, issuer) {
|
||||
return nil, warnings, fmt.Errorf("automation policy from site block is also default/catch-all policy because of key without hostname, and the two are in conflict: %#v != %#v", ap.Issuer, issuer)
|
||||
}
|
||||
ap.IssuerRaw = encoded
|
||||
ap.Issuer = issuer
|
||||
}
|
||||
}
|
||||
|
||||
// custom bind host
|
||||
for _, cfgVal := range sblock.pile["bind"] {
|
||||
// either an existing issuer is already configured (and thus, ap is not
|
||||
// nil), or we need to configure an issuer, so we need ap to be non-nil
|
||||
if ap == nil {
|
||||
ap, err = newBaseAutomationPolicy(options, warnings, true)
|
||||
if err != nil {
|
||||
return nil, warnings, err
|
||||
}
|
||||
}
|
||||
|
||||
// if an issuer was already configured and it is NOT an ACME
|
||||
// issuer, skip, since we intend to adjust only ACME issuers
|
||||
var acmeIssuer *caddytls.ACMEIssuer
|
||||
if ap.Issuer != nil {
|
||||
var ok bool
|
||||
if acmeIssuer, ok = ap.Issuer.(*caddytls.ACMEIssuer); !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// proceed to configure the ACME issuer's bind host, without
|
||||
// overwriting any existing settings
|
||||
if acmeIssuer == nil {
|
||||
acmeIssuer = new(caddytls.ACMEIssuer)
|
||||
}
|
||||
if acmeIssuer.Challenges == nil {
|
||||
acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
|
||||
}
|
||||
if acmeIssuer.Challenges.BindHost == "" {
|
||||
// only binding to one host is supported
|
||||
var bindHost string
|
||||
if bindHosts, ok := cfgVal.Value.([]string); ok && len(bindHosts) > 0 {
|
||||
bindHost = bindHosts[0]
|
||||
}
|
||||
acmeIssuer.Challenges.BindHost = bindHost
|
||||
}
|
||||
ap.Issuer = acmeIssuer // we'll encode it later
|
||||
}
|
||||
|
||||
if ap != nil {
|
||||
// encode issuer now that it's all set up
|
||||
issuerName := ap.Issuer.(caddy.Module).CaddyModule().ID.Name()
|
||||
ap.IssuerRaw = caddyconfig.JSONModuleObject(ap.Issuer, "module", issuerName, &warnings)
|
||||
|
||||
// first make sure this block is allowed to create an automation policy;
|
||||
// doing so is forbidden if it has a key with no host (i.e. ":443")
|
||||
// and if there is a different server block that also has a key with no
|
||||
@@ -202,7 +246,7 @@ func (st ServerType) buildTLSApp(
|
||||
}
|
||||
clVal := reflect.ValueOf(cl)
|
||||
for i := 0; i < clVal.Len(); i++ {
|
||||
combined = reflect.Append(reflect.Value(combined), clVal.Index(i))
|
||||
combined = reflect.Append(combined, clVal.Index(i))
|
||||
}
|
||||
loadersByName[name] = combined.Interface().(caddytls.CertificateLoader)
|
||||
}
|
||||
@@ -220,26 +264,45 @@ func (st ServerType) buildTLSApp(
|
||||
tlsApp.Automation.OnDemand = onDemand
|
||||
}
|
||||
|
||||
// if there is a global/catch-all automation policy, ensure it goes last
|
||||
if catchAllAP != nil {
|
||||
if tlsApp.Automation == nil {
|
||||
tlsApp.Automation = new(caddytls.AutomationConfig)
|
||||
}
|
||||
tlsApp.Automation.Policies = append(tlsApp.Automation.Policies, catchAllAP)
|
||||
}
|
||||
|
||||
// if any hostnames appear on the same server block as a key with
|
||||
// no host, they will not be used with route matchers because the
|
||||
// hostless key matches all hosts, therefore, it wouldn't be
|
||||
// considered for auto-HTTPS, so we need to make sure those hosts
|
||||
// are manually considered for managed certificates
|
||||
// are manually considered for managed certificates; we also need
|
||||
// to make sure that any of these names which are internal-only
|
||||
// get internal certificates by default rather than ACME
|
||||
var al caddytls.AutomateLoader
|
||||
internalAP := &caddytls.AutomationPolicy{
|
||||
IssuerRaw: json.RawMessage(`{"module":"internal"}`),
|
||||
}
|
||||
for h := range hostsSharedWithHostlessKey {
|
||||
al = append(al, h)
|
||||
if !certmagic.SubjectQualifiesForPublicCert(h) {
|
||||
internalAP.Subjects = append(internalAP.Subjects, h)
|
||||
}
|
||||
}
|
||||
if len(al) > 0 {
|
||||
tlsApp.CertificatesRaw["automate"] = caddyconfig.JSON(al, &warnings)
|
||||
}
|
||||
if len(internalAP.Subjects) > 0 {
|
||||
if tlsApp.Automation == nil {
|
||||
tlsApp.Automation = new(caddytls.AutomationConfig)
|
||||
}
|
||||
tlsApp.Automation.Policies = append(tlsApp.Automation.Policies, internalAP)
|
||||
}
|
||||
|
||||
// if there is a global/catch-all automation policy, ensure it goes last
|
||||
if catchAllAP != nil {
|
||||
// first, encode its issuer
|
||||
issuerName := catchAllAP.Issuer.(caddy.Module).CaddyModule().ID.Name()
|
||||
catchAllAP.IssuerRaw = caddyconfig.JSONModuleObject(catchAllAP.Issuer, "module", issuerName, &warnings)
|
||||
|
||||
// then append it to the end of the policies list
|
||||
if tlsApp.Automation == nil {
|
||||
tlsApp.Automation = new(caddytls.AutomationConfig)
|
||||
}
|
||||
tlsApp.Automation.Policies = append(tlsApp.Automation.Policies, catchAllAP)
|
||||
}
|
||||
|
||||
// do a little verification & cleanup
|
||||
if tlsApp.Automation != nil {
|
||||
@@ -274,8 +337,9 @@ func newBaseAutomationPolicy(options map[string]interface{}, warnings []caddycon
|
||||
acmeCARoot, hasACMECARoot := options["acme_ca_root"]
|
||||
email, hasEmail := options["email"]
|
||||
localCerts, hasLocalCerts := options["local_certs"]
|
||||
keyType, hasKeyType := options["key_type"]
|
||||
|
||||
hasGlobalAutomationOpts := hasACMECA || hasACMEDNS || hasACMECARoot || hasEmail || hasLocalCerts
|
||||
hasGlobalAutomationOpts := hasACMECA || hasACMEDNS || hasACMECARoot || hasEmail || hasLocalCerts || hasKeyType
|
||||
|
||||
// if there are no global options related to automation policies
|
||||
// set, then we can just return right away
|
||||
@@ -290,7 +354,7 @@ func newBaseAutomationPolicy(options map[string]interface{}, warnings []caddycon
|
||||
|
||||
if localCerts != nil {
|
||||
// internal issuer enabled trumps any ACME configurations; useful in testing
|
||||
ap.IssuerRaw = caddyconfig.JSONModuleObject(caddytls.InternalIssuer{}, "module", "internal", &warnings)
|
||||
ap.Issuer = new(caddytls.InternalIssuer) // we'll encode it later
|
||||
} else {
|
||||
if acmeCA == nil {
|
||||
acmeCA = ""
|
||||
@@ -298,7 +362,7 @@ func newBaseAutomationPolicy(options map[string]interface{}, warnings []caddycon
|
||||
if email == nil {
|
||||
email = ""
|
||||
}
|
||||
mgr := caddytls.ACMEIssuer{
|
||||
mgr := &caddytls.ACMEIssuer{
|
||||
CA: acmeCA.(string),
|
||||
Email: email.(string),
|
||||
}
|
||||
@@ -315,7 +379,10 @@ func newBaseAutomationPolicy(options map[string]interface{}, warnings []caddycon
|
||||
if acmeCARoot != nil {
|
||||
mgr.TrustedRootsPEMFiles = []string{acmeCARoot.(string)}
|
||||
}
|
||||
ap.IssuerRaw = caddyconfig.JSONModuleObject(mgr, "module", "acme", &warnings)
|
||||
if keyType != nil {
|
||||
ap.KeyType = keyType.(string)
|
||||
}
|
||||
ap.Issuer = mgr // we'll encode it later
|
||||
}
|
||||
|
||||
return ap, nil
|
||||
|
||||
+57
-2
@@ -19,6 +19,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aryann/difflib"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
||||
caddycmd "github.com/caddyserver/caddy/v2/cmd"
|
||||
|
||||
// plug in Caddy modules here
|
||||
@@ -58,7 +60,8 @@ func timeElapsed(start time.Time, name string) {
|
||||
// InitServer this will configure the server with a configurion of a specific
|
||||
// type. The configType must be either "json" or the adapter type.
|
||||
func InitServer(t *testing.T, rawConfig string, configType string) {
|
||||
if err := initServer(t, rawConfig, configType); errors.Is(err, &configLoadError{}) {
|
||||
|
||||
if err := initServer(t, rawConfig, configType); err != nil {
|
||||
t.Logf("failed to load config: %s", err)
|
||||
t.Fail()
|
||||
}
|
||||
@@ -252,7 +255,7 @@ func AssertGetResponse(t *testing.T, requestURI string, statusCode int, expected
|
||||
if !strings.Contains(body, expectedBody) {
|
||||
t.Errorf("requesting \"%s\" expected response body \"%s\" but got \"%s\"", requestURI, expectedBody, body)
|
||||
}
|
||||
return resp, string(body)
|
||||
return resp, body
|
||||
}
|
||||
|
||||
// AssertGetResponseBody request a URI and assert the status code matches
|
||||
@@ -316,3 +319,55 @@ func AssertRedirect(t *testing.T, requestURI string, expectedToLocation string,
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
// AssertAdapt adapts a config and then tests it against an expected result
|
||||
func AssertAdapt(t *testing.T, rawConfig string, adapterName string, expectedResponse string) {
|
||||
|
||||
cfgAdapter := caddyconfig.GetAdapter(adapterName)
|
||||
if cfgAdapter == nil {
|
||||
t.Errorf("unrecognized config adapter '%s'", adapterName)
|
||||
return
|
||||
}
|
||||
|
||||
options := make(map[string]interface{})
|
||||
options["pretty"] = "true"
|
||||
|
||||
result, warnings, err := cfgAdapter.Adapt([]byte(rawConfig), options)
|
||||
if err != nil {
|
||||
t.Errorf("adapting config using %s adapter: %v", adapterName, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(warnings) > 0 {
|
||||
for _, w := range warnings {
|
||||
t.Logf("warning: directive: %s : %s", w.Directive, w.Message)
|
||||
}
|
||||
}
|
||||
|
||||
diff := difflib.Diff(
|
||||
strings.Split(expectedResponse, "\n"),
|
||||
strings.Split(string(result), "\n"))
|
||||
|
||||
// scan for failure
|
||||
failed := false
|
||||
for _, d := range diff {
|
||||
if d.Delta != difflib.Common {
|
||||
failed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if failed {
|
||||
for _, d := range diff {
|
||||
switch d.Delta {
|
||||
case difflib.Common:
|
||||
fmt.Printf(" %s\n", d.Payload)
|
||||
case difflib.LeftOnly:
|
||||
fmt.Printf(" - %s\n", d.Payload)
|
||||
case difflib.RightOnly:
|
||||
fmt.Printf(" + %s\n", d.Payload)
|
||||
}
|
||||
}
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/caddyserver/caddy/v2/caddytest"
|
||||
)
|
||||
|
||||
func TestHttpOnlyOnLocalhost(t *testing.T) {
|
||||
caddytest.AssertAdapt(t, `
|
||||
localhost:80 {
|
||||
respond /version 200 {
|
||||
body "hello from localhost"
|
||||
}
|
||||
}
|
||||
`, "caddyfile", `{
|
||||
"apps": {
|
||||
"http": {
|
||||
"servers": {
|
||||
"srv0": {
|
||||
"listen": [
|
||||
":80"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"match": [
|
||||
{
|
||||
"host": [
|
||||
"localhost"
|
||||
]
|
||||
}
|
||||
],
|
||||
"handle": [
|
||||
{
|
||||
"handler": "subroute",
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"body": "hello from localhost",
|
||||
"handler": "static_response",
|
||||
"status_code": 200
|
||||
}
|
||||
],
|
||||
"match": [
|
||||
{
|
||||
"path": [
|
||||
"/version"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"terminal": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`)
|
||||
}
|
||||
|
||||
func TestHttpOnlyOnAnyAddress(t *testing.T) {
|
||||
caddytest.AssertAdapt(t, `
|
||||
:80 {
|
||||
respond /version 200 {
|
||||
body "hello from localhost"
|
||||
}
|
||||
}
|
||||
`, "caddyfile", `{
|
||||
"apps": {
|
||||
"http": {
|
||||
"servers": {
|
||||
"srv0": {
|
||||
"listen": [
|
||||
":80"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"match": [
|
||||
{
|
||||
"path": [
|
||||
"/version"
|
||||
]
|
||||
}
|
||||
],
|
||||
"handle": [
|
||||
{
|
||||
"body": "hello from localhost",
|
||||
"handler": "static_response",
|
||||
"status_code": 200
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`)
|
||||
}
|
||||
|
||||
func TestHttpsOnDomain(t *testing.T) {
|
||||
caddytest.AssertAdapt(t, `
|
||||
a.caddy.localhost {
|
||||
respond /version 200 {
|
||||
body "hello from localhost"
|
||||
}
|
||||
}
|
||||
`, "caddyfile", `{
|
||||
"apps": {
|
||||
"http": {
|
||||
"servers": {
|
||||
"srv0": {
|
||||
"listen": [
|
||||
":443"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"match": [
|
||||
{
|
||||
"host": [
|
||||
"a.caddy.localhost"
|
||||
]
|
||||
}
|
||||
],
|
||||
"handle": [
|
||||
{
|
||||
"handler": "subroute",
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"body": "hello from localhost",
|
||||
"handler": "static_response",
|
||||
"status_code": 200
|
||||
}
|
||||
],
|
||||
"match": [
|
||||
{
|
||||
"path": [
|
||||
"/version"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"terminal": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`)
|
||||
}
|
||||
|
||||
func TestHttpOnlyOnDomain(t *testing.T) {
|
||||
caddytest.AssertAdapt(t, `
|
||||
http://a.caddy.localhost {
|
||||
respond /version 200 {
|
||||
body "hello from localhost"
|
||||
}
|
||||
}
|
||||
`, "caddyfile", `{
|
||||
"apps": {
|
||||
"http": {
|
||||
"servers": {
|
||||
"srv0": {
|
||||
"listen": [
|
||||
":80"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"match": [
|
||||
{
|
||||
"host": [
|
||||
"a.caddy.localhost"
|
||||
]
|
||||
}
|
||||
],
|
||||
"handle": [
|
||||
{
|
||||
"handler": "subroute",
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"body": "hello from localhost",
|
||||
"handler": "static_response",
|
||||
"status_code": 200
|
||||
}
|
||||
],
|
||||
"match": [
|
||||
{
|
||||
"path": [
|
||||
"/version"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"terminal": true
|
||||
}
|
||||
],
|
||||
"automatic_https": {
|
||||
"skip": [
|
||||
"a.caddy.localhost"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`)
|
||||
}
|
||||
|
||||
func TestHttpOnlyOnNonStandardPort(t *testing.T) {
|
||||
caddytest.AssertAdapt(t, `
|
||||
http://a.caddy.localhost:81 {
|
||||
respond /version 200 {
|
||||
body "hello from localhost"
|
||||
}
|
||||
}
|
||||
`, "caddyfile", `{
|
||||
"apps": {
|
||||
"http": {
|
||||
"servers": {
|
||||
"srv0": {
|
||||
"listen": [
|
||||
":81"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"match": [
|
||||
{
|
||||
"host": [
|
||||
"a.caddy.localhost"
|
||||
]
|
||||
}
|
||||
],
|
||||
"handle": [
|
||||
{
|
||||
"handler": "subroute",
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"body": "hello from localhost",
|
||||
"handler": "static_response",
|
||||
"status_code": 200
|
||||
}
|
||||
],
|
||||
"match": [
|
||||
{
|
||||
"path": [
|
||||
"/version"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"terminal": true
|
||||
}
|
||||
],
|
||||
"automatic_https": {
|
||||
"skip": [
|
||||
"a.caddy.localhost"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`)
|
||||
}
|
||||
@@ -272,3 +272,46 @@ func TestDefaultSNIWithPortMappingOnly(t *testing.T) {
|
||||
// makes a request with no sni
|
||||
caddytest.AssertGetResponse(t, "https://127.0.0.1:9443/version", 200, "hello from a")
|
||||
}
|
||||
|
||||
func TestHttpOnlyOnDomainWithSNI(t *testing.T) {
|
||||
caddytest.AssertAdapt(t, `
|
||||
{
|
||||
default_sni a.caddy.localhost
|
||||
}
|
||||
:80 {
|
||||
respond /version 200 {
|
||||
body "hello from localhost"
|
||||
}
|
||||
}
|
||||
`, "caddyfile", `{
|
||||
"apps": {
|
||||
"http": {
|
||||
"servers": {
|
||||
"srv0": {
|
||||
"listen": [
|
||||
":80"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"match": [
|
||||
{
|
||||
"path": [
|
||||
"/version"
|
||||
]
|
||||
}
|
||||
],
|
||||
"handle": [
|
||||
{
|
||||
"body": "hello from localhost",
|
||||
"handler": "static_response",
|
||||
"status_code": 200
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`)
|
||||
}
|
||||
|
||||
+9
-1
@@ -171,7 +171,15 @@ func cmdRun(fl Flags) (int, error) {
|
||||
} else if err != nil {
|
||||
return caddy.ExitCodeFailedStartup, err
|
||||
} else {
|
||||
caddy.Log().Info("resuming from last configuration", zap.String("autosave_file", caddy.ConfigAutosavePath))
|
||||
if runCmdConfigFlag == "" {
|
||||
caddy.Log().Info("resuming from last configuration",
|
||||
zap.String("autosave_file", caddy.ConfigAutosavePath))
|
||||
} else {
|
||||
// if they also specified a config file, user should be aware that we're not
|
||||
// using it (doing so could lead to data/config loss by overwriting!)
|
||||
caddy.Log().Warn("--config and --resume flags were used together; ignoring --config and resuming from last configuration",
|
||||
zap.String("autosave_file", caddy.ConfigAutosavePath))
|
||||
}
|
||||
}
|
||||
}
|
||||
// we don't use 'else' here since this value might have been changed in 'if' block; i.e. not mutually exclusive
|
||||
|
||||
@@ -5,28 +5,33 @@ go 1.14
|
||||
require (
|
||||
github.com/Masterminds/sprig/v3 v3.0.2
|
||||
github.com/alecthomas/chroma v0.7.2-0.20200305040604-4f3623dce67a
|
||||
github.com/caddyserver/certmagic v0.10.7
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a
|
||||
github.com/caddyserver/certmagic v0.10.11
|
||||
github.com/cenkalti/backoff/v4 v4.0.2 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac
|
||||
github.com/go-acme/lego/v3 v3.5.0
|
||||
github.com/gogo/protobuf v1.3.1
|
||||
github.com/google/cel-go v0.4.1
|
||||
github.com/imdario/mergo v0.3.9 // indirect
|
||||
github.com/jsternberg/zap-logfmt v1.2.0
|
||||
github.com/klauspost/compress v1.10.3
|
||||
github.com/klauspost/compress v1.10.4
|
||||
github.com/klauspost/cpuid v1.2.3
|
||||
github.com/lucas-clemente/quic-go v0.15.2
|
||||
github.com/lucas-clemente/quic-go v0.15.3
|
||||
github.com/manifoldco/promptui v0.7.0 // indirect
|
||||
github.com/miekg/dns v1.1.29 // indirect
|
||||
github.com/naoina/go-stringutil v0.1.0 // indirect
|
||||
github.com/naoina/toml v0.1.1
|
||||
github.com/smallstep/certificates v0.14.0-rc.5
|
||||
github.com/smallstep/cli v0.14.0-rc.3
|
||||
github.com/smallstep/certificates v0.14.1
|
||||
github.com/smallstep/cli v0.14.1
|
||||
github.com/smallstep/truststore v0.9.5
|
||||
github.com/vulcand/oxy v1.1.0
|
||||
github.com/yuin/goldmark v1.1.27
|
||||
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691
|
||||
go.uber.org/zap v1.14.1
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59
|
||||
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e
|
||||
google.golang.org/genproto v0.0.0-20200323114720-3f67cca34472
|
||||
golang.org/x/sys v0.0.0-20200409092240-59c9f1ba88fa // indirect
|
||||
google.golang.org/genproto v0.0.0-20200409111301-baae70f3302d
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
gopkg.in/square/go-jose.v2 v2.4.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
|
||||
@@ -54,11 +54,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
|
||||
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
|
||||
github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic=
|
||||
github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
|
||||
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
|
||||
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/semver/v3 v3.0.1 h1:2kKm5lb7dKVrt5TYUiAavE6oFc1cFT0057UVGT+JqLk=
|
||||
github.com/Masterminds/semver/v3 v3.0.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3sm36u14=
|
||||
@@ -67,7 +64,6 @@ github.com/Masterminds/sprig/v3 v3.0.0 h1:KSQz7Nb08/3VU9E4ns29dDxcczhOD1q7O1UfM4
|
||||
github.com/Masterminds/sprig/v3 v3.0.0/go.mod h1:NEUY/Qq8Gdm2xgYA+NwJM6wmfdRV9xkh8h/Rld20R0U=
|
||||
github.com/Masterminds/sprig/v3 v3.0.2 h1:wz22D0CiSctrliXiI9ZO3HoNApweeRGftyDN+BQa3B8=
|
||||
github.com/Masterminds/sprig/v3 v3.0.2/go.mod h1:oesJ8kPONMONaZgtiHNzUShJbksypC5kWczhZAf6+aU=
|
||||
github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
|
||||
@@ -106,9 +102,9 @@ github.com/antlr/antlr4 v0.0.0-20190819145818-b43a4c3a8015 h1:StuiJFxQUsxSCzcby6
|
||||
github.com/antlr/antlr4 v0.0.0-20190819145818-b43a4c3a8015/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20180315120708-ccb8e960c48f/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a h1:pv34s756C4pEXnjgPfGYgdhg/ZdajGhyOvzx8k+23nw=
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
|
||||
@@ -117,13 +113,14 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bombsimon/wsl/v2 v2.0.0/go.mod h1:mf25kr/SqFEPhhcxW1+7pxzGlW+hIl/hYTKY95VwV8U=
|
||||
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/caddyserver/certmagic v0.10.7 h1:OwT4Delj91ee7vumu+CnFJdWLsYBYj6kJ2PwsoqI7LA=
|
||||
github.com/caddyserver/certmagic v0.10.7/go.mod h1:Y8jcUBctgk/IhpAzlHKfimZNyXCkfGgRTC0orl8gROQ=
|
||||
github.com/caddyserver/certmagic v0.10.11 h1:70wN1rNTc1EVwWA2i1IwYg8jJcVFsE7cvy2UT0uVA5k=
|
||||
github.com/caddyserver/certmagic v0.10.11/go.mod h1:Y8jcUBctgk/IhpAzlHKfimZNyXCkfGgRTC0orl8gROQ=
|
||||
github.com/cenkalti/backoff/v4 v4.0.0 h1:6VeaLF9aI+MAUQ95106HwWzYZgJJpZ4stumjj6RFYAU=
|
||||
github.com/cenkalti/backoff/v4 v4.0.0/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
|
||||
github.com/cenkalti/backoff/v4 v4.0.2 h1:JIufpQLbh4DkbQoii76ItQIUFzevQSqOLZca4eamEDs=
|
||||
github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
@@ -141,7 +138,6 @@ github.com/cloudflare/cloudflare-go v0.10.2 h1:VBodKICVPnwmDxstcW3biKcDSpFIfS/RE
|
||||
github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
@@ -153,8 +149,6 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/corpix/uarand v0.0.0-20170903190822-2b8494104d86/go.mod h1:JSm890tOkDN+M1jqN8pUGDKnzJrsVbJwSMHBY4zwz7M=
|
||||
github.com/corpix/uarand v0.1.1/go.mod h1:SFKZvkcRoLqVRFZ4u25xPmp6m9ktANfbpXZ7SJ0/FNU=
|
||||
github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok=
|
||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
@@ -207,7 +201,6 @@ github.com/go-acme/lego/v3 v3.4.0 h1:deB9NkelA+TfjGHVw8J7iKl/rMtffcGMWSMmptvMv0A
|
||||
github.com/go-acme/lego/v3 v3.4.0/go.mod h1:xYbLDuxq3Hy4bMUT1t9JIuz6GWIWb3m5X+TeTHYaT7M=
|
||||
github.com/go-acme/lego/v3 v3.5.0 h1:/0+NJQK+hNwRznhCi+19lbEa4xufhe7wJZOVd5j486s=
|
||||
github.com/go-acme/lego/v3 v3.5.0/go.mod h1:TXodhTGOiWEqXDdgrzBoCtJ5R4L9lfOE68CTM0KGkT0=
|
||||
github.com/go-chi/chi v3.3.4-0.20181024101233-0ebf7795c516+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
|
||||
github.com/go-critic/go-critic v0.3.5-0.20190526074819-1df300866540/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
|
||||
@@ -245,6 +238,7 @@ github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx
|
||||
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/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
@@ -301,7 +295,6 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
|
||||
github.com/google/cel-go v0.4.1 h1:2kqc5arTucvtLJzXVUbmiUh7n2xjizwZijPrpEsagAE=
|
||||
github.com/google/cel-go v0.4.1/go.mod h1:F0UncVAXNlNjl/4C8hqGdoV6APmuFpetoMJSLIQLBPU=
|
||||
github.com/google/cel-spec v0.3.0/go.mod h1:MjQm800JAGhOZXI7vatnVpmIaFTR6L8FHcKk+piiKpI=
|
||||
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||
github.com/google/certificate-transparency-go v1.1.0/go.mod h1:i+Q7XY+ArBveOUT36jiHGfuSK1fHICIg6sUkRxPAbCs=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
@@ -368,8 +361,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
|
||||
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo=
|
||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4=
|
||||
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
|
||||
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
@@ -391,21 +384,19 @@ github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU
|
||||
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
|
||||
github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
|
||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.10.4 h1:jFzIFaf586tquEB5EhzQG0HwGNSlgAJpG53G6Ss11wc=
|
||||
github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs=
|
||||
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
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 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@@ -427,8 +418,8 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA=
|
||||
github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ=
|
||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/lucas-clemente/quic-go v0.15.2 h1:RgxRJ7rPde0Q/uXDeb3/UdblVvxrYGDAG9G9GO78LmI=
|
||||
github.com/lucas-clemente/quic-go v0.15.2/go.mod h1:qxmO5Y4ZMhdNkunGfxuZnZXnJwYpW9vjQkyrZ7BsgUI=
|
||||
github.com/lucas-clemente/quic-go v0.15.3 h1:i6n4Jr7673z9TlurAjc87+GlE/BN10++r9XZIPS9j6I=
|
||||
github.com/lucas-clemente/quic-go v0.15.3/go.mod h1:oj40DjNLuNugvtXWg4PwaYgv7tAbzAabrT57CC69EhI=
|
||||
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw=
|
||||
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8=
|
||||
@@ -447,8 +438,8 @@ github.com/manifoldco/promptui v0.7.0 h1:3l11YT8tm9MnwGFQ4kETwkzpAwY2Jt9lCrumCUW
|
||||
github.com/manifoldco/promptui v0.7.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ=
|
||||
github.com/marten-seemann/qpack v0.1.0 h1:/0M7lkda/6mus9B8u34Asqm8ZhHAAt9Ho0vniNuVSVg=
|
||||
github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI=
|
||||
github.com/marten-seemann/qtls v0.8.0 h1:aj+MPLibzKByw8CmG0WvWgbtBkctYPAXeB11cQJC8mo=
|
||||
github.com/marten-seemann/qtls v0.8.0/go.mod h1:Lao6jDqlCfxyLKYFmZXGm2LSHBgVn+P+ROOex6YkT+k=
|
||||
github.com/marten-seemann/qtls v0.9.0 h1:8Zguhc72eS+DH5EAb0BpAPIy3HDXYcihQi4xoDZOnjQ=
|
||||
github.com/marten-seemann/qtls v0.9.0/go.mod h1:T1MmAdDPyISzxlK6kjRr0pcZFBVd1OZbBb/j3cvzHhk=
|
||||
github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
@@ -458,7 +449,6 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
@@ -495,6 +485,7 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
||||
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
||||
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
|
||||
github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks=
|
||||
@@ -507,9 +498,7 @@ github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96d
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/newrelic/go-agent v1.11.0/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ=
|
||||
github.com/newrelic/go-agent v2.15.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ=
|
||||
github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM=
|
||||
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
|
||||
github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/HzqidhOhjw=
|
||||
github.com/nrdcg/auroradns v1.0.1/go.mod h1:y4pc0i9QXYlFCWrhWrUSIETnZgrf4KuwjDIWmmXo3JI=
|
||||
@@ -533,6 +522,7 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34=
|
||||
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
@@ -628,8 +618,8 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
|
||||
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.1.1/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.3.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 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
@@ -637,47 +627,17 @@ github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:s
|
||||
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5/go.mod h1:TC9A4+RjIOS+HyTH7wG17/gSqVv95uDw2J64dQZx7RE=
|
||||
github.com/smallstep/assert v0.0.0-20200103212524-b99dc1097b15 h1:kSImCuenAkXtCaBeQ1UhmzzJGRhSm8sVH7I3sHE2Qdg=
|
||||
github.com/smallstep/assert v0.0.0-20200103212524-b99dc1097b15/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc=
|
||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191023014154-4669bef8c700/go.mod h1:/WOAB2LkcjkEbKG5rDol+A22Lp3UsttkLPLkY7tVtuk=
|
||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191025192352-8ef9b020ed24/go.mod h1:043iBnsMvNhQ+QFwSh0N6JR3H2yamHPPAc78vCf+8Tc=
|
||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191126035953-e88034bea402/go.mod h1:r2UTcAZNriKlwvNNXymNAcF3iKL6mTYOYrOCtBYYGJU=
|
||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191210005525-50152391a397/go.mod h1:8leACUXHFo0JVm9YcrcX09aar2H8hz1BAWxD1D/GpsU=
|
||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191213215656-d2100821138c/go.mod h1:HMXt9hWBm7M7ZrUy0uZ/T/077te2x9bnXZCxrdVsBf4=
|
||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191217235337-aa5894058226/go.mod h1:MTKifeJBe1B/dzH5NDoPFpIPaWD0MzRozzONVkF8egc=
|
||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191218224459-1fa35491ea07/go.mod h1:eEtpedAL4inqaAx6ZqJnE4NOx1/GxDh6VQOmbs7CPf0=
|
||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20200110185849-085ae821636e/go.mod h1:weY9Os8g0yPfyxd+Zy1CTAwCb7YMqg/u5XnEagBN5Rk=
|
||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20200111012147-3ce267cdd6b7/go.mod h1:jljUh6mTvHOAqvIUvbD2L3Q/aqSpTI6HzJiNFQkj1Hc=
|
||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20200128212940-432ed0090f3d/go.mod h1:lWKe0ZOg45lNWtByxh82fOfzXwx93S0TeWzTCOjc19k=
|
||||
github.com/smallstep/certificates v0.14.0-rc.2.0.20200129195847-7846696fbb69/go.mod h1:vZjJp4hweYVx+rBouWEVOf3KlH2Yilxo/50dsj7y8aY=
|
||||
github.com/smallstep/certificates v0.14.0-rc.5 h1:AifQ0Agi3FPvcxi+AUkxzjfmj3jGQOd2jKSzpcyhSd0=
|
||||
github.com/smallstep/certificates v0.14.0-rc.5/go.mod h1:ii3ce5KzeY72D4+f/HBz2rXjgQ9dfpkN8KQRiIxImVM=
|
||||
github.com/smallstep/certinfo v0.0.0-20191008000228-b0e530932339/go.mod h1:n4YHPL9hJIyB+N4F2rPBy3mpPxMxTGJP5Pdsyaoc2Ns=
|
||||
github.com/smallstep/certinfo v0.0.0-20191029235839-00563809d483/go.mod h1:xmx5n8+7jI0lrjTUwc8WMMqXeOHRyxYUW9U1wrvP3Vo=
|
||||
github.com/smallstep/certinfo v1.0.0/go.mod h1:xmx5n8+7jI0lrjTUwc8WMMqXeOHRyxYUW9U1wrvP3Vo=
|
||||
github.com/smallstep/certinfo v1.1.0/go.mod h1:1gQJekdPwPvUwFWGTi7bZELmQT09cxC9wJ0VBkBNiwU=
|
||||
github.com/smallstep/cli v0.12.1-0.20191016010425-15911d8625df/go.mod h1:zGPm8vWCqzvDqkdC1laFJNdIOjNSB8V4qDp68Ny538o=
|
||||
github.com/smallstep/cli v0.13.3 h1:S29UydCtDVy0QQBtGdatq064tnks1/0DYxxnEtNiQpc=
|
||||
github.com/smallstep/cli v0.13.3/go.mod h1:zGPm8vWCqzvDqkdC1laFJNdIOjNSB8V4qDp68Ny538o=
|
||||
github.com/smallstep/cli v0.14.0-rc.1.0.20191024214139-914a67ed80c2/go.mod h1:GoA1cE4YrZRRvVbFlPKJUsMuWHnFBX+R88j1pmpbGgk=
|
||||
github.com/smallstep/cli v0.14.0-rc.1.0.20191105013638-8cf838b56d03/go.mod h1:dklnISxr+GzUmurBngEF9Jvj0aI9KK5uVgZwOdFniNs=
|
||||
github.com/smallstep/cli v0.14.0-rc.1.0.20191127025104-2821b0b811c1/go.mod h1:F6/cZ7VguiUV4nsoqPdDyZtGOgg3oLHz+LstEQsiSAg=
|
||||
github.com/smallstep/cli v0.14.0-rc.1.0.20191211225301-a5e848783407/go.mod h1:1DDxP5W6pSuPL7DudNMbr/qVVjToo8qz3tlRt8ka8TA=
|
||||
github.com/smallstep/cli v0.14.0-rc.1.0.20191217223638-5ee30a55af45/go.mod h1:6pTiWJKfIQcUYtK7lVnI0pOXRiYAWuy0qrlFVnn9q8M=
|
||||
github.com/smallstep/cli v0.14.0-rc.1.0.20191218000521-3e7348324838/go.mod h1:JPG34JrC37Pw0HjoB+cAtXT1yFOXfab/5nrM7ZTSw8c=
|
||||
github.com/smallstep/cli v0.14.0-rc.1.0.20200110185014-8a0d0cd3202e/go.mod h1:MA99N6UETSrq7/Pk/iZcgHqqiIU3tDscFNx2pGcdLlU=
|
||||
github.com/smallstep/cli v0.14.0-rc.1.0.20200111011727-83a91ec8e405/go.mod h1:MCvJvfMNtWCi/VBfXxP1JONqLLfF9TcBj1/t5Rqme90=
|
||||
github.com/smallstep/cli v0.14.0-rc.1.0.20200127233252-e55637e57819/go.mod h1:SUBVVdOk5XI7yllSupRYHzN5y4MBo89X27CN4P0d+Jw=
|
||||
github.com/smallstep/cli v0.14.0-rc.1.0.20200128213701-65805ae554f6/go.mod h1:50kmsPMAiR9XD0jHZYY19fkSSD3mKF9ztQjgtTLefjU=
|
||||
github.com/smallstep/cli v0.14.0-rc.3 h1:IphfrTtJHR2tpFiiBUKfPpO7SCGvfca72PbYJq8k1kU=
|
||||
github.com/smallstep/cli v0.14.0-rc.3/go.mod h1:5kg85FrLTaQE0JgV3IZAxuVRS7G5qvV0hxOh0u/H6IE=
|
||||
github.com/smallstep/nosql v0.1.1-0.20191009043502-4b26d8029e61/go.mod h1:MFhYHIE/0V7OOHjYzjnWHqySJ40PVbwhjy24UBkJI2g=
|
||||
github.com/smallstep/nosql v0.1.1/go.mod h1:qyxCqeyGwkuM6bfJSY3sg+aiXEiD0GbQOPzIF8/ZD8Q=
|
||||
github.com/smallstep/certificates v0.14.1 h1:dkNs0woJHUEOk7KYMsZ2eTViwPE+TkBGyseD5ctvhQo=
|
||||
github.com/smallstep/certificates v0.14.1/go.mod h1:e29f7tVH3ftStiIDnnPe44cbqgZqCTfk8h68gCutQ8A=
|
||||
github.com/smallstep/certinfo v1.2.0/go.mod h1:1gQJekdPwPvUwFWGTi7bZELmQT09cxC9wJ0VBkBNiwU=
|
||||
github.com/smallstep/cli v0.14.1 h1:iD068BTHnPYS8/ZMtaDju2x4M9c4aLklPOgwM69lCBo=
|
||||
github.com/smallstep/cli v0.14.1/go.mod h1:GamctJGsb52xWB5966pIJmbki+hOHz2J4aXg4c1mX74=
|
||||
github.com/smallstep/nosql v0.2.0 h1:IscXK9m9hRyl5GoYgn+Iml//5Bpad3LyIj6R0dZosKM=
|
||||
github.com/smallstep/nosql v0.2.0/go.mod h1:qyxCqeyGwkuM6bfJSY3sg+aiXEiD0GbQOPzIF8/ZD8Q=
|
||||
github.com/smallstep/truststore v0.9.3/go.mod h1:PRSkpRIhAYBK/KLWkHNgRdYgzWMEy45bN7PSJCfKKGE=
|
||||
github.com/smallstep/truststore v0.9.5 h1:KQ6bFXUadu3PG57sFSIBsu2pb/35NqO+MyS2Pvi62bA=
|
||||
github.com/smallstep/truststore v0.9.5/go.mod h1:HwHKRcBi0RUxxw1LYDpTRhYC4jZUuxPpkHdVonlkoDM=
|
||||
github.com/smallstep/zcrypto v0.0.0-20191008000232-9fc4bea33f70/go.mod h1:8LA6x9T22WADMj89Ksf6DnOVCOJF3zLKUdSRAcZmW4U=
|
||||
github.com/smallstep/zcrypto v0.0.0-20191122194514-76530dff70e7/go.mod h1:8LA6x9T22WADMj89Ksf6DnOVCOJF3zLKUdSRAcZmW4U=
|
||||
github.com/smallstep/zcrypto v0.0.0-20200203191936-fbc32cf76bce/go.mod h1:+F24VU3UCxfVFvvqgm5jNUFQOm/L6ed13ImwWGFgg/g=
|
||||
github.com/smallstep/zlint v0.0.0-20180727184541-d84eaafe274f/go.mod h1:GeHHT7sJDI9ti3oEaFnvx1F4N8n3ZSw2YM1+sbEoxc4=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
@@ -731,7 +691,6 @@ github.com/ultraware/funlen v0.0.1/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lP
|
||||
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
|
||||
github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.20.1-0.20181029213200-b67dcf995b6a/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
|
||||
@@ -750,7 +709,6 @@ github.com/vulcand/oxy v1.1.0/go.mod h1:ADiMYHi8gkGl2987yQIzDRoXZilANF4WtKaQ92Op
|
||||
github.com/vulcand/predicate v1.1.0/go.mod h1:mlccC5IRBoc2cIFmCB8ZM62I3VDb6p2GXESMHa3CnZg=
|
||||
github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA=
|
||||
github.com/weppos/publicsuffix-go v0.4.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
|
||||
github.com/weppos/publicsuffix-go v0.10.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
||||
@@ -762,6 +720,12 @@ github.com/yuin/goldmark v1.1.27 h1:nqDD4MMMQA0lmWq03Z2/myGPYLQoXtmi0rGVs95ntbo=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691 h1:VWSxtAiQNh3zgHJpdpkpVYjTPqRE3P6UZCOPa1nRDio=
|
||||
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691/go.mod h1:YLF3kDffRfUH/bTxOxHhV6lxwIB3Vfj91rEwNMS9MXo=
|
||||
github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE=
|
||||
github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE=
|
||||
github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is=
|
||||
github.com/zmap/zcertificate v0.0.0-20190521191901-30e388164f71/go.mod h1:gIZi1KPgkZNUQzPZXsZrNnUnxy05nTc0+tmlqvIkhRw=
|
||||
github.com/zmap/zcrypto v0.0.0-20190329181646-dff83107394d/go.mod h1:ix3q2kpLy0ibAuFXlr7qOhPKwFRRSjuynGuTR8EUPCk=
|
||||
github.com/zmap/zlint v0.0.0-20190516161541-9047d02cf65a/go.mod h1:xwLbce0UzBXp44sIAL1cii+hoK8j4AxRKlymZA2AIcY=
|
||||
go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd v3.3.13+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI=
|
||||
@@ -821,6 +785,8 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqp
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71 h1:DOmugCavvUtnUD114C1Wh+UgTgQZ4pMLzXxi1pSt+/Y=
|
||||
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -868,6 +834,7 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@@ -943,7 +910,6 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -960,6 +926,8 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200409092240-59c9f1ba88fa h1:mQTN3ECqfsViCNBgq+A40vdwhkGykrrQlYe3mPj6BoU=
|
||||
golang.org/x/sys v0.0.0-20200409092240-59c9f1ba88fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1086,8 +1054,8 @@ google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfG
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171 h1:xes2Q2k+d/+YNXVw0FpZkIDJiaux4OVrRKXRAzH6A0U=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200323114720-3f67cca34472 h1:XRuIAeTRoXziYGYTVer+YGxVXQBiOhZ8+SpNELP73oQ=
|
||||
google.golang.org/genproto v0.0.0-20200323114720-3f67cca34472/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200409111301-baae70f3302d h1:I7Vuu5Ejagca+VcgfBINHke3xwjCTYnIG4Q57fv0wYY=
|
||||
google.golang.org/genproto v0.0.0-20200409111301-baae70f3302d/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
|
||||
+44
-27
@@ -254,49 +254,66 @@ type globalListener struct {
|
||||
pc net.PacketConn
|
||||
}
|
||||
|
||||
// ParsedAddress contains the individual components
|
||||
// NetworkAddress contains the individual components
|
||||
// for a parsed network address of the form accepted
|
||||
// by ParseNetworkAddress(). Network should be a
|
||||
// network value accepted by Go's net package. Port
|
||||
// ranges are given by [StartPort, EndPort].
|
||||
type ParsedAddress struct {
|
||||
type NetworkAddress struct {
|
||||
Network string
|
||||
Host string
|
||||
StartPort uint
|
||||
EndPort uint
|
||||
}
|
||||
|
||||
// IsUnixNetwork returns true if pa.Network is
|
||||
// IsUnixNetwork returns true if na.Network is
|
||||
// unix, unixgram, or unixpacket.
|
||||
func (pa ParsedAddress) IsUnixNetwork() bool {
|
||||
return isUnixNetwork(pa.Network)
|
||||
func (na NetworkAddress) IsUnixNetwork() bool {
|
||||
return isUnixNetwork(na.Network)
|
||||
}
|
||||
|
||||
// JoinHostPort is like net.JoinHostPort, but where the port
|
||||
// is StartPort + offset.
|
||||
func (pa ParsedAddress) JoinHostPort(offset uint) string {
|
||||
if pa.IsUnixNetwork() {
|
||||
return pa.Host
|
||||
func (na NetworkAddress) JoinHostPort(offset uint) string {
|
||||
if na.IsUnixNetwork() {
|
||||
return na.Host
|
||||
}
|
||||
return net.JoinHostPort(pa.Host, strconv.Itoa(int(pa.StartPort+offset)))
|
||||
return net.JoinHostPort(na.Host, strconv.Itoa(int(na.StartPort+offset)))
|
||||
}
|
||||
|
||||
// PortRangeSize returns how many ports are in
|
||||
// pa's port range. Port ranges are inclusive,
|
||||
// so the size is the difference of start and
|
||||
// end ports plus one.
|
||||
func (pa ParsedAddress) PortRangeSize() uint {
|
||||
return (pa.EndPort - pa.StartPort) + 1
|
||||
func (na NetworkAddress) PortRangeSize() uint {
|
||||
return (na.EndPort - na.StartPort) + 1
|
||||
}
|
||||
|
||||
func (na NetworkAddress) isLoopback() bool {
|
||||
if na.IsUnixNetwork() {
|
||||
return true
|
||||
}
|
||||
if na.Host == "localhost" {
|
||||
return true
|
||||
}
|
||||
if ip := net.ParseIP(na.Host); ip != nil {
|
||||
return ip.IsLoopback()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (na NetworkAddress) port() string {
|
||||
if na.StartPort == na.EndPort {
|
||||
return strconv.FormatUint(uint64(na.StartPort), 10)
|
||||
}
|
||||
return fmt.Sprintf("%d-%d", na.StartPort, na.EndPort)
|
||||
}
|
||||
|
||||
// String reconstructs the address string to the form expected
|
||||
// by ParseNetworkAddress().
|
||||
func (pa ParsedAddress) String() string {
|
||||
port := strconv.FormatUint(uint64(pa.StartPort), 10)
|
||||
if pa.StartPort != pa.EndPort {
|
||||
port += "-" + strconv.FormatUint(uint64(pa.EndPort), 10)
|
||||
}
|
||||
return JoinNetworkAddress(pa.Network, pa.Host, port)
|
||||
// by ParseNetworkAddress(). If the address is a unix socket,
|
||||
// any non-zero port will be dropped.
|
||||
func (na NetworkAddress) String() string {
|
||||
return JoinNetworkAddress(na.Network, na.Host, na.port())
|
||||
}
|
||||
|
||||
func isUnixNetwork(netw string) bool {
|
||||
@@ -311,17 +328,17 @@ func isUnixNetwork(netw string) bool {
|
||||
//
|
||||
// Network addresses are distinct from URLs and do not
|
||||
// use URL syntax.
|
||||
func ParseNetworkAddress(addr string) (ParsedAddress, error) {
|
||||
func ParseNetworkAddress(addr string) (NetworkAddress, error) {
|
||||
var host, port string
|
||||
network, host, port, err := SplitNetworkAddress(addr)
|
||||
if network == "" {
|
||||
network = "tcp"
|
||||
}
|
||||
if err != nil {
|
||||
return ParsedAddress{}, err
|
||||
return NetworkAddress{}, err
|
||||
}
|
||||
if isUnixNetwork(network) {
|
||||
return ParsedAddress{
|
||||
return NetworkAddress{
|
||||
Network: network,
|
||||
Host: host,
|
||||
}, nil
|
||||
@@ -333,19 +350,19 @@ func ParseNetworkAddress(addr string) (ParsedAddress, error) {
|
||||
var start, end uint64
|
||||
start, err = strconv.ParseUint(ports[0], 10, 16)
|
||||
if err != nil {
|
||||
return ParsedAddress{}, fmt.Errorf("invalid start port: %v", err)
|
||||
return NetworkAddress{}, fmt.Errorf("invalid start port: %v", err)
|
||||
}
|
||||
end, err = strconv.ParseUint(ports[1], 10, 16)
|
||||
if err != nil {
|
||||
return ParsedAddress{}, fmt.Errorf("invalid end port: %v", err)
|
||||
return NetworkAddress{}, fmt.Errorf("invalid end port: %v", err)
|
||||
}
|
||||
if end < start {
|
||||
return ParsedAddress{}, fmt.Errorf("end port must not be less than start port")
|
||||
return NetworkAddress{}, fmt.Errorf("end port must not be less than start port")
|
||||
}
|
||||
if (end - start) > maxPortSpan {
|
||||
return ParsedAddress{}, fmt.Errorf("port range exceeds %d ports", maxPortSpan)
|
||||
return NetworkAddress{}, fmt.Errorf("port range exceeds %d ports", maxPortSpan)
|
||||
}
|
||||
return ParsedAddress{
|
||||
return NetworkAddress{
|
||||
Network: network,
|
||||
Host: host,
|
||||
StartPort: uint(start),
|
||||
@@ -378,7 +395,7 @@ func JoinNetworkAddress(network, host, port string) string {
|
||||
if network != "" {
|
||||
a = network + "/"
|
||||
}
|
||||
if host != "" && port == "" {
|
||||
if (host != "" && port == "") || isUnixNetwork(network) {
|
||||
a += host
|
||||
} else if port != "" {
|
||||
a += net.JoinHostPort(host, port)
|
||||
|
||||
+21
-13
@@ -138,6 +138,14 @@ func TestJoinNetworkAddress(t *testing.T) {
|
||||
network: "unix", host: "/foo/bar", port: "",
|
||||
expect: "unix//foo/bar",
|
||||
},
|
||||
{
|
||||
network: "unix", host: "/foo/bar", port: "0",
|
||||
expect: "unix//foo/bar",
|
||||
},
|
||||
{
|
||||
network: "unix", host: "/foo/bar", port: "1234",
|
||||
expect: "unix//foo/bar",
|
||||
},
|
||||
{
|
||||
network: "", host: "::1", port: "1234",
|
||||
expect: "[::1]:1234",
|
||||
@@ -153,7 +161,7 @@ func TestJoinNetworkAddress(t *testing.T) {
|
||||
func TestParseNetworkAddress(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
input string
|
||||
expectAddr ParsedAddress
|
||||
expectAddr NetworkAddress
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
@@ -166,7 +174,7 @@ func TestParseNetworkAddress(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: ":1234",
|
||||
expectAddr: ParsedAddress{
|
||||
expectAddr: NetworkAddress{
|
||||
Network: "tcp",
|
||||
Host: "",
|
||||
StartPort: 1234,
|
||||
@@ -175,7 +183,7 @@ func TestParseNetworkAddress(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: "tcp/:1234",
|
||||
expectAddr: ParsedAddress{
|
||||
expectAddr: NetworkAddress{
|
||||
Network: "tcp",
|
||||
Host: "",
|
||||
StartPort: 1234,
|
||||
@@ -184,7 +192,7 @@ func TestParseNetworkAddress(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: "tcp6/:1234",
|
||||
expectAddr: ParsedAddress{
|
||||
expectAddr: NetworkAddress{
|
||||
Network: "tcp6",
|
||||
Host: "",
|
||||
StartPort: 1234,
|
||||
@@ -193,7 +201,7 @@ func TestParseNetworkAddress(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: "tcp4/localhost:1234",
|
||||
expectAddr: ParsedAddress{
|
||||
expectAddr: NetworkAddress{
|
||||
Network: "tcp4",
|
||||
Host: "localhost",
|
||||
StartPort: 1234,
|
||||
@@ -202,14 +210,14 @@ func TestParseNetworkAddress(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: "unix//foo/bar",
|
||||
expectAddr: ParsedAddress{
|
||||
expectAddr: NetworkAddress{
|
||||
Network: "unix",
|
||||
Host: "/foo/bar",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "localhost:1234-1234",
|
||||
expectAddr: ParsedAddress{
|
||||
expectAddr: NetworkAddress{
|
||||
Network: "tcp",
|
||||
Host: "localhost",
|
||||
StartPort: 1234,
|
||||
@@ -222,7 +230,7 @@ func TestParseNetworkAddress(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: "localhost:0",
|
||||
expectAddr: ParsedAddress{
|
||||
expectAddr: NetworkAddress{
|
||||
Network: "tcp",
|
||||
Host: "localhost",
|
||||
StartPort: 0,
|
||||
@@ -253,12 +261,12 @@ func TestParseNetworkAddress(t *testing.T) {
|
||||
|
||||
func TestJoinHostPort(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
pa ParsedAddress
|
||||
pa NetworkAddress
|
||||
offset uint
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
pa: ParsedAddress{
|
||||
pa: NetworkAddress{
|
||||
Network: "tcp",
|
||||
Host: "localhost",
|
||||
StartPort: 1234,
|
||||
@@ -267,7 +275,7 @@ func TestJoinHostPort(t *testing.T) {
|
||||
expect: "localhost:1234",
|
||||
},
|
||||
{
|
||||
pa: ParsedAddress{
|
||||
pa: NetworkAddress{
|
||||
Network: "tcp",
|
||||
Host: "localhost",
|
||||
StartPort: 1234,
|
||||
@@ -276,7 +284,7 @@ func TestJoinHostPort(t *testing.T) {
|
||||
expect: "localhost:1234",
|
||||
},
|
||||
{
|
||||
pa: ParsedAddress{
|
||||
pa: NetworkAddress{
|
||||
Network: "tcp",
|
||||
Host: "localhost",
|
||||
StartPort: 1234,
|
||||
@@ -286,7 +294,7 @@ func TestJoinHostPort(t *testing.T) {
|
||||
expect: "localhost:1235",
|
||||
},
|
||||
{
|
||||
pa: ParsedAddress{
|
||||
pa: NetworkAddress{
|
||||
Network: "unix",
|
||||
Host: "/run/php/php7.3-fpm.sock",
|
||||
},
|
||||
|
||||
+25
-16
@@ -217,7 +217,7 @@ func (logging *Logging) Logger(mod Module) *zap.Logger {
|
||||
|
||||
multiCore := zapcore.NewTee(cores...)
|
||||
|
||||
return zap.New(multiCore).Named(string(modID))
|
||||
return zap.New(multiCore).Named(modID)
|
||||
}
|
||||
|
||||
// openWriter opens a writer using opener, and returns true if
|
||||
@@ -396,17 +396,6 @@ func (cl *CustomLog) provision(ctx Context, logging *Logging) error {
|
||||
}
|
||||
}
|
||||
|
||||
if cl.EncoderRaw != nil {
|
||||
mod, err := ctx.LoadModule(cl, "EncoderRaw")
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading log encoder module: %v", err)
|
||||
}
|
||||
cl.encoder = mod.(zapcore.Encoder)
|
||||
}
|
||||
if cl.encoder == nil {
|
||||
cl.encoder = newDefaultProductionLogEncoder()
|
||||
}
|
||||
|
||||
if cl.WriterRaw != nil {
|
||||
mod, err := ctx.LoadModule(cl, "WriterRaw")
|
||||
if err != nil {
|
||||
@@ -423,6 +412,24 @@ func (cl *CustomLog) provision(ctx Context, logging *Logging) error {
|
||||
return fmt.Errorf("opening log writer using %#v: %v", cl.writerOpener, err)
|
||||
}
|
||||
|
||||
if cl.EncoderRaw != nil {
|
||||
mod, err := ctx.LoadModule(cl, "EncoderRaw")
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading log encoder module: %v", err)
|
||||
}
|
||||
cl.encoder = mod.(zapcore.Encoder)
|
||||
}
|
||||
if cl.encoder == nil {
|
||||
// only allow colorized output if this log is going to stdout or stderr
|
||||
var colorize bool
|
||||
switch cl.writerOpener.(type) {
|
||||
case StdoutWriter, StderrWriter,
|
||||
*StdoutWriter, *StderrWriter:
|
||||
colorize = true
|
||||
}
|
||||
cl.encoder = newDefaultProductionLogEncoder(colorize)
|
||||
}
|
||||
|
||||
cl.buildCore()
|
||||
|
||||
return nil
|
||||
@@ -458,7 +465,7 @@ func (cl *CustomLog) buildCore() {
|
||||
}
|
||||
|
||||
func (cl *CustomLog) matchesModule(moduleID string) bool {
|
||||
return cl.loggerAllowed(string(moduleID), true)
|
||||
return cl.loggerAllowed(moduleID, true)
|
||||
}
|
||||
|
||||
// loggerAllowed returns true if name is allowed to emit
|
||||
@@ -650,7 +657,7 @@ func newDefaultProductionLog() (*defaultCustomLog, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cl.encoder = newDefaultProductionLogEncoder()
|
||||
cl.encoder = newDefaultProductionLogEncoder(true)
|
||||
cl.levelEnabler = zapcore.InfoLevel
|
||||
|
||||
cl.buildCore()
|
||||
@@ -661,14 +668,16 @@ func newDefaultProductionLog() (*defaultCustomLog, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func newDefaultProductionLogEncoder() zapcore.Encoder {
|
||||
func newDefaultProductionLogEncoder(colorize bool) zapcore.Encoder {
|
||||
encCfg := zap.NewProductionEncoderConfig()
|
||||
if terminal.IsTerminal(int(os.Stdout.Fd())) {
|
||||
// if interactive terminal, make output more human-readable by default
|
||||
encCfg.EncodeTime = func(ts time.Time, encoder zapcore.PrimitiveArrayEncoder) {
|
||||
encoder.AppendString(ts.UTC().Format("2006/01/02 15:04:05.000"))
|
||||
}
|
||||
encCfg.EncodeLevel = zapcore.CapitalColorLevelEncoder
|
||||
if colorize {
|
||||
encCfg.EncodeLevel = zapcore.CapitalColorLevelEncoder
|
||||
}
|
||||
return zapcore.NewConsoleEncoder(encCfg)
|
||||
}
|
||||
return zapcore.NewJSONEncoder(encCfg)
|
||||
|
||||
+12
-13
@@ -125,33 +125,32 @@ type ModuleMap map[string]json.RawMessage
|
||||
// be properly recorded, this should be called in the
|
||||
// init phase of runtime. Typically, the module package
|
||||
// will do this as a side-effect of being imported.
|
||||
// This function returns an error if the module's info
|
||||
// is incomplete or invalid, or if the module is
|
||||
// already registered.
|
||||
func RegisterModule(instance Module) error {
|
||||
// This function panics if the module's info is
|
||||
// incomplete or invalid, or if the module is already
|
||||
// registered.
|
||||
func RegisterModule(instance Module) {
|
||||
mod := instance.CaddyModule()
|
||||
|
||||
if mod.ID == "" {
|
||||
return fmt.Errorf("module ID missing")
|
||||
panic("module ID missing")
|
||||
}
|
||||
if mod.ID == "caddy" || mod.ID == "admin" {
|
||||
return fmt.Errorf("module ID '%s' is reserved", mod.ID)
|
||||
panic(fmt.Sprintf("module ID '%s' is reserved", mod.ID))
|
||||
}
|
||||
if mod.New == nil {
|
||||
return fmt.Errorf("missing ModuleInfo.New")
|
||||
panic("missing ModuleInfo.New")
|
||||
}
|
||||
if val := mod.New(); val == nil {
|
||||
return fmt.Errorf("ModuleInfo.New must return a non-nil module instance")
|
||||
panic("ModuleInfo.New must return a non-nil module instance")
|
||||
}
|
||||
|
||||
modulesMu.Lock()
|
||||
defer modulesMu.Unlock()
|
||||
|
||||
if _, ok := modules[string(mod.ID)]; ok {
|
||||
return fmt.Errorf("module already registered: %s", mod.ID)
|
||||
panic(fmt.Sprintf("module already registered: %s", mod.ID))
|
||||
}
|
||||
modules[string(mod.ID)] = mod
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetModule returns module information from its ID (full name).
|
||||
@@ -210,7 +209,7 @@ func GetModules(scope string) []ModuleInfo {
|
||||
var mods []ModuleInfo
|
||||
iterateModules:
|
||||
for id, m := range modules {
|
||||
modParts := strings.Split(string(id), ".")
|
||||
modParts := strings.Split(id, ".")
|
||||
|
||||
// match only the next level of nesting
|
||||
if len(modParts) != len(scopeParts)+1 {
|
||||
@@ -241,9 +240,9 @@ func Modules() []string {
|
||||
modulesMu.RLock()
|
||||
defer modulesMu.RUnlock()
|
||||
|
||||
var names []string
|
||||
names := make([]string, 0, len(modules))
|
||||
for name := range modules {
|
||||
names = append(names, string(name))
|
||||
names = append(names, name)
|
||||
}
|
||||
|
||||
sort.Strings(names)
|
||||
|
||||
@@ -30,10 +30,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
err := caddy.RegisterModule(App{})
|
||||
if err != nil {
|
||||
caddy.Log().Fatal(err.Error())
|
||||
}
|
||||
caddy.RegisterModule(App{})
|
||||
}
|
||||
|
||||
// App is a robust, production-ready HTTP server.
|
||||
|
||||
@@ -82,7 +82,7 @@ func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) er
|
||||
|
||||
// this maps domain names for automatic HTTP->HTTPS
|
||||
// redirects to their destination server address
|
||||
redirDomains := make(map[string]caddy.ParsedAddress)
|
||||
redirDomains := make(map[string]caddy.NetworkAddress)
|
||||
|
||||
for srvName, srv := range app.Servers {
|
||||
// as a prerequisite, provision route matchers; this is
|
||||
@@ -339,7 +339,7 @@ uniqueDomainsLoop:
|
||||
}
|
||||
redirTo += "{http.request.uri}"
|
||||
routes = append(routes, Route{
|
||||
MatcherSets: []MatcherSet{MatcherSet{MatchProtocol("http")}},
|
||||
MatcherSets: []MatcherSet{{MatchProtocol("http")}},
|
||||
Handlers: []MiddlewareHandler{
|
||||
StaticResponse{
|
||||
StatusCode: WeakString(strconv.Itoa(http.StatusPermanentRedirect)),
|
||||
|
||||
@@ -77,8 +77,8 @@ func (hba *HTTPBasicAuth) Provision(ctx caddy.Context) error {
|
||||
}
|
||||
|
||||
acct.Username = repl.ReplaceAll(acct.Username, "")
|
||||
acct.Password = repl.ReplaceAll(string(acct.Password), "")
|
||||
acct.Salt = repl.ReplaceAll(string(acct.Salt), "")
|
||||
acct.Password = repl.ReplaceAll(acct.Password, "")
|
||||
acct.Salt = repl.ReplaceAll(acct.Salt, "")
|
||||
|
||||
if acct.Username == "" || acct.Password == "" {
|
||||
return fmt.Errorf("account %d: username and password are required", i)
|
||||
@@ -105,20 +105,8 @@ func (hba *HTTPBasicAuth) Provision(ctx caddy.Context) error {
|
||||
// Authenticate validates the user credentials in req and returns the user, if valid.
|
||||
func (hba HTTPBasicAuth) Authenticate(w http.ResponseWriter, req *http.Request) (User, bool, error) {
|
||||
username, plaintextPasswordStr, ok := req.BasicAuth()
|
||||
|
||||
// if basic auth is missing or invalid, prompt for credentials
|
||||
if !ok {
|
||||
// browsers show a message that says something like:
|
||||
// "The website says: <realm>"
|
||||
// which is kinda dumb, but whatever.
|
||||
realm := hba.Realm
|
||||
if realm == "" {
|
||||
realm = "restricted"
|
||||
}
|
||||
|
||||
w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm="%s"`, realm))
|
||||
|
||||
return User{}, false, nil
|
||||
return hba.promptForCredentials(w, nil)
|
||||
}
|
||||
|
||||
plaintextPassword := []byte(plaintextPasswordStr)
|
||||
@@ -129,15 +117,27 @@ func (hba HTTPBasicAuth) Authenticate(w http.ResponseWriter, req *http.Request)
|
||||
|
||||
same, err := hba.Hash.Compare(account.password, plaintextPassword, account.salt)
|
||||
if err != nil {
|
||||
return User{}, false, err
|
||||
return hba.promptForCredentials(w, err)
|
||||
}
|
||||
if !same || !accountExists {
|
||||
return User{}, false, nil
|
||||
return hba.promptForCredentials(w, nil)
|
||||
}
|
||||
|
||||
return User{ID: username}, true, nil
|
||||
}
|
||||
|
||||
func (hba HTTPBasicAuth) promptForCredentials(w http.ResponseWriter, err error) (User, bool, error) {
|
||||
// browsers show a message that says something like:
|
||||
// "The website says: <realm>"
|
||||
// which is kinda dumb, but whatever.
|
||||
realm := hba.Realm
|
||||
if realm == "" {
|
||||
realm = "restricted"
|
||||
}
|
||||
w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm="%s"`, realm))
|
||||
return User{}, false, err
|
||||
}
|
||||
|
||||
// Comparer is a type that can securely compare
|
||||
// a plaintext password with a hashed password
|
||||
// in constant-time. Comparers should hash the
|
||||
|
||||
@@ -76,7 +76,7 @@ func cmdHashPassword(fs caddycmd.Flags) (int, error) {
|
||||
return caddy.ExitCodeFailedStartup, err
|
||||
}
|
||||
|
||||
hashBase64 := base64.StdEncoding.EncodeToString([]byte(hash))
|
||||
hashBase64 := base64.StdEncoding.EncodeToString(hash)
|
||||
|
||||
fmt.Println(hashBase64)
|
||||
|
||||
|
||||
@@ -30,10 +30,7 @@ import (
|
||||
func init() {
|
||||
weakrand.Seed(time.Now().UnixNano())
|
||||
|
||||
err := caddy.RegisterModule(tlsPlaceholderWrapper{})
|
||||
if err != nil {
|
||||
caddy.Log().Fatal(err.Error())
|
||||
}
|
||||
caddy.RegisterModule(tlsPlaceholderWrapper{})
|
||||
}
|
||||
|
||||
// RequestMatcher is a type that can match to a request.
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/google/cel-go/cel"
|
||||
"github.com/google/cel-go/checker/decls"
|
||||
"github.com/google/cel-go/common/types"
|
||||
@@ -43,6 +44,10 @@ func init() {
|
||||
// This enables complex logic to be expressed using a comfortable,
|
||||
// familiar syntax.
|
||||
//
|
||||
// This matcher's JSON interface is actually a string, not a struct.
|
||||
// The generated docs are not correct because this type has custom
|
||||
// marshaling logic.
|
||||
//
|
||||
// COMPATIBILITY NOTE: This module is still experimental and is not
|
||||
// subject to Caddy's compatibility guarantee.
|
||||
type MatchExpression struct {
|
||||
@@ -53,6 +58,7 @@ type MatchExpression struct {
|
||||
|
||||
expandedExpr string
|
||||
prg cel.Program
|
||||
ta ref.TypeAdapter
|
||||
}
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
@@ -79,6 +85,9 @@ func (m *MatchExpression) Provision(_ caddy.Context) error {
|
||||
// light (and possibly naïve) syntactic sugar
|
||||
m.expandedExpr = placeholderRegexp.ReplaceAllString(m.Expr, placeholderExpansion)
|
||||
|
||||
// our type adapter expands CEL's standard type support
|
||||
m.ta = celTypeAdapter{}
|
||||
|
||||
// create the CEL environment
|
||||
env, err := cel.NewEnv(
|
||||
cel.Declarations(
|
||||
@@ -88,23 +97,23 @@ func (m *MatchExpression) Provision(_ caddy.Context) error {
|
||||
[]*exprpb.Type{httpRequestObjectType, decls.String},
|
||||
decls.Any)),
|
||||
),
|
||||
cel.CustomTypeAdapter(celHTTPRequestTypeAdapter{}),
|
||||
cel.CustomTypeAdapter(m.ta),
|
||||
ext.Strings(),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting up CEL environment: %v", err)
|
||||
}
|
||||
|
||||
// parse the expression
|
||||
parsed, issues := env.Parse(m.expandedExpr)
|
||||
// parse and type-check the expression
|
||||
checked, issues := env.Compile(m.expandedExpr)
|
||||
if issues != nil && issues.Err() != nil {
|
||||
return fmt.Errorf("parsing CEL program: %s", issues.Err())
|
||||
return fmt.Errorf("compiling CEL program: %s", issues.Err())
|
||||
}
|
||||
|
||||
// type-check it
|
||||
checked, issues := env.Check(parsed)
|
||||
if issues != nil && issues.Err() != nil {
|
||||
return fmt.Errorf("type-checking CEL program: %s", issues.Err())
|
||||
// request matching is a boolean operation, so we don't really know
|
||||
// what to do if the expression returns a non-boolean type
|
||||
if !proto.Equal(checked.ResultType(), decls.Bool) {
|
||||
return fmt.Errorf("CEL request matcher expects return type of bool, not %s", checked.ResultType())
|
||||
}
|
||||
|
||||
// compile the "program"
|
||||
@@ -112,7 +121,7 @@ func (m *MatchExpression) Provision(_ caddy.Context) error {
|
||||
cel.Functions(
|
||||
&functions.Overload{
|
||||
Operator: placeholderFuncName,
|
||||
Binary: caddyPlaceholderFunc,
|
||||
Binary: m.caddyPlaceholderFunc,
|
||||
},
|
||||
),
|
||||
)
|
||||
@@ -143,14 +152,34 @@ func (m *MatchExpression) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// caddyPlaceholderFunc implements the custom CEL function that accesses the
|
||||
// Replacer on a request and gets values from it.
|
||||
func (m MatchExpression) caddyPlaceholderFunc(lhs, rhs ref.Val) ref.Val {
|
||||
celReq, ok := lhs.(celHTTPRequest)
|
||||
if !ok {
|
||||
return types.NewErr(
|
||||
"invalid request of type '%v' to "+placeholderFuncName+"(request, placeholderVarName)",
|
||||
lhs.Type())
|
||||
}
|
||||
phStr, ok := rhs.(types.String)
|
||||
if !ok {
|
||||
return types.NewErr(
|
||||
"invalid placeholder variable name of type '%v' to "+placeholderFuncName+"(request, placeholderVarName)",
|
||||
rhs.Type())
|
||||
}
|
||||
|
||||
repl := celReq.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||
val, _ := repl.Get(string(phStr))
|
||||
|
||||
return m.ta.NativeToValue(val)
|
||||
}
|
||||
|
||||
// httpRequestCELType is the type representation of a native HTTP request.
|
||||
var httpRequestCELType = types.NewTypeValue("http.Request", traits.ReceiverType)
|
||||
|
||||
// cellHTTPRequest wraps an http.Request with
|
||||
// methods to satisfy the ref.Val interface.
|
||||
type celHTTPRequest struct {
|
||||
*http.Request
|
||||
}
|
||||
type celHTTPRequest struct{ *http.Request }
|
||||
|
||||
func (cr celHTTPRequest) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
|
||||
return cr.Request, nil
|
||||
@@ -167,13 +196,15 @@ func (cr celHTTPRequest) Equal(other ref.Val) ref.Val {
|
||||
func (celHTTPRequest) Type() ref.Type { return httpRequestCELType }
|
||||
func (cr celHTTPRequest) Value() interface{} { return cr }
|
||||
|
||||
// celHTTPRequestTypeAdapter can adapt a
|
||||
// celHTTPRequest to a CEL value.
|
||||
type celHTTPRequestTypeAdapter struct{}
|
||||
// celTypeAdapter can adapt our custom types to a CEL value.
|
||||
type celTypeAdapter struct{}
|
||||
|
||||
func (celHTTPRequestTypeAdapter) NativeToValue(value interface{}) ref.Val {
|
||||
if celReq, ok := value.(celHTTPRequest); ok {
|
||||
return celReq
|
||||
func (celTypeAdapter) NativeToValue(value interface{}) ref.Val {
|
||||
switch v := value.(type) {
|
||||
case celHTTPRequest:
|
||||
return v
|
||||
case error:
|
||||
types.NewErr(v.Error())
|
||||
}
|
||||
return types.DefaultTypeAdapter.NativeToValue(value)
|
||||
}
|
||||
@@ -191,56 +222,6 @@ var httpRequestObjectType = decls.NewObjectType("http.Request")
|
||||
// The name of the CEL function which accesses Replacer values.
|
||||
const placeholderFuncName = "caddyPlaceholder"
|
||||
|
||||
// caddyPlaceholderFunc implements the custom CEL function that
|
||||
// accesses the Replacer on a request and gets values from it.
|
||||
func caddyPlaceholderFunc(lhs, rhs ref.Val) ref.Val {
|
||||
celReq, ok := lhs.(celHTTPRequest)
|
||||
if !ok {
|
||||
return types.NewErr(
|
||||
"invalid request of type '%v' to "+placeholderFuncName+"(request, placeholderVarName)",
|
||||
lhs.Type())
|
||||
}
|
||||
phStr, ok := rhs.(types.String)
|
||||
if !ok {
|
||||
return types.NewErr(
|
||||
"invalid placeholder variable name of type '%v' to "+placeholderFuncName+"(request, placeholderVarName)",
|
||||
rhs.Type())
|
||||
}
|
||||
|
||||
repl := celReq.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||
val, _ := repl.Get(string(phStr))
|
||||
|
||||
// TODO: this is... kinda awful and underwhelming, how can we expand CEL's type system more easily?
|
||||
switch v := val.(type) {
|
||||
case string:
|
||||
return types.String(v)
|
||||
case fmt.Stringer:
|
||||
return types.String(v.String())
|
||||
case error:
|
||||
return types.NewErr(v.Error())
|
||||
case int:
|
||||
return types.Int(v)
|
||||
case int32:
|
||||
return types.Int(v)
|
||||
case int64:
|
||||
return types.Int(v)
|
||||
case uint:
|
||||
return types.Int(v)
|
||||
case uint32:
|
||||
return types.Int(v)
|
||||
case uint64:
|
||||
return types.Int(v)
|
||||
case float32:
|
||||
return types.Double(v)
|
||||
case float64:
|
||||
return types.Double(v)
|
||||
case bool:
|
||||
return types.Bool(v)
|
||||
default:
|
||||
return types.String(fmt.Sprintf("%+v", v))
|
||||
}
|
||||
}
|
||||
|
||||
// Interface guards
|
||||
var (
|
||||
_ caddy.Provisioner = (*MatchExpression)(nil)
|
||||
|
||||
@@ -146,7 +146,7 @@ func parseTryFiles(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
|
||||
// if there are query strings in the list, we have to split into
|
||||
// a separate route for each item with a query string, because
|
||||
// the rewrite is different for that item
|
||||
var try []string
|
||||
try := make([]string, 0, len(tryFiles))
|
||||
for _, item := range tryFiles {
|
||||
if idx := strings.Index(item, "?"); idx >= 0 {
|
||||
if len(try) > 0 {
|
||||
|
||||
@@ -85,7 +85,7 @@ func cmdFileServer(fs caddycmd.Flags) (int, error) {
|
||||
|
||||
if domain != "" {
|
||||
route.MatcherSetsRaw = []caddy.ModuleMap{
|
||||
caddy.ModuleMap{
|
||||
{
|
||||
"host": caddyconfig.JSON(caddyhttp.MatchHost{domain}, nil),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -114,6 +114,20 @@ type (
|
||||
// true, the final result of the "not" matcher is false. Individual
|
||||
// matchers within a set work the same (i.e. different matchers in
|
||||
// the same set are AND'ed).
|
||||
//
|
||||
// Note that the generated docs which describe the structure of
|
||||
// this module are wrong because of how this type unmarshals JSON
|
||||
// in a custom way. The correct structure is:
|
||||
//
|
||||
// ```json
|
||||
// [
|
||||
// {},
|
||||
// {}
|
||||
// ]
|
||||
// ```
|
||||
//
|
||||
// where each of the array elements is a matcher set, i.e. an
|
||||
// object keyed by matcher name.
|
||||
MatchNot struct {
|
||||
MatcherSetsRaw []caddy.ModuleMap `json:"-" caddy:"namespace=http.matchers"`
|
||||
MatcherSets []MatcherSet `json:"-"`
|
||||
@@ -145,6 +159,9 @@ func (MatchHost) CaddyModule() caddy.ModuleInfo {
|
||||
func (m *MatchHost) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
for d.Next() {
|
||||
*m = append(*m, d.RemainingArgs()...)
|
||||
if d.NextBlock(0) {
|
||||
return d.Err("malformed host matcher: blocks are not supported")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -271,6 +288,9 @@ func (m MatchPath) Match(r *http.Request) bool {
|
||||
func (m *MatchPath) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
for d.Next() {
|
||||
*m = append(*m, d.RemainingArgs()...)
|
||||
if d.NextBlock(0) {
|
||||
return d.Err("malformed path matcher: blocks are not supported")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -301,6 +321,9 @@ func (MatchMethod) CaddyModule() caddy.ModuleInfo {
|
||||
func (m *MatchMethod) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
for d.Next() {
|
||||
*m = append(*m, d.RemainingArgs()...)
|
||||
if d.NextBlock(0) {
|
||||
return d.Err("malformed method matcher: blocks are not supported")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -339,6 +362,9 @@ func (m *MatchQuery) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
return d.Errf("malformed query matcher token: %s; must be in param=val format", d.Val())
|
||||
}
|
||||
url.Values(*m).Set(parts[0], parts[1])
|
||||
if d.NextBlock(0) {
|
||||
return d.Err("malformed query matcher: blocks are not supported")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -374,9 +400,12 @@ func (m *MatchHeader) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
for d.Next() {
|
||||
var field, val string
|
||||
if !d.Args(&field, &val) {
|
||||
return d.Errf("expected both field and value")
|
||||
return d.Errf("malformed header matcher: expected both field and value")
|
||||
}
|
||||
http.Header(*m).Set(field, val)
|
||||
if d.NextBlock(0) {
|
||||
return d.Err("malformed header matcher: blocks are not supported")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -461,6 +490,10 @@ func (m *MatchHeaderRE) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
}
|
||||
|
||||
(*m)[field] = &MatchRegexp{Pattern: val, Name: name}
|
||||
|
||||
if d.NextBlock(0) {
|
||||
return d.Err("malformed header_regexp matcher: blocks are not supported")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -559,7 +592,7 @@ func (m *MatchNot) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
for d.Next() {
|
||||
var mp matcherPair
|
||||
matcherMap := make(map[string]RequestMatcher)
|
||||
for d.NextBlock(0) {
|
||||
for d.NextArg() || d.NextBlock(0) {
|
||||
matcherName := d.Val()
|
||||
mod, err := caddy.GetModule("http.matchers." + matcherName)
|
||||
if err != nil {
|
||||
@@ -646,6 +679,9 @@ func (MatchRemoteIP) CaddyModule() caddy.ModuleInfo {
|
||||
func (m *MatchRemoteIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
for d.Next() {
|
||||
m.Ranges = append(m.Ranges, d.RemainingArgs()...)
|
||||
if d.NextBlock(0) {
|
||||
return d.Err("malformed remote_ip matcher: blocks are not supported")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -796,6 +832,9 @@ func (mre *MatchRegexp) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
default:
|
||||
return d.ArgErr()
|
||||
}
|
||||
if d.NextBlock(0) {
|
||||
return d.Err("malformed path_regexp matcher: blocks are not supported")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -85,6 +85,9 @@ func addHTTPVarsToReplacer(repl *caddy.Replacer, req *http.Request, w http.Respo
|
||||
return host, true
|
||||
case "http.request.port":
|
||||
_, port, _ := net.SplitHostPort(req.Host)
|
||||
if portNum, err := strconv.Atoi(port); err == nil {
|
||||
return portNum, true
|
||||
}
|
||||
return port, true
|
||||
case "http.request.hostport":
|
||||
return req.Host, true
|
||||
@@ -98,6 +101,9 @@ func addHTTPVarsToReplacer(repl *caddy.Replacer, req *http.Request, w http.Respo
|
||||
return host, true
|
||||
case "http.request.remote.port":
|
||||
_, port, _ := net.SplitHostPort(req.RemoteAddr)
|
||||
if portNum, err := strconv.Atoi(port); err == nil {
|
||||
return portNum, true
|
||||
}
|
||||
return port, true
|
||||
|
||||
// current URI, including any internal rewrites
|
||||
@@ -182,7 +188,7 @@ func addHTTPVarsToReplacer(repl *caddy.Replacer, req *http.Request, w http.Respo
|
||||
if strings.HasPrefix(key, varsReplPrefix) {
|
||||
varName := key[len(varsReplPrefix):]
|
||||
tbl := req.Context().Value(VarsCtxKey).(map[string]interface{})
|
||||
raw, _ := tbl[varName]
|
||||
raw := tbl[varName]
|
||||
// variables can be dynamic, so always return true
|
||||
// even when it may not be set; treat as empty then
|
||||
return raw, true
|
||||
|
||||
@@ -76,13 +76,13 @@ var ErrNotImplemented = fmt.Errorf("method not implemented")
|
||||
|
||||
type responseRecorder struct {
|
||||
*ResponseWriterWrapper
|
||||
wroteHeader bool
|
||||
statusCode int
|
||||
buf *bytes.Buffer
|
||||
shouldBuffer ShouldBufferFunc
|
||||
stream bool
|
||||
size int
|
||||
header http.Header
|
||||
wroteHeader bool
|
||||
stream bool
|
||||
}
|
||||
|
||||
// NewResponseRecorder returns a new ResponseRecorder that can be
|
||||
|
||||
@@ -551,26 +551,20 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
|
||||
// verify transport configuration, and finally encode it
|
||||
if transport != nil {
|
||||
// TODO: these two cases are identical, but I don't know how to reuse the code
|
||||
switch ht := transport.(type) {
|
||||
case *HTTPTransport:
|
||||
if commonScheme == "https" && ht.TLS == nil {
|
||||
ht.TLS = new(TLSConfig)
|
||||
if te, ok := transport.(TLSTransport); ok {
|
||||
if commonScheme == "https" && !te.TLSEnabled() {
|
||||
err := te.EnableTLS(new(TLSConfig))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if ht.TLS != nil && commonScheme == "http" {
|
||||
return d.Errf("upstream address scheme is HTTP but transport is configured for HTTP+TLS (HTTPS)")
|
||||
}
|
||||
|
||||
case *NTLMTransport:
|
||||
if commonScheme == "https" && ht.TLS == nil {
|
||||
ht.TLS = new(TLSConfig)
|
||||
}
|
||||
if ht.TLS != nil && commonScheme == "http" {
|
||||
if commonScheme == "http" && te.TLSEnabled() {
|
||||
return d.Errf("upstream address scheme is HTTP but transport is configured for HTTP+TLS (HTTPS)")
|
||||
}
|
||||
} else if commonScheme == "https" {
|
||||
return d.Errf("upstreams are configured for HTTPS but transport module does not support TLS: %T", transport)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(transport, new(HTTPTransport)) {
|
||||
if !reflect.DeepEqual(transport, reflect.New(reflect.TypeOf(transport).Elem()).Interface()) {
|
||||
h.TransportRaw = caddyconfig.JSONModuleObject(transport, "protocol", transportModuleName, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) {
|
||||
urlHost := fromURL.Hostname()
|
||||
if urlHost != "" {
|
||||
route.MatcherSetsRaw = []caddy.ModuleMap{
|
||||
caddy.ModuleMap{
|
||||
{
|
||||
"host": caddyconfig.JSON(caddyhttp.MatchHost{urlHost}, nil),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -181,14 +181,14 @@ func (t Transport) buildEnv(r *http.Request) (map[string]string, error) {
|
||||
|
||||
fpath := r.URL.Path
|
||||
|
||||
// Split path in preparation for env variables.
|
||||
// Previous canSplit checks ensure this can never be -1.
|
||||
// TODO: I haven't brought over canSplit from v1; make sure this doesn't break
|
||||
splitPos := t.splitPos(fpath)
|
||||
|
||||
// Request has the extension; path was split successfully
|
||||
docURI := fpath[:splitPos]
|
||||
pathInfo := fpath[splitPos:]
|
||||
// split "actual path" from "path info" if configured
|
||||
var docURI, pathInfo string
|
||||
if splitPos := t.splitPos(fpath); splitPos > -1 {
|
||||
docURI = fpath[:splitPos]
|
||||
pathInfo = fpath[splitPos:]
|
||||
} else {
|
||||
docURI = fpath
|
||||
}
|
||||
scriptName := fpath
|
||||
|
||||
// Strip PATH_INFO from SCRIPT_NAME
|
||||
|
||||
@@ -138,7 +138,7 @@ func (u *Upstream) Full() bool {
|
||||
// field is used. Note that the returned value is not a pointer.
|
||||
func (u *Upstream) fillDialInfo(r *http.Request) (DialInfo, error) {
|
||||
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||
var addr caddy.ParsedAddress
|
||||
var addr caddy.NetworkAddress
|
||||
|
||||
if u.LookupSRV != "" {
|
||||
// perform DNS lookup for SRV records and choose one
|
||||
|
||||
@@ -42,19 +42,50 @@ type HTTPTransport struct {
|
||||
// able to borrow/use at least some of these config fields; if so,
|
||||
// maybe move them into a type called CommonTransport and embed it?
|
||||
|
||||
TLS *TLSConfig `json:"tls,omitempty"`
|
||||
KeepAlive *KeepAlive `json:"keep_alive,omitempty"`
|
||||
Compression *bool `json:"compression,omitempty"`
|
||||
MaxConnsPerHost int `json:"max_conns_per_host,omitempty"`
|
||||
DialTimeout caddy.Duration `json:"dial_timeout,omitempty"`
|
||||
FallbackDelay caddy.Duration `json:"dial_fallback_delay,omitempty"`
|
||||
ResponseHeaderTimeout caddy.Duration `json:"response_header_timeout,omitempty"`
|
||||
ExpectContinueTimeout caddy.Duration `json:"expect_continue_timeout,omitempty"`
|
||||
MaxResponseHeaderSize int64 `json:"max_response_header_size,omitempty"`
|
||||
WriteBufferSize int `json:"write_buffer_size,omitempty"`
|
||||
ReadBufferSize int `json:"read_buffer_size,omitempty"`
|
||||
Versions []string `json:"versions,omitempty"`
|
||||
// Configures TLS to the upstream. Setting this to an empty struct
|
||||
// is sufficient to enable TLS with reasonable defaults.
|
||||
TLS *TLSConfig `json:"tls,omitempty"`
|
||||
|
||||
// Configures HTTP Keep-Alive (enabled by default). Should only be
|
||||
// necessary if rigorous testing has shown that tuning this helps
|
||||
// improve performance.
|
||||
KeepAlive *KeepAlive `json:"keep_alive,omitempty"`
|
||||
|
||||
// Whether to enable compression to upstream. Default: true
|
||||
Compression *bool `json:"compression,omitempty"`
|
||||
|
||||
// Maximum number of connections per host. Default: 0 (no limit)
|
||||
MaxConnsPerHost int `json:"max_conns_per_host,omitempty"`
|
||||
|
||||
// How long to wait before timing out trying to connect to
|
||||
// an upstream.
|
||||
DialTimeout caddy.Duration `json:"dial_timeout,omitempty"`
|
||||
|
||||
// How long to wait before spawning an RFC 6555 Fast Fallback
|
||||
// connection. A negative value disables this.
|
||||
FallbackDelay caddy.Duration `json:"dial_fallback_delay,omitempty"`
|
||||
|
||||
// How long to wait for reading response headers from server.
|
||||
ResponseHeaderTimeout caddy.Duration `json:"response_header_timeout,omitempty"`
|
||||
|
||||
// The length of time to wait for a server's first response
|
||||
// headers after fully writing the request headers if the
|
||||
// request has a header "Expect: 100-continue".
|
||||
ExpectContinueTimeout caddy.Duration `json:"expect_continue_timeout,omitempty"`
|
||||
|
||||
// The maximum bytes to read from response headers.
|
||||
MaxResponseHeaderSize int64 `json:"max_response_header_size,omitempty"`
|
||||
|
||||
// The size of the write buffer in bytes.
|
||||
WriteBufferSize int `json:"write_buffer_size,omitempty"`
|
||||
|
||||
// The size of the read buffer in bytes.
|
||||
ReadBufferSize int `json:"read_buffer_size,omitempty"`
|
||||
|
||||
// The versions of HTTP to support. Default: ["1.1", "2"]
|
||||
Versions []string `json:"versions,omitempty"`
|
||||
|
||||
// The pre-configured underlying HTTP transport.
|
||||
Transport *http.Transport `json:"-"`
|
||||
}
|
||||
|
||||
@@ -68,12 +99,12 @@ func (HTTPTransport) CaddyModule() caddy.ModuleInfo {
|
||||
|
||||
// Provision sets up h.Transport with a *http.Transport
|
||||
// that is ready to use.
|
||||
func (h *HTTPTransport) Provision(_ caddy.Context) error {
|
||||
func (h *HTTPTransport) Provision(ctx caddy.Context) error {
|
||||
if len(h.Versions) == 0 {
|
||||
h.Versions = []string{"1.1", "2"}
|
||||
}
|
||||
|
||||
rt, err := h.newTransport()
|
||||
rt, err := h.NewTransport(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -82,7 +113,9 @@ func (h *HTTPTransport) Provision(_ caddy.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HTTPTransport) newTransport() (*http.Transport, error) {
|
||||
// NewTransport builds a standard-lib-compatible
|
||||
// http.Transport value from h.
|
||||
func (h *HTTPTransport) NewTransport(_ caddy.Context) (*http.Transport, error) {
|
||||
dialer := &net.Dialer{
|
||||
Timeout: time.Duration(h.DialTimeout),
|
||||
FallbackDelay: time.Duration(h.FallbackDelay),
|
||||
@@ -148,14 +181,14 @@ func (h *HTTPTransport) newTransport() (*http.Transport, error) {
|
||||
|
||||
// RoundTrip implements http.RoundTripper.
|
||||
func (h *HTTPTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
h.setScheme(req)
|
||||
h.SetScheme(req)
|
||||
return h.Transport.RoundTrip(req)
|
||||
}
|
||||
|
||||
// setScheme ensures that the outbound request req
|
||||
// SetScheme ensures that the outbound request req
|
||||
// has the scheme set in its URL; the underlying
|
||||
// http.Transport requires a scheme to be set.
|
||||
func (h *HTTPTransport) setScheme(req *http.Request) {
|
||||
func (h *HTTPTransport) SetScheme(req *http.Request) {
|
||||
if req.URL.Scheme == "" {
|
||||
req.URL.Scheme = "http"
|
||||
if h.TLS != nil {
|
||||
@@ -164,6 +197,17 @@ func (h *HTTPTransport) setScheme(req *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// TLSEnabled returns true if TLS is enabled.
|
||||
func (h HTTPTransport) TLSEnabled() bool {
|
||||
return h.TLS != nil
|
||||
}
|
||||
|
||||
// EnableTLS enables TLS on the transport.
|
||||
func (h *HTTPTransport) EnableTLS(base *TLSConfig) error {
|
||||
h.TLS = base
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cleanup implements caddy.CleanerUpper and closes any idle connections.
|
||||
func (h HTTPTransport) Cleanup() error {
|
||||
if h.Transport == nil {
|
||||
@@ -173,18 +217,32 @@ func (h HTTPTransport) Cleanup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TLSConfig holds configuration related to the
|
||||
// TLS configuration for the transport/client.
|
||||
// TLSConfig holds configuration related to the TLS configuration for the
|
||||
// transport/client.
|
||||
type TLSConfig struct {
|
||||
// Optional list of base64-encoded DER-encoded CA certificates to trust.
|
||||
RootCAPool []string `json:"root_ca_pool,omitempty"`
|
||||
// Added to the same pool as above, but brought in from files
|
||||
|
||||
// List of PEM-encoded CA certificate files to add to the same trust
|
||||
// store as RootCAPool (or root_ca_pool in the JSON).
|
||||
RootCAPEMFiles []string `json:"root_ca_pem_files,omitempty"`
|
||||
// TODO: Should the client cert+key config use caddytls.CertificateLoader modules?
|
||||
ClientCertificateFile string `json:"client_certificate_file,omitempty"`
|
||||
ClientCertificateKeyFile string `json:"client_certificate_key_file,omitempty"`
|
||||
InsecureSkipVerify bool `json:"insecure_skip_verify,omitempty"`
|
||||
HandshakeTimeout caddy.Duration `json:"handshake_timeout,omitempty"`
|
||||
ServerName string `json:"server_name,omitempty"`
|
||||
|
||||
// PEM-encoded client certificate filename to present to servers.
|
||||
ClientCertificateFile string `json:"client_certificate_file,omitempty"`
|
||||
|
||||
// PEM-encoded key to use with the client certificate.
|
||||
ClientCertificateKeyFile string `json:"client_certificate_key_file,omitempty"`
|
||||
|
||||
// If true, TLS verification of server certificates will be disabled.
|
||||
// This is insecure and may be removed in the future. Do not use this
|
||||
// option except in testing or local development environments.
|
||||
InsecureSkipVerify bool `json:"insecure_skip_verify,omitempty"`
|
||||
|
||||
// The duration to allow a TLS handshake to a server.
|
||||
HandshakeTimeout caddy.Duration `json:"handshake_timeout,omitempty"`
|
||||
|
||||
// The server name (SNI) to use in TLS handshakes.
|
||||
ServerName string `json:"server_name,omitempty"`
|
||||
}
|
||||
|
||||
// MakeTLSClientConfig returns a tls.Config usable by a client to a backend.
|
||||
@@ -244,11 +302,20 @@ func (t TLSConfig) MakeTLSClientConfig() (*tls.Config, error) {
|
||||
|
||||
// KeepAlive holds configuration pertaining to HTTP Keep-Alive.
|
||||
type KeepAlive struct {
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
ProbeInterval caddy.Duration `json:"probe_interval,omitempty"`
|
||||
MaxIdleConns int `json:"max_idle_conns,omitempty"`
|
||||
MaxIdleConnsPerHost int `json:"max_idle_conns_per_host,omitempty"`
|
||||
IdleConnTimeout caddy.Duration `json:"idle_timeout,omitempty"` // how long should connections be kept alive when idle
|
||||
// Whether HTTP Keep-Alive is enabled. Default: true
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
|
||||
// How often to probe for liveness.
|
||||
ProbeInterval caddy.Duration `json:"probe_interval,omitempty"`
|
||||
|
||||
// Maximum number of idle connections.
|
||||
MaxIdleConns int `json:"max_idle_conns,omitempty"`
|
||||
|
||||
// Maximum number of idle connections per upstream host.
|
||||
MaxIdleConnsPerHost int `json:"max_idle_conns_per_host,omitempty"`
|
||||
|
||||
// How long connections should be kept alive when idle.
|
||||
IdleConnTimeout caddy.Duration `json:"idle_timeout,omitempty"`
|
||||
}
|
||||
|
||||
// decodeBase64DERCert base64-decodes, then DER-decodes, certStr.
|
||||
@@ -278,4 +345,5 @@ var (
|
||||
_ caddy.Provisioner = (*HTTPTransport)(nil)
|
||||
_ http.RoundTripper = (*HTTPTransport)(nil)
|
||||
_ caddy.CleanerUpper = (*HTTPTransport)(nil)
|
||||
_ TLSTransport = (*HTTPTransport)(nil)
|
||||
)
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
// 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 reverseproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterModule(NTLMTransport{})
|
||||
}
|
||||
|
||||
// NTLMTransport proxies HTTP with NTLM authentication.
|
||||
// It basically wraps HTTPTransport so that it is compatible with
|
||||
// NTLM's HTTP-hostile requirements. Specifically, it will use
|
||||
// HTTPTransport's single, default *http.Transport for all requests
|
||||
// (unless the client's connection is already mapped to a different
|
||||
// transport) until a request comes in with an Authorization header
|
||||
// that has "NTLM" or "Negotiate"; when that happens, NTLMTransport
|
||||
// maps the client's connection (by its address, req.RemoteAddr)
|
||||
// to a new transport that is used only by that downstream conn.
|
||||
// When the upstream connection is closed, the mapping is deleted.
|
||||
// This preserves NTLM authentication contexts by ensuring that
|
||||
// client connections use the same upstream connection. It does
|
||||
// hurt performance a bit, but that's NTLM for you.
|
||||
//
|
||||
// This transport also forces HTTP/1.1 and Keep-Alives in order
|
||||
// for NTLM to succeed.
|
||||
//
|
||||
// It is basically the same thing as
|
||||
// [nginx's paid ntlm directive](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#ntlm)
|
||||
// (but is free in Caddy!).
|
||||
type NTLMTransport struct {
|
||||
*HTTPTransport
|
||||
|
||||
transports map[string]*http.Transport
|
||||
transportsMu *sync.RWMutex
|
||||
}
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
func (NTLMTransport) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: "http.reverse_proxy.transport.http_ntlm",
|
||||
New: func() caddy.Module {
|
||||
m := new(NTLMTransport)
|
||||
m.HTTPTransport = new(HTTPTransport)
|
||||
return m
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Provision sets up the transport module.
|
||||
func (n *NTLMTransport) Provision(ctx caddy.Context) error {
|
||||
n.transports = make(map[string]*http.Transport)
|
||||
n.transportsMu = new(sync.RWMutex)
|
||||
|
||||
if n.HTTPTransport == nil {
|
||||
n.HTTPTransport = new(HTTPTransport)
|
||||
}
|
||||
|
||||
// NTLM requires HTTP/1.1
|
||||
n.HTTPTransport.Versions = []string{"1.1"}
|
||||
|
||||
// NLTM requires keep-alive
|
||||
if n.HTTPTransport.KeepAlive != nil {
|
||||
enabled := true
|
||||
n.HTTPTransport.KeepAlive.Enabled = &enabled
|
||||
}
|
||||
|
||||
// set up the underlying transport, since we
|
||||
// rely on it for the heavy lifting
|
||||
err := n.HTTPTransport.Provision(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RoundTrip implements http.RoundTripper. It basically wraps
|
||||
// the underlying HTTPTransport.Transport in a way that preserves
|
||||
// NTLM context by mapping transports/connections. Note that this
|
||||
// method does not call n.HTTPTransport.RoundTrip (our own method),
|
||||
// but the underlying n.HTTPTransport.Transport.RoundTrip (standard
|
||||
// library's method).
|
||||
func (n *NTLMTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
n.HTTPTransport.setScheme(req)
|
||||
|
||||
// when the upstream connection is closed, make sure
|
||||
// we close the downstream connection with the client
|
||||
// when this request is done; we only do this if
|
||||
// using a bound transport
|
||||
closeDownstreamIfClosedUpstream := func() {
|
||||
n.transportsMu.Lock()
|
||||
if _, ok := n.transports[req.RemoteAddr]; !ok {
|
||||
req.Close = true
|
||||
}
|
||||
n.transportsMu.Unlock()
|
||||
}
|
||||
|
||||
// first, see if this downstream connection is
|
||||
// already bound to a particular transport
|
||||
// (transports are abstractions over connections
|
||||
// to our upstream, and NTLM auth requires
|
||||
// preserving authentication state for separate
|
||||
// connections over multiple roundtrips, sigh)
|
||||
n.transportsMu.Lock()
|
||||
transport, ok := n.transports[req.RemoteAddr]
|
||||
if ok {
|
||||
n.transportsMu.Unlock()
|
||||
defer closeDownstreamIfClosedUpstream()
|
||||
return transport.RoundTrip(req)
|
||||
}
|
||||
|
||||
// otherwise, start by assuming we will use
|
||||
// the default transport that carries all
|
||||
// normal/non-NTLM-authenticated requests
|
||||
transport = n.HTTPTransport.Transport
|
||||
|
||||
// but if this request begins the NTLM authentication
|
||||
// process, we need to pin it to a specific transport
|
||||
if requestHasAuth(req) {
|
||||
var err error
|
||||
transport, err = n.newTransport()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("making new transport for %s: %v", req.RemoteAddr, err)
|
||||
}
|
||||
n.transports[req.RemoteAddr] = transport
|
||||
defer closeDownstreamIfClosedUpstream()
|
||||
}
|
||||
n.transportsMu.Unlock()
|
||||
|
||||
// finally, do the roundtrip with the transport we selected
|
||||
return transport.RoundTrip(req)
|
||||
}
|
||||
|
||||
// newTransport makes an NTLM-compatible transport.
|
||||
func (n *NTLMTransport) newTransport() (*http.Transport, error) {
|
||||
// start with a regular HTTP transport
|
||||
transport, err := n.HTTPTransport.newTransport()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// we need to wrap upstream connections so we can
|
||||
// clean up in two ways when that connection is
|
||||
// closed: 1) destroy the transport that housed
|
||||
// this connection, and 2) use that as a signal
|
||||
// to close the connection to the downstream.
|
||||
wrappedDialContext := transport.DialContext
|
||||
|
||||
transport.DialContext = func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
conn2, err := wrappedDialContext(ctx, network, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req := ctx.Value(caddyhttp.OriginalRequestCtxKey).(http.Request)
|
||||
conn := &unbinderConn{Conn: conn2, ntlm: n, clientAddr: req.RemoteAddr}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
return transport, nil
|
||||
}
|
||||
|
||||
// Cleanup implements caddy.CleanerUpper and closes any idle connections.
|
||||
func (n *NTLMTransport) Cleanup() error {
|
||||
if err := n.HTTPTransport.Cleanup(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.transportsMu.Lock()
|
||||
for _, t := range n.transports {
|
||||
t.CloseIdleConnections()
|
||||
}
|
||||
n.transports = make(map[string]*http.Transport)
|
||||
n.transportsMu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteTransportsForClient deletes (unmaps) transports that are
|
||||
// associated with clientAddr (a req.RemoteAddr value).
|
||||
func (n *NTLMTransport) deleteTransportsForClient(clientAddr string) {
|
||||
n.transportsMu.Lock()
|
||||
for key := range n.transports {
|
||||
if key == clientAddr {
|
||||
delete(n.transports, key)
|
||||
}
|
||||
}
|
||||
n.transportsMu.Unlock()
|
||||
}
|
||||
|
||||
// requestHasAuth returns true if req has an Authorization
|
||||
// header with values "NTLM" or "Negotiate".
|
||||
func requestHasAuth(req *http.Request) bool {
|
||||
for _, val := range req.Header["Authorization"] {
|
||||
if strings.HasPrefix(val, "NTLM") ||
|
||||
strings.HasPrefix(val, "Negotiate") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// unbinderConn is used to wrap upstream connections
|
||||
// so that we know when they are closed and can clean
|
||||
// up after that.
|
||||
type unbinderConn struct {
|
||||
net.Conn
|
||||
clientAddr string
|
||||
ntlm *NTLMTransport
|
||||
}
|
||||
|
||||
func (uc *unbinderConn) Close() error {
|
||||
uc.ntlm.deleteTransportsForClient(uc.clientAddr)
|
||||
return uc.Conn.Close()
|
||||
}
|
||||
|
||||
// Interface guards
|
||||
var (
|
||||
_ caddy.Provisioner = (*NTLMTransport)(nil)
|
||||
_ http.RoundTripper = (*NTLMTransport)(nil)
|
||||
_ caddy.CleanerUpper = (*NTLMTransport)(nil)
|
||||
)
|
||||
@@ -739,8 +739,19 @@ var hopHeaders = []string{
|
||||
|
||||
// DialError is an error that specifically occurs
|
||||
// in a call to Dial or DialContext.
|
||||
type DialError struct {
|
||||
error
|
||||
type DialError struct{ error }
|
||||
|
||||
// TLSTransport is implemented by transports
|
||||
// that are capable of using TLS.
|
||||
type TLSTransport interface {
|
||||
// TLSEnabled returns true if the transport
|
||||
// has TLS enabled, false otherwise.
|
||||
TLSEnabled() bool
|
||||
|
||||
// EnableTLS enables TLS within the transport
|
||||
// if it is not already, using the provided
|
||||
// value as a basis for the TLS config.
|
||||
EnableTLS(base *TLSConfig) error
|
||||
}
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
|
||||
@@ -74,6 +74,16 @@ func (r RandomSelection) Select(pool UpstreamPool, request *http.Request) *Upstr
|
||||
return randomHost
|
||||
}
|
||||
|
||||
// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
|
||||
func (r *RandomSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
for d.Next() {
|
||||
if d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RandomChoiceSelection is a policy that selects
|
||||
// two or more available hosts at random, then
|
||||
// chooses the one with the least load.
|
||||
@@ -192,6 +202,16 @@ func (LeastConnSelection) Select(pool UpstreamPool, _ *http.Request) *Upstream {
|
||||
return bestHost
|
||||
}
|
||||
|
||||
// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
|
||||
func (r *LeastConnSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
for d.Next() {
|
||||
if d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RoundRobinSelection is a policy that selects
|
||||
// a host based on round-robin ordering.
|
||||
type RoundRobinSelection struct {
|
||||
@@ -222,6 +242,16 @@ func (r *RoundRobinSelection) Select(pool UpstreamPool, _ *http.Request) *Upstre
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
|
||||
func (r *RoundRobinSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
for d.Next() {
|
||||
if d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FirstSelection is a policy that selects
|
||||
// the first available host.
|
||||
type FirstSelection struct{}
|
||||
@@ -244,6 +274,16 @@ func (FirstSelection) Select(pool UpstreamPool, _ *http.Request) *Upstream {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
|
||||
func (r *FirstSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
for d.Next() {
|
||||
if d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IPHashSelection is a policy that selects a host
|
||||
// based on hashing the remote IP of the request.
|
||||
type IPHashSelection struct{}
|
||||
@@ -265,6 +305,16 @@ func (IPHashSelection) Select(pool UpstreamPool, req *http.Request) *Upstream {
|
||||
return hostByHashing(pool, clientIP)
|
||||
}
|
||||
|
||||
// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
|
||||
func (r *IPHashSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
for d.Next() {
|
||||
if d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// URIHashSelection is a policy that selects a
|
||||
// host by hashing the request URI.
|
||||
type URIHashSelection struct{}
|
||||
@@ -282,6 +332,16 @@ func (URIHashSelection) Select(pool UpstreamPool, req *http.Request) *Upstream {
|
||||
return hostByHashing(pool, req.RequestURI)
|
||||
}
|
||||
|
||||
// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
|
||||
func (r *URIHashSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
for d.Next() {
|
||||
if d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HeaderHashSelection is a policy that selects
|
||||
// a host based on a given request header.
|
||||
type HeaderHashSelection struct {
|
||||
@@ -309,6 +369,17 @@ func (s HeaderHashSelection) Select(pool UpstreamPool, req *http.Request) *Upstr
|
||||
return hostByHashing(pool, val)
|
||||
}
|
||||
|
||||
// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
|
||||
func (s *HeaderHashSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
for d.Next() {
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
s.Field = d.Val()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// leastRequests returns the host with the
|
||||
// least number of active requests to it.
|
||||
// If more than one host has the same
|
||||
|
||||
@@ -167,7 +167,7 @@ func (routes RouteList) ProvisionHandlers(ctx caddy.Context) error {
|
||||
// This should only be done once: after all the routes have
|
||||
// been provisioned, and before serving requests.
|
||||
func (routes RouteList) Compile(next Handler) Handler {
|
||||
var mid []Middleware
|
||||
mid := make([]Middleware, 0, len(routes))
|
||||
for _, route := range routes {
|
||||
mid = append(mid, wrapRoute(route))
|
||||
}
|
||||
|
||||
@@ -170,13 +170,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
repl.Set("http.response.latency", latency)
|
||||
|
||||
logger := accLog
|
||||
if s.Logs != nil && s.Logs.LoggerNames != nil {
|
||||
if loggerName, ok := s.Logs.LoggerNames[r.Host]; ok {
|
||||
logger = logger.Named(loggerName)
|
||||
} else {
|
||||
// see if there's a default log name to attach to
|
||||
logger = logger.Named(s.Logs.LoggerNames[""])
|
||||
}
|
||||
if s.Logs != nil {
|
||||
logger = s.Logs.wrapLogger(logger, r.Host)
|
||||
}
|
||||
|
||||
log := logger.Info
|
||||
@@ -205,8 +200,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if err != nil {
|
||||
// prepare the error log
|
||||
logger := errLog
|
||||
if s.Logs != nil && s.Logs.LoggerNames != nil {
|
||||
logger = logger.Named(s.Logs.LoggerNames[r.Host])
|
||||
if s.Logs != nil {
|
||||
logger = s.Logs.wrapLogger(logger, r.Host)
|
||||
}
|
||||
|
||||
// get the values that will be used to log the error
|
||||
@@ -372,6 +367,10 @@ func (*HTTPErrorConfig) WithError(r *http.Request, err error) *http.Request {
|
||||
|
||||
// ServerLogConfig describes a server's logging configuration.
|
||||
type ServerLogConfig struct {
|
||||
// The logger name for all logs emitted by this server unless
|
||||
// the hostname is found in the LoggerNames (logger_names) map.
|
||||
LoggerName string `json:"log_name,omitempty"`
|
||||
|
||||
// LoggerNames maps request hostnames to a custom logger name.
|
||||
// For example, a mapping of "example.com" to "example" would
|
||||
// cause access logs from requests with a Host of example.com
|
||||
@@ -379,6 +378,21 @@ type ServerLogConfig struct {
|
||||
LoggerNames map[string]string `json:"logger_names,omitempty"`
|
||||
}
|
||||
|
||||
// wrapLogger wraps logger in a logger named according to user preferences for the given host.
|
||||
func (slc ServerLogConfig) wrapLogger(logger *zap.Logger, host string) *zap.Logger {
|
||||
if loggerName := slc.getLoggerName(host); loggerName != "" {
|
||||
return logger.Named(loggerName)
|
||||
}
|
||||
return logger
|
||||
}
|
||||
|
||||
func (slc ServerLogConfig) getLoggerName(host string) string {
|
||||
if loggerName, ok := slc.LoggerNames[host]; ok {
|
||||
return loggerName
|
||||
}
|
||||
return slc.LoggerName
|
||||
}
|
||||
|
||||
// errLogValues inspects err and returns the status code
|
||||
// to use, the error log message, and any extra fields.
|
||||
// If err is a HandlerError, the returned values will
|
||||
|
||||
@@ -34,11 +34,11 @@ func init() {
|
||||
// Since this handler does not write a response, the error information
|
||||
// is for use by the server to know how to handle the error.
|
||||
type StaticError struct {
|
||||
// The recommended HTTP status code. Can be either an integer or a
|
||||
// string if placeholders are needed. Optional. Default is 500.
|
||||
// The error message. Optional. Default is no error message.
|
||||
Error string `json:"error,omitempty"`
|
||||
|
||||
// The error message. Optional. Default is no error message.
|
||||
// The recommended HTTP status code. Can be either an integer or a
|
||||
// string if placeholders are needed. Optional. Default is 500.
|
||||
StatusCode WeakString `json:"status_code,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ func extractFrontMatter(input string) (map[string]interface{}, string, error) {
|
||||
// see what kind of front matter there is, if any
|
||||
var closingFence string
|
||||
var fmParser func([]byte) (map[string]interface{}, error)
|
||||
switch string(firstLine) {
|
||||
switch firstLine {
|
||||
case yamlFrontMatterFenceOpen:
|
||||
fmParser = yamlFrontMatter
|
||||
closingFence = yamlFrontMatterFenceClose
|
||||
|
||||
@@ -33,12 +33,14 @@ func init() {
|
||||
// The syntax is documented in the Go standard library's
|
||||
// [text/template package](https://golang.org/pkg/text/template/).
|
||||
//
|
||||
// ⚠️ Template functions/actions are still experimental, so they are subject to change.
|
||||
//
|
||||
// [All Sprig functions](https://masterminds.github.io/sprig/) are supported.
|
||||
//
|
||||
// In addition to the standard functions and Sprig functions, Caddy adds
|
||||
// extra functions and data that are available to a template:
|
||||
//
|
||||
// ##### **`.Args`**
|
||||
// ##### `.Args`
|
||||
//
|
||||
// Access arguments passed to this page/context, for example as the result of a `include`.
|
||||
//
|
||||
@@ -54,6 +56,14 @@ func init() {
|
||||
// {{.Cookie "cookiename"}}
|
||||
// ```
|
||||
//
|
||||
// ##### `env`
|
||||
//
|
||||
// Gets an environment variable.
|
||||
//
|
||||
// ```
|
||||
// {{env "VAR_NAME"}}
|
||||
// ```
|
||||
//
|
||||
// ##### `.Host`
|
||||
//
|
||||
// Returns the hostname portion (no port) of the Host header of the HTTP request.
|
||||
|
||||
@@ -17,14 +17,15 @@ package templates
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
|
||||
"github.com/Masterminds/sprig/v3"
|
||||
"github.com/alecthomas/chroma/formatters/html"
|
||||
@@ -57,7 +58,7 @@ func (c templateContext) OriginalReq() http.Request {
|
||||
// Note that included files are NOT escaped, so you should only include
|
||||
// trusted files. If it is not trusted, be sure to use escaping functions
|
||||
// in your template.
|
||||
func (c templateContext) funcInclude(filename string, args ...interface{}) (template.HTML, error) {
|
||||
func (c templateContext) funcInclude(filename string, args ...interface{}) (string, error) {
|
||||
if c.Root == nil {
|
||||
return "", fmt.Errorf("root file system not specified")
|
||||
}
|
||||
@@ -84,14 +85,14 @@ func (c templateContext) funcInclude(filename string, args ...interface{}) (temp
|
||||
return "", err
|
||||
}
|
||||
|
||||
return template.HTML(bodyBuf.String()), nil
|
||||
return bodyBuf.String(), nil
|
||||
}
|
||||
|
||||
// funcHTTPInclude returns the body of a virtual (lightweight) request
|
||||
// to the given URI on the same server. Note that included bodies
|
||||
// are NOT escaped, so you should only include trusted resources.
|
||||
// If it is not trusted, be sure to use escaping functions yourself.
|
||||
func (c templateContext) funcHTTPInclude(uri string) (template.HTML, error) {
|
||||
func (c templateContext) funcHTTPInclude(uri string) (string, error) {
|
||||
// prevent virtual request loops by counting how many levels
|
||||
// deep we are; and if we get too deep, return an error
|
||||
recursionCount := 1
|
||||
@@ -132,7 +133,7 @@ func (c templateContext) funcHTTPInclude(uri string) (template.HTML, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return template.HTML(buf.String()), nil
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func (c templateContext) executeTemplateInBuffer(tplName string, buf *bytes.Buffer) error {
|
||||
@@ -150,6 +151,7 @@ func (c templateContext) executeTemplateInBuffer(tplName string, buf *bytes.Buff
|
||||
"markdown": c.funcMarkdown,
|
||||
"splitFrontMatter": c.funcSplitFrontMatter,
|
||||
"listFiles": c.funcListFiles,
|
||||
"env": c.funcEnv,
|
||||
})
|
||||
|
||||
parsedTpl, err := tpl.Parse(buf.String())
|
||||
@@ -162,6 +164,10 @@ func (c templateContext) executeTemplateInBuffer(tplName string, buf *bytes.Buff
|
||||
return parsedTpl.Execute(buf, c)
|
||||
}
|
||||
|
||||
func (templateContext) funcEnv(varName string) string {
|
||||
return os.Getenv(varName)
|
||||
}
|
||||
|
||||
// Cookie gets the value of a cookie with name name.
|
||||
func (c templateContext) Cookie(name string) string {
|
||||
cookies := c.Req.Cookies()
|
||||
@@ -198,7 +204,7 @@ func (c templateContext) Host() (string, error) {
|
||||
|
||||
// funcStripHTML returns s without HTML tags. It is fairly naive
|
||||
// but works with most valid HTML inputs.
|
||||
func (c templateContext) funcStripHTML(s string) string {
|
||||
func (templateContext) funcStripHTML(s string) string {
|
||||
var buf bytes.Buffer
|
||||
var inTag, inQuotes bool
|
||||
var tagStart int
|
||||
@@ -231,7 +237,7 @@ func (c templateContext) funcStripHTML(s string) string {
|
||||
|
||||
// funcMarkdown renders the markdown body as HTML. The resulting
|
||||
// HTML is NOT escaped so that it can be rendered as HTML.
|
||||
func (c templateContext) funcMarkdown(input interface{}) (template.HTML, error) {
|
||||
func (templateContext) funcMarkdown(input interface{}) (string, error) {
|
||||
inputStr := toString(input)
|
||||
|
||||
md := goldmark.New(
|
||||
@@ -259,13 +265,13 @@ func (c templateContext) funcMarkdown(input interface{}) (template.HTML, error)
|
||||
|
||||
md.Convert([]byte(inputStr), buf)
|
||||
|
||||
return template.HTML(buf.String()), nil
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
// splitFrontMatter parses front matter out from the beginning of input,
|
||||
// and returns the separated key-value pairs and the body/content. input
|
||||
// must be a "stringy" value.
|
||||
func (c templateContext) funcSplitFrontMatter(input interface{}) (parsedMarkdownDoc, error) {
|
||||
func (templateContext) funcSplitFrontMatter(input interface{}) (parsedMarkdownDoc, error) {
|
||||
meta, body, err := extractFrontMatter(toString(input))
|
||||
if err != nil {
|
||||
return parsedMarkdownDoc{}, err
|
||||
@@ -338,14 +344,12 @@ func toString(input interface{}) string {
|
||||
switch v := input.(type) {
|
||||
case string:
|
||||
return v
|
||||
case template.HTML:
|
||||
return string(v)
|
||||
case fmt.Stringer:
|
||||
return v.String()
|
||||
case error:
|
||||
return v.Error()
|
||||
default:
|
||||
return fmt.Sprintf("%s", input)
|
||||
return fmt.Sprintf("%v", input)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,6 +361,6 @@ var bufPool = sync.Pool{
|
||||
|
||||
// at time of writing, sprig.FuncMap() makes a copy, thus
|
||||
// involves iterating the whole map, so do it just once
|
||||
var sprigFuncMap = sprig.FuncMap()
|
||||
var sprigFuncMap = sprig.TxtFuncMap()
|
||||
|
||||
const recursionPreventionHeader = "Caddy-Templates-Include"
|
||||
|
||||
@@ -31,7 +31,6 @@ package templates
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -48,7 +47,7 @@ func TestMarkdown(t *testing.T) {
|
||||
|
||||
for i, test := range []struct {
|
||||
body string
|
||||
expect template.HTML
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
body: "- str1\n- str2\n",
|
||||
|
||||
@@ -153,6 +153,7 @@ func (m *ACMEIssuer) makeIssuerTemplate() (certmagic.ACMEManager, error) {
|
||||
template.AltTLSALPNPort = m.Challenges.TLSALPN.AlternatePort
|
||||
}
|
||||
template.DNSProvider = m.Challenges.DNS
|
||||
template.ListenHost = m.Challenges.BindHost
|
||||
}
|
||||
|
||||
return template, nil
|
||||
|
||||
@@ -53,7 +53,8 @@ type AutomationConfig struct {
|
||||
// a low value.
|
||||
RenewCheckInterval caddy.Duration `json:"renew_interval,omitempty"`
|
||||
|
||||
defaultAutomationPolicy *AutomationPolicy
|
||||
defaultPublicAutomationPolicy *AutomationPolicy
|
||||
defaultInternalAutomationPolicy *AutomationPolicy // only initialized if necessary
|
||||
}
|
||||
|
||||
// AutomationPolicy designates the policy for automating the
|
||||
@@ -67,7 +68,8 @@ type AutomationPolicy struct {
|
||||
// Which subjects (hostnames or IP addresses) this policy applies to.
|
||||
Subjects []string `json:"subjects,omitempty"`
|
||||
|
||||
// The module that will issue certificates. Default: acme
|
||||
// The module that will issue certificates. Default: internal if all
|
||||
// subjects do not qualify for public certificates; othewise acme.
|
||||
IssuerRaw json.RawMessage `json:"issuer,omitempty" caddy:"namespace=tls.issuance inline_key=module"`
|
||||
|
||||
// If true, certificates will be requested with MustStaple. Not all
|
||||
@@ -148,7 +150,7 @@ func (ap *AutomationPolicy) Provision(tlsApp *TLS) error {
|
||||
// none of the subjects qualify for a public certificate,
|
||||
// set the issuer to internal so that these names can all
|
||||
// get certificates; critically, we can only do this if an
|
||||
// issuer is not explictly configured (IssuerRaw, vs. just
|
||||
// issuer is not explicitly configured (IssuerRaw, vs. just
|
||||
// Issuer) AND if the list of subjects is non-empty
|
||||
if ap.IssuerRaw == nil && len(ap.Subjects) > 0 {
|
||||
var anyPublic bool
|
||||
@@ -239,6 +241,10 @@ type ChallengesConfig struct {
|
||||
// to Caddy from an external server.
|
||||
DNSRaw json.RawMessage `json:"dns,omitempty" caddy:"namespace=tls.dns inline_key=provider"`
|
||||
|
||||
// Optionally customize the host to which a listener
|
||||
// is bound if required for solving a challenge.
|
||||
BindHost string `json:"bind_host,omitempty"`
|
||||
|
||||
DNS challenge.Provider `json:"-"`
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ type CertKeyFilePair struct {
|
||||
|
||||
// LoadCertificates returns the certificates to be loaded by fl.
|
||||
func (fl FileLoader) LoadCertificates() ([]Certificate, error) {
|
||||
var certs []Certificate
|
||||
certs := make([]Certificate, 0, len(fl))
|
||||
for _, pair := range fl {
|
||||
certData, err := ioutil.ReadFile(pair.Certificate)
|
||||
if err != nil {
|
||||
|
||||
@@ -54,7 +54,7 @@ type CertKeyPEMPair struct {
|
||||
|
||||
// LoadCertificates returns the certificates contained in pl.
|
||||
func (pl PEMLoader) LoadCertificates() ([]Certificate, error) {
|
||||
var certs []Certificate
|
||||
certs := make([]Certificate, 0, len(pl))
|
||||
for i, pair := range pl {
|
||||
cert, err := tls.X509KeyPair([]byte(pair.CertificatePEM), []byte(pair.KeyPEM))
|
||||
if err != nil {
|
||||
|
||||
+43
-21
@@ -89,22 +89,6 @@ func (t *TLS) Provision(ctx caddy.Context) error {
|
||||
}
|
||||
t.certCache = certmagic.NewCache(cacheOpts)
|
||||
|
||||
// automation/management policies
|
||||
if t.Automation == nil {
|
||||
t.Automation = new(AutomationConfig)
|
||||
}
|
||||
t.Automation.defaultAutomationPolicy = new(AutomationPolicy)
|
||||
err := t.Automation.defaultAutomationPolicy.Provision(t)
|
||||
if err != nil {
|
||||
return fmt.Errorf("provisioning default automation policy: %v", err)
|
||||
}
|
||||
for i, ap := range t.Automation.Policies {
|
||||
err := ap.Provision(t)
|
||||
if err != nil {
|
||||
return fmt.Errorf("provisioning automation policy %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// certificate loaders
|
||||
val, err := ctx.LoadModule(t, "CertificatesRaw")
|
||||
if err != nil {
|
||||
@@ -112,9 +96,8 @@ func (t *TLS) Provision(ctx caddy.Context) error {
|
||||
}
|
||||
for modName, modIface := range val.(map[string]interface{}) {
|
||||
if modName == "automate" {
|
||||
// special case; these will be loaded in later
|
||||
// using our automation facilities, which we
|
||||
// want to avoid during provisioning
|
||||
// special case; these will be loaded in later using our automation facilities,
|
||||
// which we want to avoid doing during provisioning
|
||||
if automateNames, ok := modIface.(*AutomateLoader); ok && automateNames != nil {
|
||||
t.automateNames = []string(*automateNames)
|
||||
} else {
|
||||
@@ -125,6 +108,38 @@ func (t *TLS) Provision(ctx caddy.Context) error {
|
||||
t.certificateLoaders = append(t.certificateLoaders, modIface.(CertificateLoader))
|
||||
}
|
||||
|
||||
// automation/management policies
|
||||
if t.Automation == nil {
|
||||
t.Automation = new(AutomationConfig)
|
||||
}
|
||||
t.Automation.defaultPublicAutomationPolicy = new(AutomationPolicy)
|
||||
err = t.Automation.defaultPublicAutomationPolicy.Provision(t)
|
||||
if err != nil {
|
||||
return fmt.Errorf("provisioning default public automation policy: %v", err)
|
||||
}
|
||||
for _, n := range t.automateNames {
|
||||
// if any names specified by the "automate" loader do not qualify for a public
|
||||
// certificate, we should initialize a default internal automation policy
|
||||
// (but we don't want to do this unnecessarily, since it may prompt for password!)
|
||||
if certmagic.SubjectQualifiesForPublicCert(n) {
|
||||
continue
|
||||
}
|
||||
t.Automation.defaultInternalAutomationPolicy = &AutomationPolicy{
|
||||
IssuerRaw: json.RawMessage(`{"module":"internal"}`),
|
||||
}
|
||||
err = t.Automation.defaultInternalAutomationPolicy.Provision(t)
|
||||
if err != nil {
|
||||
return fmt.Errorf("provisioning default internal automation policy: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
for i, ap := range t.Automation.Policies {
|
||||
err := ap.Provision(t)
|
||||
if err != nil {
|
||||
return fmt.Errorf("provisioning automation policy %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// session ticket ephemeral keys (STEK) service and provider
|
||||
if t.SessionTickets != nil {
|
||||
err := t.SessionTickets.provision(ctx)
|
||||
@@ -318,6 +333,10 @@ func (t *TLS) getConfigForName(name string) *certmagic.Config {
|
||||
return ap.magic
|
||||
}
|
||||
|
||||
// getAutomationPolicyForName returns the first matching automation policy
|
||||
// for the given subject name. If no matching policy can be found, the
|
||||
// default policy is used, depending on whether the name qualifies for a
|
||||
// public certificate or not.
|
||||
func (t *TLS) getAutomationPolicyForName(name string) *AutomationPolicy {
|
||||
for _, ap := range t.Automation.Policies {
|
||||
if len(ap.Subjects) == 0 {
|
||||
@@ -329,7 +348,10 @@ func (t *TLS) getAutomationPolicyForName(name string) *AutomationPolicy {
|
||||
}
|
||||
}
|
||||
}
|
||||
return t.Automation.defaultAutomationPolicy
|
||||
if certmagic.SubjectQualifiesForPublicCert(name) || t.Automation.defaultInternalAutomationPolicy == nil {
|
||||
return t.Automation.defaultPublicAutomationPolicy
|
||||
}
|
||||
return t.Automation.defaultInternalAutomationPolicy
|
||||
}
|
||||
|
||||
// AllMatchingCertificates returns the list of all certificates in
|
||||
@@ -457,7 +479,7 @@ func (t *TLS) moveCertificates() error {
|
||||
}
|
||||
|
||||
// get list of used CAs
|
||||
var oldCANames []string
|
||||
oldCANames := make([]string, 0, len(oldAcmeCas))
|
||||
for _, fi := range oldAcmeCas {
|
||||
if !fi.IsDir() {
|
||||
continue
|
||||
|
||||
@@ -31,7 +31,7 @@ func init() {
|
||||
type NetWriter struct {
|
||||
Address string `json:"address,omitempty"`
|
||||
|
||||
addr caddy.ParsedAddress
|
||||
addr caddy.NetworkAddress
|
||||
}
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
|
||||
Reference in New Issue
Block a user