mirror of
https://github.com/caddyserver/caddy.git
synced 2025-02-24 08:49:01 +01:00
reverseproxy: allow specifying ip version for dynamic a
upstream (#5401)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
This commit is contained in:
parent
096971e313
commit
941eae5f61
3 changed files with 64 additions and 5 deletions
|
@ -11,6 +11,7 @@
|
||||||
resolvers 8.8.8.8 8.8.4.4
|
resolvers 8.8.8.8 8.8.4.4
|
||||||
dial_timeout 2s
|
dial_timeout 2s
|
||||||
dial_fallback_delay 300ms
|
dial_fallback_delay 300ms
|
||||||
|
versions ipv6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +67,10 @@
|
||||||
"8.8.4.4"
|
"8.8.4.4"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"source": "a"
|
"source": "a",
|
||||||
|
"versions": {
|
||||||
|
"ipv6": true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"handler": "reverse_proxy"
|
"handler": "reverse_proxy"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1324,6 +1324,7 @@ func (u *SRVUpstreams) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
// resolvers <resolvers...>
|
// resolvers <resolvers...>
|
||||||
// dial_timeout <timeout>
|
// dial_timeout <timeout>
|
||||||
// dial_fallback_delay <timeout>
|
// dial_fallback_delay <timeout>
|
||||||
|
// versions ipv4|ipv6
|
||||||
// }
|
// }
|
||||||
func (u *AUpstreams) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
func (u *AUpstreams) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
for d.Next() {
|
for d.Next() {
|
||||||
|
@ -1397,8 +1398,30 @@ func (u *AUpstreams) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
}
|
}
|
||||||
u.FallbackDelay = caddy.Duration(dur)
|
u.FallbackDelay = caddy.Duration(dur)
|
||||||
|
|
||||||
|
case "versions":
|
||||||
|
args := d.RemainingArgs()
|
||||||
|
if len(args) == 0 {
|
||||||
|
return d.Errf("must specify at least one version")
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Versions == nil {
|
||||||
|
u.Versions = &ipVersions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
trueBool := true
|
||||||
|
for _, arg := range args {
|
||||||
|
switch arg {
|
||||||
|
case "ipv4":
|
||||||
|
u.Versions.IPv4 = &trueBool
|
||||||
|
case "ipv6":
|
||||||
|
u.Versions.IPv6 = &trueBool
|
||||||
|
default:
|
||||||
|
return d.Errf("unsupported version: '%s'", arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return d.Errf("unrecognized srv option '%s'", d.Val())
|
return d.Errf("unrecognized a option '%s'", d.Val())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,6 +213,11 @@ func (sl srvLookup) isFresh() bool {
|
||||||
return time.Since(sl.freshness) < time.Duration(sl.srvUpstreams.Refresh)
|
return time.Since(sl.freshness) < time.Duration(sl.srvUpstreams.Refresh)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ipVersions struct {
|
||||||
|
IPv4 *bool `json:"ipv4,omitempty"`
|
||||||
|
IPv6 *bool `json:"ipv6,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// AUpstreams provides upstreams from A/AAAA lookups.
|
// AUpstreams provides upstreams from A/AAAA lookups.
|
||||||
// Results are cached and refreshed at the configured
|
// Results are cached and refreshed at the configured
|
||||||
// refresh interval.
|
// refresh interval.
|
||||||
|
@ -240,6 +245,11 @@ type AUpstreams struct {
|
||||||
// A negative value disables this.
|
// A negative value disables this.
|
||||||
FallbackDelay caddy.Duration `json:"dial_fallback_delay,omitempty"`
|
FallbackDelay caddy.Duration `json:"dial_fallback_delay,omitempty"`
|
||||||
|
|
||||||
|
// The IP versions to resolve for. By default, both
|
||||||
|
// "ipv4" and "ipv6" will be enabled, which
|
||||||
|
// correspond to A and AAAA records respectively.
|
||||||
|
Versions *ipVersions `json:"versions,omitempty"`
|
||||||
|
|
||||||
resolver *net.Resolver
|
resolver *net.Resolver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,7 +296,29 @@ func (au *AUpstreams) Provision(_ caddy.Context) error {
|
||||||
|
|
||||||
func (au AUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
|
func (au AUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
|
||||||
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||||
auStr := repl.ReplaceAll(au.String(), "")
|
|
||||||
|
resolveIpv4 := au.Versions.IPv4 == nil || *au.Versions.IPv4
|
||||||
|
resolveIpv6 := au.Versions.IPv6 == nil || *au.Versions.IPv6
|
||||||
|
|
||||||
|
// Map ipVersion early, so we can use it as part of the cache-key.
|
||||||
|
// This should be fairly inexpensive and comes and the upside of
|
||||||
|
// allowing the same dynamic upstream (name + port combination)
|
||||||
|
// to be used multiple times with different ip versions.
|
||||||
|
//
|
||||||
|
// It also forced a cache-miss if a previously cached dynamic
|
||||||
|
// upstream changes its ip version, e.g. after a config reload,
|
||||||
|
// while keeping the cache-invalidation as simple as it currently is.
|
||||||
|
var ipVersion string
|
||||||
|
switch {
|
||||||
|
case resolveIpv4 && !resolveIpv6:
|
||||||
|
ipVersion = "ip4"
|
||||||
|
case !resolveIpv4 && resolveIpv6:
|
||||||
|
ipVersion = "ip6"
|
||||||
|
default:
|
||||||
|
ipVersion = "ip"
|
||||||
|
}
|
||||||
|
|
||||||
|
auStr := repl.ReplaceAll(au.String()+ipVersion, "")
|
||||||
|
|
||||||
// first, use a cheap read-lock to return a cached result quickly
|
// first, use a cheap read-lock to return a cached result quickly
|
||||||
aAaaaMu.RLock()
|
aAaaaMu.RLock()
|
||||||
|
@ -311,7 +343,7 @@ func (au AUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
|
||||||
name := repl.ReplaceAll(au.Name, "")
|
name := repl.ReplaceAll(au.Name, "")
|
||||||
port := repl.ReplaceAll(au.Port, "")
|
port := repl.ReplaceAll(au.Port, "")
|
||||||
|
|
||||||
ips, err := au.resolver.LookupIPAddr(r.Context(), name)
|
ips, err := au.resolver.LookupIP(r.Context(), ipVersion, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue