mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-27 14:13:48 +03:00
httpcaddyfile: fix placeholder shorthands in named routes (#5791)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
This commit is contained in:
parent
f2ab7099db
commit
2cac3c5491
3 changed files with 109 additions and 71 deletions
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
|
@ -56,3 +56,5 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: govulncheck
|
- name: govulncheck
|
||||||
uses: golang/govulncheck-action@v1
|
uses: golang/govulncheck-action@v1
|
||||||
|
with:
|
||||||
|
check-latest: true
|
||||||
|
|
|
@ -18,7 +18,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -82,46 +81,18 @@ func (st ServerType) Setup(
|
||||||
return nil, warnings, err
|
return nil, warnings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
originalServerBlocks, err = st.extractNamedRoutes(originalServerBlocks, options, &warnings)
|
// this will replace both static and user-defined placeholder shorthands
|
||||||
|
// with actual identifiers used by Caddy
|
||||||
|
replacer := NewShorthandReplacer()
|
||||||
|
|
||||||
|
originalServerBlocks, err = st.extractNamedRoutes(originalServerBlocks, options, &warnings, replacer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, warnings, err
|
return nil, warnings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace shorthand placeholders (which are convenient
|
|
||||||
// when writing a Caddyfile) with their actual placeholder
|
|
||||||
// identifiers or variable names
|
|
||||||
replacer := strings.NewReplacer(placeholderShorthands()...)
|
|
||||||
|
|
||||||
// these are placeholders that allow a user-defined final
|
|
||||||
// parameters, but we still want to provide a shorthand
|
|
||||||
// for those, so we use a regexp to replace
|
|
||||||
regexpReplacements := []struct {
|
|
||||||
search *regexp.Regexp
|
|
||||||
replace string
|
|
||||||
}{
|
|
||||||
{regexp.MustCompile(`{header\.([\w-]*)}`), "{http.request.header.$1}"},
|
|
||||||
{regexp.MustCompile(`{cookie\.([\w-]*)}`), "{http.request.cookie.$1}"},
|
|
||||||
{regexp.MustCompile(`{labels\.([\w-]*)}`), "{http.request.host.labels.$1}"},
|
|
||||||
{regexp.MustCompile(`{path\.([\w-]*)}`), "{http.request.uri.path.$1}"},
|
|
||||||
{regexp.MustCompile(`{file\.([\w-]*)}`), "{http.request.uri.path.file.$1}"},
|
|
||||||
{regexp.MustCompile(`{query\.([\w-]*)}`), "{http.request.uri.query.$1}"},
|
|
||||||
{regexp.MustCompile(`{re\.([\w-]*)\.([\w-]*)}`), "{http.regexp.$1.$2}"},
|
|
||||||
{regexp.MustCompile(`{vars\.([\w-]*)}`), "{http.vars.$1}"},
|
|
||||||
{regexp.MustCompile(`{rp\.([\w-\.]*)}`), "{http.reverse_proxy.$1}"},
|
|
||||||
{regexp.MustCompile(`{err\.([\w-\.]*)}`), "{http.error.$1}"},
|
|
||||||
{regexp.MustCompile(`{file_match\.([\w-]*)}`), "{http.matchers.file.$1}"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, sb := range originalServerBlocks {
|
for _, sb := range originalServerBlocks {
|
||||||
for _, segment := range sb.block.Segments {
|
for i := range sb.block.Segments {
|
||||||
for i := 0; i < len(segment); i++ {
|
replacer.ApplyToSegment(&sb.block.Segments[i])
|
||||||
// simple string replacements
|
|
||||||
segment[i].Text = replacer.Replace(segment[i].Text)
|
|
||||||
// complex regexp replacements
|
|
||||||
for _, r := range regexpReplacements {
|
|
||||||
segment[i].Text = r.search.ReplaceAllString(segment[i].Text, r.replace)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(sb.block.Keys) == 0 {
|
if len(sb.block.Keys) == 0 {
|
||||||
|
@ -452,6 +423,7 @@ func (ServerType) extractNamedRoutes(
|
||||||
serverBlocks []serverBlock,
|
serverBlocks []serverBlock,
|
||||||
options map[string]any,
|
options map[string]any,
|
||||||
warnings *[]caddyconfig.Warning,
|
warnings *[]caddyconfig.Warning,
|
||||||
|
replacer ShorthandReplacer,
|
||||||
) ([]serverBlock, error) {
|
) ([]serverBlock, error) {
|
||||||
namedRoutes := map[string]*caddyhttp.Route{}
|
namedRoutes := map[string]*caddyhttp.Route{}
|
||||||
|
|
||||||
|
@ -477,11 +449,14 @@ func (ServerType) extractNamedRoutes(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wholeSegment := caddyfile.Segment{}
|
||||||
|
for i := range sb.block.Segments {
|
||||||
|
// replace user-defined placeholder shorthands in extracted named routes
|
||||||
|
replacer.ApplyToSegment(&sb.block.Segments[i])
|
||||||
|
|
||||||
// zip up all the segments since ParseSegmentAsSubroute
|
// zip up all the segments since ParseSegmentAsSubroute
|
||||||
// was designed to take a directive+
|
// was designed to take a directive+
|
||||||
wholeSegment := caddyfile.Segment{}
|
wholeSegment = append(wholeSegment, sb.block.Segments[i]...)
|
||||||
for _, segment := range sb.block.Segments {
|
|
||||||
wholeSegment = append(wholeSegment, segment...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h := Helper{
|
h := Helper{
|
||||||
|
@ -1449,37 +1424,6 @@ func encodeMatcherSet(matchers map[string]caddyhttp.RequestMatcher) (caddy.Modul
|
||||||
return msEncoded, nil
|
return msEncoded, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// placeholderShorthands returns a slice of old-new string pairs,
|
|
||||||
// where the left of the pair is a placeholder shorthand that may
|
|
||||||
// be used in the Caddyfile, and the right is the replacement.
|
|
||||||
func placeholderShorthands() []string {
|
|
||||||
return []string{
|
|
||||||
"{dir}", "{http.request.uri.path.dir}",
|
|
||||||
"{file}", "{http.request.uri.path.file}",
|
|
||||||
"{host}", "{http.request.host}",
|
|
||||||
"{hostport}", "{http.request.hostport}",
|
|
||||||
"{port}", "{http.request.port}",
|
|
||||||
"{method}", "{http.request.method}",
|
|
||||||
"{path}", "{http.request.uri.path}",
|
|
||||||
"{query}", "{http.request.uri.query}",
|
|
||||||
"{remote}", "{http.request.remote}",
|
|
||||||
"{remote_host}", "{http.request.remote.host}",
|
|
||||||
"{remote_port}", "{http.request.remote.port}",
|
|
||||||
"{scheme}", "{http.request.scheme}",
|
|
||||||
"{uri}", "{http.request.uri}",
|
|
||||||
"{tls_cipher}", "{http.request.tls.cipher_suite}",
|
|
||||||
"{tls_version}", "{http.request.tls.version}",
|
|
||||||
"{tls_client_fingerprint}", "{http.request.tls.client.fingerprint}",
|
|
||||||
"{tls_client_issuer}", "{http.request.tls.client.issuer}",
|
|
||||||
"{tls_client_serial}", "{http.request.tls.client.serial}",
|
|
||||||
"{tls_client_subject}", "{http.request.tls.client.subject}",
|
|
||||||
"{tls_client_certificate_pem}", "{http.request.tls.client.certificate_pem}",
|
|
||||||
"{tls_client_certificate_der_base64}", "{http.request.tls.client.certificate_der_base64}",
|
|
||||||
"{upstream_hostport}", "{http.reverse_proxy.upstream.hostport}",
|
|
||||||
"{client_ip}", "{http.vars.client_ip}",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WasReplacedPlaceholderShorthand checks if a token string was
|
// WasReplacedPlaceholderShorthand checks if a token string was
|
||||||
// likely a replaced shorthand of the known Caddyfile placeholder
|
// likely a replaced shorthand of the known Caddyfile placeholder
|
||||||
// replacement outputs. Useful to prevent some user-defined map
|
// replacement outputs. Useful to prevent some user-defined map
|
||||||
|
|
92
caddyconfig/httpcaddyfile/shorthands.go
Normal file
92
caddyconfig/httpcaddyfile/shorthands.go
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
package httpcaddyfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ComplexShorthandReplacer struct {
|
||||||
|
search *regexp.Regexp
|
||||||
|
replace string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShorthandReplacer struct {
|
||||||
|
complex []ComplexShorthandReplacer
|
||||||
|
simple *strings.Replacer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewShorthandReplacer() ShorthandReplacer {
|
||||||
|
// replace shorthand placeholders (which are convenient
|
||||||
|
// when writing a Caddyfile) with their actual placeholder
|
||||||
|
// identifiers or variable names
|
||||||
|
replacer := strings.NewReplacer(placeholderShorthands()...)
|
||||||
|
|
||||||
|
// these are placeholders that allow a user-defined final
|
||||||
|
// parameters, but we still want to provide a shorthand
|
||||||
|
// for those, so we use a regexp to replace
|
||||||
|
regexpReplacements := []ComplexShorthandReplacer{
|
||||||
|
{regexp.MustCompile(`{header\.([\w-]*)}`), "{http.request.header.$1}"},
|
||||||
|
{regexp.MustCompile(`{cookie\.([\w-]*)}`), "{http.request.cookie.$1}"},
|
||||||
|
{regexp.MustCompile(`{labels\.([\w-]*)}`), "{http.request.host.labels.$1}"},
|
||||||
|
{regexp.MustCompile(`{path\.([\w-]*)}`), "{http.request.uri.path.$1}"},
|
||||||
|
{regexp.MustCompile(`{file\.([\w-]*)}`), "{http.request.uri.path.file.$1}"},
|
||||||
|
{regexp.MustCompile(`{query\.([\w-]*)}`), "{http.request.uri.query.$1}"},
|
||||||
|
{regexp.MustCompile(`{re\.([\w-]*)\.([\w-]*)}`), "{http.regexp.$1.$2}"},
|
||||||
|
{regexp.MustCompile(`{vars\.([\w-]*)}`), "{http.vars.$1}"},
|
||||||
|
{regexp.MustCompile(`{rp\.([\w-\.]*)}`), "{http.reverse_proxy.$1}"},
|
||||||
|
{regexp.MustCompile(`{err\.([\w-\.]*)}`), "{http.error.$1}"},
|
||||||
|
{regexp.MustCompile(`{file_match\.([\w-]*)}`), "{http.matchers.file.$1}"},
|
||||||
|
}
|
||||||
|
|
||||||
|
return ShorthandReplacer{
|
||||||
|
complex: regexpReplacements,
|
||||||
|
simple: replacer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// placeholderShorthands returns a slice of old-new string pairs,
|
||||||
|
// where the left of the pair is a placeholder shorthand that may
|
||||||
|
// be used in the Caddyfile, and the right is the replacement.
|
||||||
|
func placeholderShorthands() []string {
|
||||||
|
return []string{
|
||||||
|
"{dir}", "{http.request.uri.path.dir}",
|
||||||
|
"{file}", "{http.request.uri.path.file}",
|
||||||
|
"{host}", "{http.request.host}",
|
||||||
|
"{hostport}", "{http.request.hostport}",
|
||||||
|
"{port}", "{http.request.port}",
|
||||||
|
"{method}", "{http.request.method}",
|
||||||
|
"{path}", "{http.request.uri.path}",
|
||||||
|
"{query}", "{http.request.uri.query}",
|
||||||
|
"{remote}", "{http.request.remote}",
|
||||||
|
"{remote_host}", "{http.request.remote.host}",
|
||||||
|
"{remote_port}", "{http.request.remote.port}",
|
||||||
|
"{scheme}", "{http.request.scheme}",
|
||||||
|
"{uri}", "{http.request.uri}",
|
||||||
|
"{tls_cipher}", "{http.request.tls.cipher_suite}",
|
||||||
|
"{tls_version}", "{http.request.tls.version}",
|
||||||
|
"{tls_client_fingerprint}", "{http.request.tls.client.fingerprint}",
|
||||||
|
"{tls_client_issuer}", "{http.request.tls.client.issuer}",
|
||||||
|
"{tls_client_serial}", "{http.request.tls.client.serial}",
|
||||||
|
"{tls_client_subject}", "{http.request.tls.client.subject}",
|
||||||
|
"{tls_client_certificate_pem}", "{http.request.tls.client.certificate_pem}",
|
||||||
|
"{tls_client_certificate_der_base64}", "{http.request.tls.client.certificate_der_base64}",
|
||||||
|
"{upstream_hostport}", "{http.reverse_proxy.upstream.hostport}",
|
||||||
|
"{client_ip}", "{http.vars.client_ip}",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyToSegment replaces shorthand placeholder to its full placeholder, understandable by Caddy.
|
||||||
|
func (s ShorthandReplacer) ApplyToSegment(segment *caddyfile.Segment) {
|
||||||
|
if segment != nil {
|
||||||
|
for i := 0; i < len(*segment); i++ {
|
||||||
|
// simple string replacements
|
||||||
|
(*segment)[i].Text = s.simple.Replace((*segment)[i].Text)
|
||||||
|
// complex regexp replacements
|
||||||
|
for _, r := range s.complex {
|
||||||
|
(*segment)[i].Text = r.search.ReplaceAllString((*segment)[i].Text, r.replace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue