1
0
Fork 0

Merge v2.10 into v3.0

This commit is contained in:
mmatur 2023-10-11 16:20:26 +02:00
commit 286181aa61
No known key found for this signature in database
GPG key ID: 2FFE42FC256CFF8E
62 changed files with 712 additions and 189 deletions

View file

@ -228,15 +228,6 @@ func (c *Configuration) PopulateUsedBy() {
}
}
func contains(entryPoints []string, entryPointName string) bool {
for _, name := range entryPoints {
if name == entryPointName {
return true
}
}
return false
}
func getProviderName(elementName string) string {
parts := strings.Split(elementName, "@")
if len(parts) > 1 {

View file

@ -3,6 +3,7 @@ package runtime
import (
"context"
"fmt"
"slices"
"sort"
"sync"
@ -24,7 +25,7 @@ func (c *Configuration) GetRoutersByEntryPoints(ctx context.Context, entryPoints
entryPointsCount := 0
for _, entryPointName := range rt.EntryPoints {
if !contains(entryPoints, entryPointName) {
if !slices.Contains(entryPoints, entryPointName) {
rt.AddError(fmt.Errorf("entryPoint %q doesn't exist", entryPointName), false)
logger.Error().Str(logs.EntryPointName, entryPointName).
Msg("EntryPoint doesn't exist")

View file

@ -3,6 +3,7 @@ package runtime
import (
"context"
"fmt"
"slices"
"github.com/rs/zerolog/log"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
@ -18,7 +19,7 @@ func (c *Configuration) GetTCPRoutersByEntryPoints(ctx context.Context, entryPoi
entryPointsCount := 0
for _, entryPointName := range rt.EntryPoints {
if !contains(entryPoints, entryPointName) {
if !slices.Contains(entryPoints, entryPointName) {
rt.AddError(fmt.Errorf("entryPoint %q doesn't exist", entryPointName), false)
logger.Error().Str(logs.EntryPointName, entryPointName).
Msg("EntryPoint doesn't exist")

View file

@ -3,6 +3,7 @@ package runtime
import (
"context"
"fmt"
"slices"
"github.com/rs/zerolog/log"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
@ -24,7 +25,7 @@ func (c *Configuration) GetUDPRoutersByEntryPoints(ctx context.Context, entryPoi
entryPointsCount := 0
for _, entryPointName := range eps {
if !contains(entryPoints, entryPointName) {
if !slices.Contains(entryPoints, entryPointName) {
rt.AddError(fmt.Errorf("entryPoint %q doesn't exist", entryPointName), false)
logger.Error().Str(logs.EntryPointName, entryPointName).
Msg("EntryPoint doesn't exist")

View file

@ -42,8 +42,8 @@ func (f *FieldHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
}
// AddOriginFields add origin fields.
func AddOriginFields(rw http.ResponseWriter, req *http.Request, next http.Handler, data *LogData) {
// AddServiceFields add service fields.
func AddServiceFields(rw http.ResponseWriter, req *http.Request, next http.Handler, data *LogData) {
start := time.Now().UTC()
next.ServeHTTP(rw, req)
@ -65,3 +65,14 @@ func AddOriginFields(rw http.ResponseWriter, req *http.Request, next http.Handle
data.Core[OriginStatus] = capt.StatusCode()
data.Core[OriginContentSize] = capt.ResponseSize()
}
// InitServiceFields init service fields.
func InitServiceFields(rw http.ResponseWriter, req *http.Request, next http.Handler, data *LogData) {
// Because they are expected to be initialized when the logger is processing the data table,
// the origin fields are initialized in case the response is returned by Traefik itself, and not a service.
data.Core[OriginDuration] = time.Duration(0)
data.Core[OriginStatus] = 0
data.Core[OriginContentSize] = int64(0)
next.ServeHTTP(rw, req)
}

View file

@ -40,8 +40,8 @@ func (f *CommonLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
toLog(entry.Data, RequestMethod, defaultValue, false),
toLog(entry.Data, RequestPath, defaultValue, false),
toLog(entry.Data, RequestProtocol, defaultValue, false),
toLog(entry.Data, OriginStatus, defaultValue, true),
toLog(entry.Data, OriginContentSize, defaultValue, true),
toLog(entry.Data, DownstreamStatus, defaultValue, true),
toLog(entry.Data, DownstreamContentSize, defaultValue, true),
toLog(entry.Data, "request_Referer", `"-"`, true),
toLog(entry.Data, "request_User-Agent", `"-"`, true),
toLog(entry.Data, RequestCount, defaultValue, true),

View file

@ -18,7 +18,7 @@ func TestCommonLogFormatter_Format(t *testing.T) {
expectedLog string
}{
{
name: "OriginStatus & OriginContentSize are nil",
name: "DownstreamStatus & DownstreamContentSize are nil",
data: map[string]interface{}{
StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
Duration: 123 * time.Second,
@ -27,8 +27,8 @@ func TestCommonLogFormatter_Format(t *testing.T) {
RequestMethod: http.MethodGet,
RequestPath: "/foo",
RequestProtocol: "http",
OriginStatus: nil,
OriginContentSize: nil,
DownstreamStatus: nil,
DownstreamContentSize: nil,
RequestRefererHeader: "",
RequestUserAgentHeader: "",
RequestCount: 0,
@ -48,8 +48,8 @@ func TestCommonLogFormatter_Format(t *testing.T) {
RequestMethod: http.MethodGet,
RequestPath: "/foo",
RequestProtocol: "http",
OriginStatus: 123,
OriginContentSize: 132,
DownstreamStatus: 123,
DownstreamContentSize: 132,
RequestRefererHeader: "referer",
RequestUserAgentHeader: "agent",
RequestCount: nil,
@ -69,8 +69,8 @@ func TestCommonLogFormatter_Format(t *testing.T) {
RequestMethod: http.MethodGet,
RequestPath: "/foo",
RequestProtocol: "http",
OriginStatus: 123,
OriginContentSize: 132,
DownstreamStatus: 123,
DownstreamContentSize: 132,
RequestRefererHeader: "referer",
RequestUserAgentHeader: "agent",
RequestCount: nil,

View file

@ -125,6 +125,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(http.StatusInternalServerError)
return
}
defer forwardResponse.Body.Close()
body, readError := io.ReadAll(forwardResponse.Body)
if readError != nil {
@ -135,7 +136,6 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(http.StatusInternalServerError)
return
}
defer forwardResponse.Body.Close()
// Pass the forward response's body and selected headers if it
// didn't return a response within the range of [200, 300).

View file

@ -5,6 +5,7 @@ import (
"fmt"
"mime"
"net/http"
"slices"
"strings"
"github.com/klauspost/compress/gzhttp"
@ -87,7 +88,7 @@ func (c *compress) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
// Notably for text/event-stream requests the response should not be compressed.
// See https://github.com/traefik/traefik/issues/2576
if contains(c.excludes, mediaType) {
if slices.Contains(c.excludes, mediaType) {
c.next.ServeHTTP(rw, req)
return
}
@ -158,13 +159,3 @@ func encodingAccepts(acceptEncoding []string, typ string) bool {
return false
}
func contains(values []string, val string) bool {
for _, v := range values {
if v == val {
return true
}
}
return false
}

View file

@ -8,6 +8,7 @@ import (
"strings"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/vulcand/oxy/v2/forward"
)
// Header is a middleware that helps setup a few basic security features.
@ -47,6 +48,7 @@ func NewHeader(next http.Handler, cfg dynamic.Headers) (*Header, error) {
func (s *Header) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
// Handle Cors headers and preflight if configured.
if isPreflight := s.processCorsHeaders(rw, req); isPreflight {
rw.WriteHeader(http.StatusOK)
return
}
@ -65,6 +67,10 @@ func (s *Header) modifyCustomRequestHeaders(req *http.Request) {
// Loop through Custom request headers
for header, value := range s.headers.CustomRequestHeaders {
switch {
// Handling https://github.com/golang/go/commit/ecdbffd4ec68b509998792f120868fec319de59b.
case value == "" && header == forward.XForwardedFor:
req.Header[header] = nil
case value == "":
req.Header.Del(header)

View file

@ -29,11 +29,14 @@ func TestNewHeader_customRequestHeader(t *testing.T) {
desc: "delete a header",
cfg: dynamic.Headers{
CustomRequestHeaders: map[string]string{
"X-Forwarded-For": "",
"X-Custom-Request-Header": "",
"Foo": "",
},
},
expected: http.Header{},
expected: http.Header{
"X-Forwarded-For": nil,
},
},
{
desc: "override a header",

View file

@ -2,6 +2,7 @@ package requestdecorator
import (
"context"
"errors"
"fmt"
"net"
"sort"
@ -65,9 +66,7 @@ func (hr *Resolver) CNAMEFlatten(ctx context.Context, host string) string {
request = resolv.Record
}
if err := hr.cache.Add(host, result, cacheDuration); err != nil {
logger.Error().Err(err).Send()
}
hr.cache.Set(host, result, cacheDuration)
return result
}
@ -79,6 +78,10 @@ func cnameResolve(ctx context.Context, host, resolvPath string) (*cnameResolv, e
return nil, fmt.Errorf("invalid resolver configuration file: %s", resolvPath)
}
if net.ParseIP(host) != nil {
return nil, nil
}
client := &dns.Client{Timeout: 30 * time.Second}
m := &dns.Msg{}
@ -88,7 +91,11 @@ func cnameResolve(ctx context.Context, host, resolvPath string) (*cnameResolv, e
for _, server := range config.Servers {
tempRecord, err := getRecord(client, m, server, config.Port)
if err != nil {
log.Ctx(ctx).Error().Err(err).Msgf("Failed to resolve host %s", host)
if errors.Is(err, errNoCNAMERecord) {
log.Ctx(ctx).Debug().Err(err).Msgf("CNAME lookup for hostname %q", host)
continue
}
log.Ctx(ctx).Error().Err(err).Msgf("CNAME lookup for hostname %q", host)
continue
}
result = append(result, tempRecord)
@ -102,6 +109,8 @@ func cnameResolve(ctx context.Context, host, resolvPath string) (*cnameResolv, e
return result[0], nil
}
var errNoCNAMERecord = errors.New("no CNAME record for host")
func getRecord(client *dns.Client, msg *dns.Msg, server, port string) (*cnameResolv, error) {
resp, _, err := client.Exchange(msg, net.JoinHostPort(server, port))
if err != nil {
@ -109,7 +118,7 @@ func getRecord(client *dns.Client, msg *dns.Msg, server, port string) (*cnameRes
}
if resp == nil || len(resp.Answer) == 0 {
return nil, fmt.Errorf("empty answer for server %s", server)
return nil, fmt.Errorf("%w: %s", errNoCNAMERecord, server)
}
rr, ok := resp.Answer[0].(*dns.CNAME)

View file

@ -86,7 +86,7 @@ type DNSChallenge struct {
// HTTPChallenge contains HTTP challenge configuration.
type HTTPChallenge struct {
EntryPoint string `description:"HTTP challenge EntryPoint" json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty" export:"true"`
EntryPoint string `description:"HTTP challenge EntryPoint" json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty" export:"true"`
}
// TLSChallenge contains TLS challenge configuration.

View file

@ -134,6 +134,16 @@ func (p *Provider) watchKv(ctx context.Context, configurationChan chan<- dynamic
func (p *Provider) buildConfiguration(ctx context.Context) (*dynamic.Configuration, error) {
pairs, err := p.kvClient.List(ctx, p.RootKey, nil)
if err != nil {
if errors.Is(err, store.ErrKeyNotFound) {
// This empty configuration satisfies the pkg/server/configurationwatcher.go isEmptyConfiguration func constraints,
// and will not be discarded by the configuration watcher.
return &dynamic.Configuration{
HTTP: &dynamic.HTTPConfiguration{
Routers: make(map[string]*dynamic.Router),
},
}, nil
}
return nil, err
}

View file

@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"reflect"
"regexp"
"github.com/mitchellh/copystructure"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
@ -67,8 +66,7 @@ func do(baseConfig interface{}, tag string, redactByDefault, indent bool) (strin
}
func doOnJSON(input string) string {
mailExp := regexp.MustCompile(`\w[-.\w]*\w@\w[-.\w]*\w\.\w{2,3}"`)
return xurls.Relaxed().ReplaceAllString(mailExp.ReplaceAllString(input, maskLarge+"\""), maskLarge)
return xurls.Relaxed().ReplaceAllString(input, maskLarge)
}
func doOnStruct(field reflect.Value, tag string, redactByDefault bool) error {

View file

@ -45,13 +45,15 @@ func Test_doOnJSON_simple(t *testing.T) {
"URL": "foo domain.com foo",
"URL": "foo sub.domain.com foo",
"URL": "foo sub.sub.domain.com foo",
"URL": "foo sub.sub.sub.domain.com.us foo"
"URL": "foo sub.sub.sub.domain.com.us foo",
"URL":"https://hub.example.com","foo":"bar"
}`,
expectedOutput: `{
"URL": "foo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx foo",
"URL": "foo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx foo",
"URL": "foo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx foo",
"URL": "foo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx foo"
"URL": "foo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx foo",
"URL":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","foo":"bar"
}`,
},
}

View file

@ -79,7 +79,7 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, t
}
handlerWithAccessLog, err := alice.New(func(next http.Handler) (http.Handler, error) {
return accesslog.NewFieldHandler(next, logs.EntryPointName, entryPointName, accesslog.AddOriginFields), nil
return accesslog.NewFieldHandler(next, logs.EntryPointName, entryPointName, accesslog.InitServiceFields), nil
}).Then(handler)
if err != nil {
logger.Error().Err(err).Send()

View file

@ -73,7 +73,6 @@ func (s *Server) Wait() {
// Stop stops the server.
func (s *Server) Stop() {
//nolint:zerologlint // false-positive https://github.com/ykadowak/zerologlint/issues/3
defer log.Info().Msg("Server stopped")
s.tcpEntryPoints.Stop()

View file

@ -301,7 +301,7 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName
proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceURL, target.String(), nil)
proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceAddr, target.Host, nil)
proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceName, serviceName, nil)
proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceName, serviceName, accesslog.AddServiceFields)
if m.metricsRegistry != nil && m.metricsRegistry.IsSvcEnabled() {
proxy = metricsMiddle.NewServiceMiddleware(ctx, proxy, m.metricsRegistry, serviceName)