Merge branch 'master' into fastcgi_buffer

This commit is contained in:
Matt Holt 2025-01-02 11:12:07 -07:00 committed by GitHub
commit 13a4f0f6fb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 449 additions and 52 deletions

10
.github/SECURITY.md vendored
View File

@ -5,11 +5,11 @@ The Caddy project would like to make sure that it stays on top of all practicall
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 2.x | ✔️ |
| 1.x | :x: |
| < 1.x | :x: |
| Version | Supported |
| -------- | ----------|
| 2.latest | ✔️ |
| 1.x | :x: |
| < 1.x | :x: |
## Acceptable Scope

View File

@ -725,8 +725,10 @@ func Validate(cfg *Config) error {
// Errors are logged along the way, and an appropriate exit
// code is emitted.
func exitProcess(ctx context.Context, logger *zap.Logger) {
// let the rest of the program know we're quitting
atomic.StoreInt32(exiting, 1)
// let the rest of the program know we're quitting; only do it once
if !atomic.CompareAndSwapInt32(exiting, 0, 1) {
return
}
// give the OS or service/process manager our 2 weeks' notice: we quit
if err := notify.Stopping(); err != nil {

View File

@ -24,7 +24,7 @@ import (
"time"
"github.com/caddyserver/certmagic"
"github.com/mholt/acmez/v2/acme"
"github.com/mholt/acmez/v3/acme"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2"
@ -84,7 +84,7 @@ func parseBind(h Helper) ([]ConfigValue, error) {
// parseTLS parses the tls directive. Syntax:
//
// tls [<email>|internal]|[<cert_file> <key_file>] {
// tls [<email>|internal|force_automate]|[<cert_file> <key_file>] {
// protocols <min> [<max>]
// ciphers <cipher_suites...>
// curves <curves...>
@ -107,6 +107,7 @@ func parseBind(h Helper) ([]ConfigValue, error) {
// dns_challenge_override_domain <domain>
// on_demand
// reuse_private_keys
// force_automate
// eab <key_id> <mac_key>
// issuer <module_name> [...]
// get_certificate <module_name> [...]
@ -126,6 +127,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
var certManagers []certmagic.Manager
var onDemand bool
var reusePrivateKeys bool
var forceAutomate bool
firstLine := h.RemainingArgs()
switch len(firstLine) {
@ -133,8 +135,10 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
case 1:
if firstLine[0] == "internal" {
internalIssuer = new(caddytls.InternalIssuer)
} else if firstLine[0] == "force_automate" {
forceAutomate = true
} else if !strings.Contains(firstLine[0], "@") {
return nil, h.Err("single argument must either be 'internal' or an email address")
return nil, h.Err("single argument must either be 'internal', 'force_automate', or an email address")
} else {
acmeIssuer = &caddytls.ACMEIssuer{
Email: firstLine[0],
@ -569,6 +573,15 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
})
}
// if enabled, the names in the site addresses will be
// added to the automation policies
if forceAutomate {
configVals = append(configVals, ConfigValue{
Class: "tls.force_automate",
Value: true,
})
}
// custom certificate selection
if len(certSelector.AnyTag) > 0 {
cp.CertSelection = &certSelector

View File

@ -763,6 +763,14 @@ func (st *ServerType) serversFromPairings(
}
}
// collect hosts that are forced to be automated
forceAutomatedNames := make(map[string]struct{})
if _, ok := sblock.pile["tls.force_automate"]; ok {
for _, host := range hosts {
forceAutomatedNames[host] = struct{}{}
}
}
// tls: connection policies
if cpVals, ok := sblock.pile["tls.connection_policy"]; ok {
// tls connection policies
@ -794,7 +802,7 @@ func (st *ServerType) serversFromPairings(
}
// only append this policy if it actually changes something
if !cp.SettingsEmpty() {
if !cp.SettingsEmpty() || mapContains(forceAutomatedNames, hosts) {
srv.TLSConnPolicies = append(srv.TLSConnPolicies, cp)
hasCatchAllTLSConnPolicy = len(hosts) == 0
}
@ -1661,6 +1669,18 @@ func listenersUseAnyPortOtherThan(addresses []string, otherPort string) bool {
return false
}
func mapContains[K comparable, V any](m map[K]V, keys []K) bool {
if len(m) == 0 || len(keys) == 0 {
return false
}
for _, key := range keys {
if _, ok := m[key]; ok {
return true
}
}
return false
}
// specificity returns len(s) minus any wildcards (*) and
// placeholders ({...}). Basically, it's a length count
// that penalizes the use of wildcards and placeholders.

View File

@ -19,7 +19,7 @@ import (
"strconv"
"github.com/caddyserver/certmagic"
"github.com/mholt/acmez/v2/acme"
"github.com/mholt/acmez/v3/acme"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig"

View File

@ -25,7 +25,7 @@ import (
"strings"
"github.com/caddyserver/certmagic"
"github.com/mholt/acmez/v2/acme"
"github.com/mholt/acmez/v3/acme"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig"
@ -94,6 +94,9 @@ func (st ServerType) buildTLSApp(
// collect all hosts that have a wildcard in them, and arent HTTP
wildcardHosts := []string{}
// hosts that have been explicitly marked to be automated,
// even if covered by another wildcard
forcedAutomatedNames := make(map[string]struct{})
for _, p := range pairings {
var addresses []string
for _, addressWithProtocols := range p.addressesWithProtocols {
@ -150,6 +153,13 @@ func (st ServerType) buildTLSApp(
ap.OnDemand = true
}
// collect hosts that are forced to be automated
if _, ok := sblock.pile["tls.force_automate"]; ok {
for _, host := range sblockHosts {
forcedAutomatedNames[host] = struct{}{}
}
}
// reuse private keys tls
if _, ok := sblock.pile["tls.reuse_private_keys"]; ok {
ap.ReusePrivateKeys = true
@ -407,6 +417,13 @@ func (st ServerType) buildTLSApp(
}
}
}
for name := range forcedAutomatedNames {
if slices.Contains(al, name) {
continue
}
al = append(al, name)
}
slices.Sort(al) // to stabilize the adapt output
if len(al) > 0 {
tlsApp.CertificatesRaw["automate"] = caddyconfig.JSON(al, &warnings)
}

View File

@ -6,6 +6,7 @@ import (
"crypto/elliptic"
"crypto/rand"
"fmt"
"log/slog"
"net"
"net/http"
"strings"
@ -13,10 +14,11 @@ import (
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddytest"
"github.com/mholt/acmez/v2"
"github.com/mholt/acmez/v2/acme"
"github.com/mholt/acmez/v3"
"github.com/mholt/acmez/v3/acme"
smallstepacme "github.com/smallstep/certificates/acme"
"go.uber.org/zap"
"go.uber.org/zap/exp/zapslog"
)
const acmeChallengePort = 9081
@ -48,7 +50,7 @@ func TestACMEServerWithDefaults(t *testing.T) {
Client: &acme.Client{
Directory: "https://acme.localhost:9443/acme/local/directory",
HTTPClient: tester.Client,
Logger: logger,
Logger: slog.New(zapslog.NewHandler(logger.Core())),
},
ChallengeSolvers: map[string]acmez.Solver{
acme.ChallengeTypeHTTP01: &naiveHTTPSolver{logger: logger},
@ -117,7 +119,7 @@ func TestACMEServerWithMismatchedChallenges(t *testing.T) {
Client: &acme.Client{
Directory: "https://acme.localhost:9443/acme/local/directory",
HTTPClient: tester.Client,
Logger: logger,
Logger: slog.New(zapslog.NewHandler(logger.Core())),
},
ChallengeSolvers: map[string]acmez.Solver{
acme.ChallengeTypeHTTP01: &naiveHTTPSolver{logger: logger},

View File

@ -5,13 +5,15 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"log/slog"
"strings"
"testing"
"github.com/caddyserver/caddy/v2/caddytest"
"github.com/mholt/acmez/v2"
"github.com/mholt/acmez/v2/acme"
"github.com/mholt/acmez/v3"
"github.com/mholt/acmez/v3/acme"
"go.uber.org/zap"
"go.uber.org/zap/exp/zapslog"
)
func TestACMEServerDirectory(t *testing.T) {
@ -76,7 +78,7 @@ func TestACMEServerAllowPolicy(t *testing.T) {
Client: &acme.Client{
Directory: "https://acme.localhost:9443/acme/local/directory",
HTTPClient: tester.Client,
Logger: logger,
Logger: slog.New(zapslog.NewHandler(logger.Core())),
},
ChallengeSolvers: map[string]acmez.Solver{
acme.ChallengeTypeHTTP01: &naiveHTTPSolver{logger: logger},
@ -165,7 +167,7 @@ func TestACMEServerDenyPolicy(t *testing.T) {
Client: &acme.Client{
Directory: "https://acme.localhost:9443/acme/local/directory",
HTTPClient: tester.Client,
Logger: logger,
Logger: slog.New(zapslog.NewHandler(logger.Core())),
},
ChallengeSolvers: map[string]acmez.Solver{
acme.ChallengeTypeHTTP01: &naiveHTTPSolver{logger: logger},

View File

@ -0,0 +1,180 @@
automated1.example.com {
tls force_automate
respond "Automated!"
}
automated2.example.com {
tls force_automate
respond "Automated!"
}
shadowed.example.com {
respond "Shadowed!"
}
*.example.com {
tls cert.pem key.pem
respond "Wildcard!"
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"automated1.example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "Automated!",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
},
{
"match": [
{
"host": [
"automated2.example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "Automated!",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
},
{
"match": [
{
"host": [
"shadowed.example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "Shadowed!",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
},
{
"match": [
{
"host": [
"*.example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "Wildcard!",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
}
],
"tls_connection_policies": [
{
"match": {
"sni": [
"automated1.example.com"
]
}
},
{
"match": {
"sni": [
"automated2.example.com"
]
}
},
{
"match": {
"sni": [
"*.example.com"
]
},
"certificate_selection": {
"any_tag": [
"cert0"
]
}
},
{}
]
}
}
},
"tls": {
"certificates": {
"automate": [
"automated1.example.com",
"automated2.example.com"
],
"load_files": [
{
"certificate": "cert.pem",
"key": "key.pem",
"tags": [
"cert0"
]
}
]
}
}
}
}

View File

@ -0,0 +1,102 @@
subdomain.example.com {
respond "Subdomain!"
}
*.example.com {
tls cert.pem key.pem
respond "Wildcard!"
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"subdomain.example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "Subdomain!",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
},
{
"match": [
{
"host": [
"*.example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "Wildcard!",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
}
],
"tls_connection_policies": [
{
"match": {
"sni": [
"*.example.com"
]
},
"certificate_selection": {
"any_tag": [
"cert0"
]
}
},
{}
]
}
}
},
"tls": {
"certificates": {
"load_files": [
{
"certificate": "cert.pem",
"key": "key.pem",
"tags": [
"cert0"
]
}
]
}
}
}
}

View File

@ -1,8 +1,3 @@
// The below line is required to enable post-quantum key agreement in Go 1.23
// by default without insisting on setting a minimum version of 1.23 in go.mod.
// See https://github.com/caddyserver/caddy/issues/6540#issuecomment-2313094905
//go:debug tlskyber=1
// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");

10
go.mod
View File

@ -9,15 +9,15 @@ require (
github.com/Masterminds/sprig/v3 v3.3.0
github.com/alecthomas/chroma/v2 v2.14.0
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b
github.com/caddyserver/certmagic v0.21.5-0.20241105180249-4293198e094d
github.com/caddyserver/certmagic v0.21.5
github.com/caddyserver/zerossl v0.1.3
github.com/dustin/go-humanize v1.0.1
github.com/go-chi/chi/v5 v5.0.12
github.com/google/cel-go v0.21.0
github.com/google/uuid v1.6.0
github.com/klauspost/compress v1.17.11
github.com/klauspost/cpuid/v2 v2.2.8
github.com/mholt/acmez/v2 v2.0.3
github.com/klauspost/cpuid/v2 v2.2.9
github.com/mholt/acmez/v3 v3.0.0
github.com/prometheus/client_golang v1.19.1
github.com/quic-go/quic-go v0.48.2
github.com/smallstep/certificates v0.26.1
@ -37,9 +37,9 @@ require (
go.uber.org/automaxprocs v1.6.0
go.uber.org/zap v1.27.0
go.uber.org/zap/exp v0.3.0
golang.org/x/crypto v0.30.0
golang.org/x/crypto v0.31.0
golang.org/x/crypto/x509roots/fallback v0.0.0-20241104001025-71ed71b4faf9
golang.org/x/net v0.32.0
golang.org/x/net v0.33.0
golang.org/x/sync v0.10.0
golang.org/x/term v0.27.0
golang.org/x/time v0.7.0

20
go.sum
View File

@ -89,8 +89,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/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.21.5-0.20241105180249-4293198e094d h1:+zOduGxxC4WBAnlDf5Uf0TXbWXRqjUXkJKevDZZa79A=
github.com/caddyserver/certmagic v0.21.5-0.20241105180249-4293198e094d/go.mod h1:swUXjQ1T9ZtMv95qj7/InJvWLXURU85r+CfG0T+ZbDE=
github.com/caddyserver/certmagic v0.21.5 h1:iIga4nZRgd27EIEbX7RZmoRMul+EVBn/h7bAGL83dnY=
github.com/caddyserver/certmagic v0.21.5/go.mod h1:n1sCo7zV1Ez2j+89wrzDxo4N/T1Ws/Vx8u5NvuBFabw=
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
@ -304,8 +304,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@ -344,8 +344,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mholt/acmez/v2 v2.0.3 h1:CgDBlEwg3QBp6s45tPQmFIBrkRIkBT4rW4orMM6p4sw=
github.com/mholt/acmez/v2 v2.0.3/go.mod h1:pQ1ysaDeGrIMvJ9dfJMk5kJNkn7L2sb3UhyrX6Q91cw=
github.com/mholt/acmez/v3 v3.0.0 h1:r1NcjuWR0VaKP2BTjDK9LRFBw/WvURx3jlaEUl9Ht8E=
github.com/mholt/acmez/v3 v3.0.0/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
@ -595,8 +595,8 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto/x509roots/fallback v0.0.0-20241104001025-71ed71b4faf9 h1:4cEcP5+OjGppY79LCQ5Go2B1Boix2x0v6pvA01P3FoA=
golang.org/x/crypto/x509roots/fallback v0.0.0-20241104001025-71ed71b4faf9/go.mod h1:kNa9WdvYnzFwC79zRpLRMJbdEFlhyM5RPFBBZp/wWH8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -628,8 +628,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=

View File

@ -347,6 +347,49 @@ func (rw *responseWriter) Write(p []byte) (int, error) {
}
}
// used to mask ReadFrom method
type writerOnly struct {
io.Writer
}
// copied from stdlib
const sniffLen = 512
// ReadFrom will try to use sendfile to copy from the reader to the response writer.
// It's only used if the response writer implements io.ReaderFrom and the data can't be compressed.
// It's based on stdlin http1.1 response writer implementation.
// https://github.com/golang/go/blob/f4e3ec3dbe3b8e04a058d266adf8e048bab563f2/src/net/http/server.go#L586
func (rw *responseWriter) ReadFrom(r io.Reader) (int64, error) {
rf, ok := rw.ResponseWriter.(io.ReaderFrom)
// sendfile can't be used anyway
if !ok {
// mask ReadFrom to avoid infinite recursion
return io.Copy(writerOnly{rw}, r)
}
var ns int64
// try to sniff the content type and determine if the response should be compressed
if !rw.wroteHeader && rw.config.MinLength > 0 {
var (
err error
buf [sniffLen]byte
)
// mask ReadFrom to let Write determine if the response should be compressed
ns, err = io.CopyBuffer(writerOnly{rw}, io.LimitReader(r, sniffLen), buf[:])
if err != nil || ns < sniffLen {
return ns, err
}
}
// the response will be compressed, no sendfile support
if rw.w != nil {
nr, err := io.Copy(rw.w, r)
return nr + ns, err
}
nr, err := rf.ReadFrom(r)
return nr + ns, err
}
// Close writes any remaining buffered response and
// deallocates any active resources.
func (rw *responseWriter) Close() error {

View File

@ -51,6 +51,9 @@ func (r LoggableHTTPRequest) MarshalLogObject(enc zapcore.ObjectEncoder) error {
Header: r.Header,
ShouldLogCredentials: r.ShouldLogCredentials,
})
if r.TransferEncoding != nil {
enc.AddArray("transfer_encoding", LoggableStringArray(r.TransferEncoding))
}
if r.TLS != nil {
enc.AddObject("tls", LoggableTLSConnState(*r.TLS))
}

View File

@ -978,7 +978,7 @@ func (m MatchHeader) Match(r *http.Request) bool {
// MatchWithError returns true if r matches m.
func (m MatchHeader) MatchWithError(r *http.Request) (bool, error) {
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
return matchHeaders(r.Header, http.Header(m), r.Host, repl), nil
return matchHeaders(r.Header, http.Header(m), r.Host, r.TransferEncoding, repl), nil
}
// CELLibrary produces options that expose this matcher for use in CEL
@ -1004,22 +1004,26 @@ func (MatchHeader) CELLibrary(_ caddy.Context) (cel.Library, error) {
}
// getHeaderFieldVals returns the field values for the given fieldName from input.
// The host parameter should be obtained from the http.Request.Host field since
// net/http removes it from the header map.
func getHeaderFieldVals(input http.Header, fieldName, host string) []string {
// The host parameter should be obtained from the http.Request.Host field, and the
// transferEncoding from http.Request.TransferEncoding, since net/http removes them
// from the header map.
func getHeaderFieldVals(input http.Header, fieldName, host string, transferEncoding []string) []string {
fieldName = textproto.CanonicalMIMEHeaderKey(fieldName)
if fieldName == "Host" && host != "" {
return []string{host}
}
if fieldName == "Transfer-Encoding" && input[fieldName] == nil {
return transferEncoding
}
return input[fieldName]
}
// matchHeaders returns true if input matches the criteria in against without regex.
// The host parameter should be obtained from the http.Request.Host field since
// net/http removes it from the header map.
func matchHeaders(input, against http.Header, host string, repl *caddy.Replacer) bool {
func matchHeaders(input, against http.Header, host string, transferEncoding []string, repl *caddy.Replacer) bool {
for field, allowedFieldVals := range against {
actualFieldVals := getHeaderFieldVals(input, field, host)
actualFieldVals := getHeaderFieldVals(input, field, host, transferEncoding)
if allowedFieldVals != nil && len(allowedFieldVals) == 0 && actualFieldVals != nil {
// a non-nil but empty list of allowed values means
// match if the header field exists at all
@ -1119,7 +1123,7 @@ func (m MatchHeaderRE) Match(r *http.Request) bool {
// MatchWithError returns true if r matches m.
func (m MatchHeaderRE) MatchWithError(r *http.Request) (bool, error) {
for field, rm := range m {
actualFieldVals := getHeaderFieldVals(r.Header, field, r.Host)
actualFieldVals := getHeaderFieldVals(r.Header, field, r.Host, r.TransferEncoding)
match := false
fieldVal:
for _, actualFieldVal := range actualFieldVals {

View File

@ -41,7 +41,7 @@ func (rm ResponseMatcher) Match(statusCode int, hdr http.Header) bool {
if !rm.matchStatusCode(statusCode) {
return false
}
return matchHeaders(hdr, rm.Headers, "", nil)
return matchHeaders(hdr, rm.Headers, "", []string{}, nil)
}
func (rm ResponseMatcher) matchStatusCode(statusCode int) bool {

View File

@ -28,7 +28,7 @@ import (
"github.com/caddyserver/certmagic"
"github.com/caddyserver/zerossl"
"github.com/mholt/acmez/v2/acme"
"github.com/mholt/acmez/v3/acme"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"

View File

@ -25,7 +25,7 @@ import (
"strings"
"github.com/caddyserver/certmagic"
"github.com/mholt/acmez/v2"
"github.com/mholt/acmez/v3"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"

View File

@ -26,7 +26,7 @@ import (
"os"
"strings"
"github.com/mholt/acmez/v2"
"github.com/mholt/acmez/v3"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
@ -350,6 +350,20 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy.Context) error {
if err := p.ClientAuthentication.ConfigureTLSConfig(cfg); err != nil {
return fmt.Errorf("configuring TLS client authentication: %v", err)
}
// Prevent privilege escalation in case multiple vhosts are configured for
// this TLS server; we could potentially figure out if that's the case, but
// that might be complex to get right every time. Actually, two proper
// solutions could leave tickets enabled, but I am not sure how to do them
// properly without significant time investment; there may be new Go
// APIs that alloaw this (Wrap/UnwrapSession?) but I do not know how to use
// them at this time. TODO: one of these is a possible future enhancement:
// A) Prevent resumptions across server identities (certificates): binding the ticket to the
// certificate we would serve in a full handshake, or even bind a ticket to the exact SNI
// it was issued under (though there are proposals for session resumption across hostnames).
// B) Prevent resumptions falsely authenticating a client: include the realm in the ticket,
// so that it can be validated upon resumption.
cfg.SessionTicketsDisabled = true
}
if p.InsecureSecretsLog != "" {