Send anonymized dynamic configuration to Pilot
Co-authored-by: Kevin Pollet <pollet.kevin@gmail.com>
This commit is contained in:
parent
a488430f23
commit
64a65cadf3
14 changed files with 1394 additions and 374 deletions
|
@ -7,6 +7,8 @@ import (
|
|||
"regexp"
|
||||
|
||||
"github.com/mitchellh/copystructure"
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/tls"
|
||||
"mvdan.cc/xurls/v2"
|
||||
)
|
||||
|
||||
|
@ -43,6 +45,11 @@ func doOnJSON(input string) string {
|
|||
}
|
||||
|
||||
func doOnStruct(field reflect.Value) error {
|
||||
if field.Type().AssignableTo(reflect.TypeOf(dynamic.PluginConf{})) {
|
||||
resetPlugin(field)
|
||||
return nil
|
||||
}
|
||||
|
||||
switch field.Kind() {
|
||||
case reflect.Ptr:
|
||||
if !field.IsNil() {
|
||||
|
@ -57,19 +64,48 @@ func doOnStruct(field reflect.Value) error {
|
|||
if !isExported(stField) {
|
||||
continue
|
||||
}
|
||||
|
||||
if stField.Tag.Get("export") == "true" {
|
||||
// A struct field cannot be set it must be filled as pointer.
|
||||
if fld.Kind() == reflect.Struct {
|
||||
fldPtr := reflect.New(fld.Type())
|
||||
fldPtr.Elem().Set(fld)
|
||||
|
||||
if err := doOnStruct(fldPtr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fld.Set(fldPtr.Elem())
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err := doOnStruct(fld); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := reset(fld, stField.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err := reset(fld, stField.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
for _, key := range field.MapKeys() {
|
||||
if err := doOnStruct(field.MapIndex(key)); err != nil {
|
||||
val := field.MapIndex(key)
|
||||
|
||||
// A struct value cannot be set it must be filled as pointer.
|
||||
if val.Kind() == reflect.Struct {
|
||||
valPtr := reflect.New(val.Type())
|
||||
valPtr.Elem().Set(val)
|
||||
|
||||
if err := doOnStruct(valPtr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
field.SetMapIndex(key, valPtr.Elem())
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err := doOnStruct(val); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +136,11 @@ func reset(field reflect.Value, name string) error {
|
|||
}
|
||||
case reflect.String:
|
||||
if field.String() != "" {
|
||||
field.Set(reflect.ValueOf(maskShort))
|
||||
if field.Type().AssignableTo(reflect.TypeOf(tls.FileOrContent(""))) {
|
||||
field.Set(reflect.ValueOf(tls.FileOrContent(maskShort)))
|
||||
} else {
|
||||
field.Set(reflect.ValueOf(maskShort))
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
if field.Len() > 0 {
|
||||
|
@ -130,6 +170,13 @@ func reset(field reflect.Value, name string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// resetPlugin resets the plugin configuration so it keep the plugin name but not its configuration.
|
||||
func resetPlugin(field reflect.Value) {
|
||||
for _, key := range field.MapKeys() {
|
||||
field.SetMapIndex(key, reflect.ValueOf(struct{}{}))
|
||||
}
|
||||
}
|
||||
|
||||
// isExported return true is a struct field is exported, else false.
|
||||
func isExported(f reflect.StructField) bool {
|
||||
if f.PkgPath != "" && !f.Anonymous {
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
ptypes "github.com/traefik/paerser/types"
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||
"github.com/traefik/traefik/v2/pkg/ping"
|
||||
"github.com/traefik/traefik/v2/pkg/plugins"
|
||||
|
@ -43,7 +44,438 @@ import (
|
|||
|
||||
var updateExpected = flag.Bool("update_expected", false, "Update expected files in fixtures")
|
||||
|
||||
func TestDo_globalConfiguration(t *testing.T) {
|
||||
func TestDo_dynamicConfiguration(t *testing.T) {
|
||||
config := &dynamic.Configuration{}
|
||||
config.HTTP = &dynamic.HTTPConfiguration{
|
||||
Routers: map[string]*dynamic.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"foo"},
|
||||
Middlewares: []string{"foo"},
|
||||
Service: "foo",
|
||||
Rule: "foo",
|
||||
Priority: 42,
|
||||
TLS: &dynamic.RouterTLSConfig{
|
||||
Options: "foo",
|
||||
CertResolver: "foo",
|
||||
Domains: []types.Domain{
|
||||
{
|
||||
Main: "foo",
|
||||
SANs: []string{"foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Services: map[string]*dynamic.Service{
|
||||
"foo": {
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
Sticky: &dynamic.Sticky{
|
||||
Cookie: &dynamic.Cookie{
|
||||
Name: "foo",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
SameSite: "foo",
|
||||
},
|
||||
},
|
||||
HealthCheck: &dynamic.HealthCheck{
|
||||
Scheme: "foo",
|
||||
Path: "foo",
|
||||
Port: 42,
|
||||
Interval: "foo",
|
||||
Timeout: "foo",
|
||||
Hostname: "foo",
|
||||
FollowRedirects: boolPtr(true),
|
||||
Headers: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
PassHostHeader: boolPtr(true),
|
||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||
FlushInterval: "foo",
|
||||
},
|
||||
ServersTransport: "foo",
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"bar": {
|
||||
Weighted: &dynamic.WeightedRoundRobin{
|
||||
Services: []dynamic.WRRService{
|
||||
{
|
||||
Name: "foo",
|
||||
Weight: intPtr(42),
|
||||
},
|
||||
},
|
||||
Sticky: &dynamic.Sticky{
|
||||
Cookie: &dynamic.Cookie{
|
||||
Name: "foo",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
SameSite: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"baz": {
|
||||
Mirroring: &dynamic.Mirroring{
|
||||
Service: "foo",
|
||||
MaxBodySize: int64Ptr(42),
|
||||
Mirrors: []dynamic.MirrorService{
|
||||
{
|
||||
Name: "foo",
|
||||
Percent: 42,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ServersTransports: map[string]*dynamic.ServersTransport{
|
||||
"foo": {
|
||||
ServerName: "foo",
|
||||
InsecureSkipVerify: true,
|
||||
RootCAs: []traefiktls.FileOrContent{"rootca.pem"},
|
||||
Certificates: []traefiktls.Certificate{
|
||||
{
|
||||
CertFile: "cert.pem",
|
||||
KeyFile: "key.pem",
|
||||
},
|
||||
},
|
||||
MaxIdleConnsPerHost: 42,
|
||||
ForwardingTimeouts: &dynamic.ForwardingTimeouts{
|
||||
DialTimeout: 42,
|
||||
ResponseHeaderTimeout: 42,
|
||||
IdleConnTimeout: 42,
|
||||
},
|
||||
},
|
||||
},
|
||||
Models: map[string]*dynamic.Model{
|
||||
"foo": {
|
||||
Middlewares: []string{"foo"},
|
||||
TLS: &dynamic.RouterTLSConfig{
|
||||
Options: "foo",
|
||||
CertResolver: "foo",
|
||||
Domains: []types.Domain{
|
||||
{
|
||||
Main: "foo",
|
||||
SANs: []string{"foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*dynamic.Middleware{
|
||||
"foo": {
|
||||
AddPrefix: &dynamic.AddPrefix{
|
||||
Prefix: "foo",
|
||||
},
|
||||
StripPrefix: &dynamic.StripPrefix{
|
||||
Prefixes: []string{"foo"},
|
||||
ForceSlash: true,
|
||||
},
|
||||
StripPrefixRegex: &dynamic.StripPrefixRegex{
|
||||
Regex: []string{"foo"},
|
||||
},
|
||||
ReplacePath: &dynamic.ReplacePath{
|
||||
Path: "foo",
|
||||
},
|
||||
ReplacePathRegex: &dynamic.ReplacePathRegex{
|
||||
Regex: "foo",
|
||||
Replacement: "foo",
|
||||
},
|
||||
Chain: &dynamic.Chain{
|
||||
Middlewares: []string{"foo"},
|
||||
},
|
||||
IPWhiteList: &dynamic.IPWhiteList{
|
||||
SourceRange: []string{"foo"},
|
||||
IPStrategy: &dynamic.IPStrategy{
|
||||
Depth: 42,
|
||||
ExcludedIPs: []string{"127.0.0.1"},
|
||||
},
|
||||
},
|
||||
Headers: &dynamic.Headers{
|
||||
CustomRequestHeaders: map[string]string{"foo": "bar"},
|
||||
CustomResponseHeaders: map[string]string{"foo": "bar"},
|
||||
AccessControlAllowCredentials: true,
|
||||
AccessControlAllowHeaders: []string{"foo"},
|
||||
AccessControlAllowMethods: []string{"foo"},
|
||||
AccessControlAllowOrigin: "foo",
|
||||
AccessControlAllowOriginList: []string{"foo"},
|
||||
AccessControlAllowOriginListRegex: []string{"foo"},
|
||||
AccessControlExposeHeaders: []string{"foo"},
|
||||
AccessControlMaxAge: 42,
|
||||
AddVaryHeader: true,
|
||||
AllowedHosts: []string{"foo"},
|
||||
HostsProxyHeaders: []string{"foo"},
|
||||
SSLRedirect: true,
|
||||
SSLTemporaryRedirect: true,
|
||||
SSLHost: "foo",
|
||||
SSLProxyHeaders: map[string]string{"foo": "bar"},
|
||||
SSLForceHost: true,
|
||||
STSSeconds: 42,
|
||||
STSIncludeSubdomains: true,
|
||||
STSPreload: true,
|
||||
ForceSTSHeader: true,
|
||||
FrameDeny: true,
|
||||
CustomFrameOptionsValue: "foo",
|
||||
ContentTypeNosniff: true,
|
||||
BrowserXSSFilter: true,
|
||||
CustomBrowserXSSValue: "foo",
|
||||
ContentSecurityPolicy: "foo",
|
||||
PublicKey: "foo",
|
||||
ReferrerPolicy: "foo",
|
||||
FeaturePolicy: "foo",
|
||||
IsDevelopment: true,
|
||||
},
|
||||
Errors: &dynamic.ErrorPage{
|
||||
Status: []string{"foo"},
|
||||
Service: "foo",
|
||||
Query: "foo",
|
||||
},
|
||||
RateLimit: &dynamic.RateLimit{
|
||||
Average: 42,
|
||||
Period: 42,
|
||||
Burst: 42,
|
||||
SourceCriterion: &dynamic.SourceCriterion{
|
||||
IPStrategy: &dynamic.IPStrategy{
|
||||
Depth: 42,
|
||||
ExcludedIPs: []string{"foo"},
|
||||
},
|
||||
RequestHeaderName: "foo",
|
||||
RequestHost: true,
|
||||
},
|
||||
},
|
||||
RedirectRegex: &dynamic.RedirectRegex{
|
||||
Regex: "foo",
|
||||
Replacement: "foo",
|
||||
Permanent: true,
|
||||
},
|
||||
RedirectScheme: &dynamic.RedirectScheme{
|
||||
Scheme: "foo",
|
||||
Port: "foo",
|
||||
Permanent: true,
|
||||
},
|
||||
BasicAuth: &dynamic.BasicAuth{
|
||||
Users: []string{"foo"},
|
||||
UsersFile: "foo",
|
||||
Realm: "foo",
|
||||
RemoveHeader: true,
|
||||
HeaderField: "foo",
|
||||
},
|
||||
DigestAuth: &dynamic.DigestAuth{
|
||||
Users: []string{"foo"},
|
||||
UsersFile: "foo",
|
||||
RemoveHeader: true,
|
||||
Realm: "foo",
|
||||
HeaderField: "foo",
|
||||
},
|
||||
ForwardAuth: &dynamic.ForwardAuth{
|
||||
Address: "127.0.0.1",
|
||||
TLS: &dynamic.ClientTLS{
|
||||
CA: "ca.pem",
|
||||
CAOptional: true,
|
||||
Cert: "cert.pem",
|
||||
Key: "cert.pem",
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
TrustForwardHeader: true,
|
||||
AuthResponseHeaders: []string{"foo"},
|
||||
AuthResponseHeadersRegex: "foo",
|
||||
AuthRequestHeaders: []string{"foo"},
|
||||
},
|
||||
InFlightReq: &dynamic.InFlightReq{
|
||||
Amount: 42,
|
||||
SourceCriterion: &dynamic.SourceCriterion{
|
||||
IPStrategy: &dynamic.IPStrategy{
|
||||
Depth: 42,
|
||||
ExcludedIPs: []string{"foo"},
|
||||
},
|
||||
RequestHeaderName: "foo",
|
||||
RequestHost: true,
|
||||
},
|
||||
},
|
||||
Buffering: &dynamic.Buffering{
|
||||
MaxRequestBodyBytes: 42,
|
||||
MemRequestBodyBytes: 42,
|
||||
MaxResponseBodyBytes: 42,
|
||||
MemResponseBodyBytes: 42,
|
||||
RetryExpression: "foo",
|
||||
},
|
||||
CircuitBreaker: &dynamic.CircuitBreaker{
|
||||
Expression: "foo",
|
||||
},
|
||||
Compress: &dynamic.Compress{
|
||||
ExcludedContentTypes: []string{"foo"},
|
||||
},
|
||||
PassTLSClientCert: &dynamic.PassTLSClientCert{
|
||||
PEM: true,
|
||||
Info: &dynamic.TLSClientCertificateInfo{
|
||||
NotAfter: true,
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
Subject: &dynamic.TLSCLientCertificateDNInfo{
|
||||
Country: true,
|
||||
Province: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
CommonName: true,
|
||||
SerialNumber: true,
|
||||
DomainComponent: true,
|
||||
},
|
||||
Issuer: &dynamic.TLSCLientCertificateDNInfo{
|
||||
Country: true,
|
||||
Province: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
CommonName: true,
|
||||
SerialNumber: true,
|
||||
DomainComponent: true,
|
||||
},
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
Retry: &dynamic.Retry{
|
||||
Attempts: 42,
|
||||
InitialInterval: 42,
|
||||
},
|
||||
ContentType: &dynamic.ContentType{
|
||||
AutoDetect: true,
|
||||
},
|
||||
Plugin: map[string]dynamic.PluginConf{
|
||||
"foo": {
|
||||
"answer": struct{ Answer int }{
|
||||
Answer: 42,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
config.TCP = &dynamic.TCPConfiguration{
|
||||
Routers: map[string]*dynamic.TCPRouter{
|
||||
"foo": {
|
||||
EntryPoints: []string{"foo"},
|
||||
Service: "foo",
|
||||
Rule: "foo",
|
||||
TLS: &dynamic.RouterTCPTLSConfig{
|
||||
Passthrough: true,
|
||||
Options: "foo",
|
||||
CertResolver: "foo",
|
||||
Domains: []types.Domain{
|
||||
{
|
||||
Main: "foo",
|
||||
SANs: []string{"foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Services: map[string]*dynamic.TCPService{
|
||||
"foo": {
|
||||
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||
TerminationDelay: intPtr(42),
|
||||
ProxyProtocol: &dynamic.ProxyProtocol{
|
||||
Version: 42,
|
||||
},
|
||||
Servers: []dynamic.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"bar": {
|
||||
Weighted: &dynamic.TCPWeightedRoundRobin{
|
||||
Services: []dynamic.TCPWRRService{
|
||||
{
|
||||
Name: "foo",
|
||||
Weight: intPtr(42),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
config.UDP = &dynamic.UDPConfiguration{
|
||||
Routers: map[string]*dynamic.UDPRouter{
|
||||
"foo": {
|
||||
EntryPoints: []string{"foo"},
|
||||
Service: "foo",
|
||||
},
|
||||
},
|
||||
Services: map[string]*dynamic.UDPService{
|
||||
"foo": {
|
||||
LoadBalancer: &dynamic.UDPServersLoadBalancer{
|
||||
Servers: []dynamic.UDPServer{
|
||||
{
|
||||
Address: "127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"bar": {
|
||||
Weighted: &dynamic.UDPWeightedRoundRobin{
|
||||
Services: []dynamic.UDPWRRService{
|
||||
{
|
||||
Name: "foo",
|
||||
Weight: intPtr(42),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
config.TLS = &dynamic.TLSConfiguration{
|
||||
Options: map[string]traefiktls.Options{
|
||||
"foo": {
|
||||
MinVersion: "foo",
|
||||
MaxVersion: "foo",
|
||||
CipherSuites: []string{"foo"},
|
||||
CurvePreferences: []string{"foo"},
|
||||
ClientAuth: traefiktls.ClientAuth{
|
||||
CAFiles: []traefiktls.FileOrContent{"ca.pem"},
|
||||
ClientAuthType: "RequireAndVerifyClientCert",
|
||||
},
|
||||
SniStrict: true,
|
||||
PreferServerCipherSuites: true,
|
||||
},
|
||||
},
|
||||
Certificates: []*traefiktls.CertAndStores{
|
||||
{
|
||||
Certificate: traefiktls.Certificate{
|
||||
CertFile: "cert.pem",
|
||||
KeyFile: "key.pem",
|
||||
},
|
||||
Stores: []string{"foo"},
|
||||
},
|
||||
},
|
||||
Stores: map[string]traefiktls.Store{
|
||||
"foo": {
|
||||
DefaultCertificate: &traefiktls.Certificate{
|
||||
CertFile: "cert.pem",
|
||||
KeyFile: "key.pem",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expectedConfiguration, err := ioutil.ReadFile("./testdata/anonymized-dynamic-config.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
cleanJSON, err := Do(config, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
if *updateExpected {
|
||||
require.NoError(t, ioutil.WriteFile("testdata/anonymized-dynamic-config.json", []byte(cleanJSON), 0666))
|
||||
}
|
||||
|
||||
expected := strings.TrimSuffix(string(expectedConfiguration), "\n")
|
||||
assert.Equal(t, expected, cleanJSON)
|
||||
}
|
||||
|
||||
func TestDo_staticConfiguration(t *testing.T) {
|
||||
config := &static.Configuration{}
|
||||
|
||||
config.Global = &static.Global{
|
||||
|
@ -538,3 +970,15 @@ func TestDo_globalConfiguration(t *testing.T) {
|
|||
expected := strings.TrimSuffix(string(expectedConfiguration), "\n")
|
||||
assert.Equal(t, expected, cleanJSON)
|
||||
}
|
||||
|
||||
func boolPtr(value bool) *bool {
|
||||
return &value
|
||||
}
|
||||
|
||||
func intPtr(value int) *int {
|
||||
return &value
|
||||
}
|
||||
|
||||
func int64Ptr(value int64) *int64 {
|
||||
return &value
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type Courgette struct {
|
||||
|
@ -39,7 +40,6 @@ func Test_doOnStruct(t *testing.T) {
|
|||
name string
|
||||
base *Carotte
|
||||
expected *Carotte
|
||||
hasError bool
|
||||
}{
|
||||
{
|
||||
name: "primitive",
|
||||
|
@ -145,7 +145,7 @@ func Test_doOnStruct(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "export map string/struct (UNSAFE)",
|
||||
name: "export map string/struct",
|
||||
base: &Carotte{
|
||||
Name: "koko",
|
||||
ESAubergine: map[string]Tomate{
|
||||
|
@ -158,11 +158,10 @@ func Test_doOnStruct(t *testing.T) {
|
|||
Name: "xxxx",
|
||||
ESAubergine: map[string]Tomate{
|
||||
"foo": {
|
||||
Ji: "JiJiJi",
|
||||
Ji: "xxxx",
|
||||
},
|
||||
},
|
||||
},
|
||||
hasError: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -170,12 +169,7 @@ func Test_doOnStruct(t *testing.T) {
|
|||
t.Run(test.name, func(t *testing.T) {
|
||||
val := reflect.ValueOf(test.base).Elem()
|
||||
err := doOnStruct(val)
|
||||
if !test.hasError && err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if test.hasError && err == nil {
|
||||
t.Fatal("Got no error but want an error.")
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.EqualValues(t, test.expected, test.base)
|
||||
})
|
||||
|
|
476
pkg/anonymize/testdata/anonymized-dynamic-config.json
vendored
Normal file
476
pkg/anonymize/testdata/anonymized-dynamic-config.json
vendored
Normal file
|
@ -0,0 +1,476 @@
|
|||
{
|
||||
"http": {
|
||||
"routers": {
|
||||
"foo": {
|
||||
"entryPoints": [
|
||||
"foo"
|
||||
],
|
||||
"middlewares": [
|
||||
"foo"
|
||||
],
|
||||
"service": "foo",
|
||||
"rule": "xxxx",
|
||||
"priority": 42,
|
||||
"tls": {
|
||||
"options": "foo",
|
||||
"certResolver": "foo",
|
||||
"domains": [
|
||||
{
|
||||
"main": "xxxx",
|
||||
"sans": [
|
||||
"xxxx"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"bar": {
|
||||
"weighted": {
|
||||
"services": [
|
||||
{
|
||||
"name": "foo",
|
||||
"weight": 42
|
||||
}
|
||||
],
|
||||
"sticky": {
|
||||
"cookie": {
|
||||
"name": "foo",
|
||||
"secure": true,
|
||||
"httpOnly": true,
|
||||
"sameSite": "foo"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"baz": {
|
||||
"mirroring": {
|
||||
"service": "foo",
|
||||
"maxBodySize": 42,
|
||||
"mirrors": [
|
||||
{
|
||||
"name": "foo",
|
||||
"percent": 42
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"foo": {
|
||||
"loadBalancer": {
|
||||
"sticky": {
|
||||
"cookie": {
|
||||
"name": "foo",
|
||||
"secure": true,
|
||||
"httpOnly": true,
|
||||
"sameSite": "foo"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "xxxx"
|
||||
}
|
||||
],
|
||||
"healthCheck": {
|
||||
"scheme": "foo",
|
||||
"path": "foo",
|
||||
"port": 42,
|
||||
"interval": "foo",
|
||||
"timeout": "foo",
|
||||
"hostname": "xxxx",
|
||||
"followRedirects": true,
|
||||
"headers": {
|
||||
"foo": "bar"
|
||||
}
|
||||
},
|
||||
"passHostHeader": true,
|
||||
"responseForwarding": {
|
||||
"flushInterval": "foo"
|
||||
},
|
||||
"serversTransport": "foo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"middlewares": {
|
||||
"foo": {
|
||||
"addPrefix": {
|
||||
"prefix": "foo"
|
||||
},
|
||||
"stripPrefix": {
|
||||
"prefixes": [
|
||||
"foo"
|
||||
],
|
||||
"forceSlash": true
|
||||
},
|
||||
"stripPrefixRegex": {
|
||||
"regex": [
|
||||
"foo"
|
||||
]
|
||||
},
|
||||
"replacePath": {
|
||||
"path": "foo"
|
||||
},
|
||||
"replacePathRegex": {
|
||||
"regex": "foo",
|
||||
"replacement": "foo"
|
||||
},
|
||||
"chain": {
|
||||
"middlewares": [
|
||||
"foo"
|
||||
]
|
||||
},
|
||||
"ipWhiteList": {
|
||||
"sourceRange": [
|
||||
"xxxx"
|
||||
],
|
||||
"ipStrategy": {
|
||||
"depth": 42,
|
||||
"excludedIPs": [
|
||||
"xxxx"
|
||||
]
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"customRequestHeaders": {
|
||||
"foo": "bar"
|
||||
},
|
||||
"customResponseHeaders": {
|
||||
"foo": "bar"
|
||||
},
|
||||
"accessControlAllowCredentials": true,
|
||||
"accessControlAllowHeaders": [
|
||||
"foo"
|
||||
],
|
||||
"accessControlAllowMethods": [
|
||||
"foo"
|
||||
],
|
||||
"accessControlAllowOrigin": "xxxx",
|
||||
"accessControlAllowOriginList": [
|
||||
"xxxx"
|
||||
],
|
||||
"accessControlAllowOriginListRegex": [
|
||||
"xxxx"
|
||||
],
|
||||
"accessControlExposeHeaders": [
|
||||
"foo"
|
||||
],
|
||||
"accessControlMaxAge": 42,
|
||||
"addVaryHeader": true,
|
||||
"allowedHosts": [
|
||||
"xxxx"
|
||||
],
|
||||
"hostsProxyHeaders": [
|
||||
"foo"
|
||||
],
|
||||
"sslRedirect": true,
|
||||
"sslTemporaryRedirect": true,
|
||||
"sslHost": "xxxx",
|
||||
"sslForceHost": true,
|
||||
"stsSeconds": 42,
|
||||
"stsIncludeSubdomains": true,
|
||||
"stsPreload": true,
|
||||
"forceSTSHeader": true,
|
||||
"frameDeny": true,
|
||||
"customFrameOptionsValue": "xxxx",
|
||||
"contentTypeNosniff": true,
|
||||
"browserXssFilter": true,
|
||||
"customBrowserXSSValue": "xxxx",
|
||||
"contentSecurityPolicy": "xxxx",
|
||||
"publicKey": "xxxx",
|
||||
"referrerPolicy": "foo",
|
||||
"featurePolicy": "foo",
|
||||
"isDevelopment": true
|
||||
},
|
||||
"errors": {
|
||||
"status": [
|
||||
"foo"
|
||||
],
|
||||
"service": "foo",
|
||||
"query": "foo"
|
||||
},
|
||||
"rateLimit": {
|
||||
"average": 42,
|
||||
"period": 42,
|
||||
"burst": 42,
|
||||
"sourceCriterion": {
|
||||
"ipStrategy": {
|
||||
"depth": 42,
|
||||
"excludedIPs": [
|
||||
"xxxx"
|
||||
]
|
||||
},
|
||||
"requestHeaderName": "foo",
|
||||
"requestHost": true
|
||||
}
|
||||
},
|
||||
"redirectRegex": {
|
||||
"regex": "xxxx",
|
||||
"replacement": "xxxx",
|
||||
"permanent": true
|
||||
},
|
||||
"redirectScheme": {
|
||||
"scheme": "foo",
|
||||
"port": "foo",
|
||||
"permanent": true
|
||||
},
|
||||
"basicAuth": {
|
||||
"users": [
|
||||
"xxxx"
|
||||
],
|
||||
"usersFile": "xxxx",
|
||||
"realm": "xxxx",
|
||||
"removeHeader": true,
|
||||
"headerField": "foo"
|
||||
},
|
||||
"digestAuth": {
|
||||
"users": [
|
||||
"xxxx"
|
||||
],
|
||||
"usersFile": "xxxx",
|
||||
"removeHeader": true,
|
||||
"realm": "xxxx",
|
||||
"headerField": "foo"
|
||||
},
|
||||
"forwardAuth": {
|
||||
"address": "xxxx",
|
||||
"tls": {
|
||||
"ca": "xxxx",
|
||||
"caOptional": true,
|
||||
"cert": "xxxx",
|
||||
"key": "xxxx",
|
||||
"insecureSkipVerify": true
|
||||
},
|
||||
"trustForwardHeader": true,
|
||||
"authResponseHeaders": [
|
||||
"foo"
|
||||
],
|
||||
"authResponseHeadersRegex": "foo",
|
||||
"authRequestHeaders": [
|
||||
"foo"
|
||||
]
|
||||
},
|
||||
"inFlightReq": {
|
||||
"amount": 42,
|
||||
"sourceCriterion": {
|
||||
"ipStrategy": {
|
||||
"depth": 42,
|
||||
"excludedIPs": [
|
||||
"xxxx"
|
||||
]
|
||||
},
|
||||
"requestHeaderName": "foo",
|
||||
"requestHost": true
|
||||
}
|
||||
},
|
||||
"buffering": {
|
||||
"maxRequestBodyBytes": 42,
|
||||
"memRequestBodyBytes": 42,
|
||||
"maxResponseBodyBytes": 42,
|
||||
"memResponseBodyBytes": 42,
|
||||
"retryExpression": "foo"
|
||||
},
|
||||
"circuitBreaker": {
|
||||
"expression": "foo"
|
||||
},
|
||||
"compress": {
|
||||
"excludedContentTypes": [
|
||||
"foo"
|
||||
]
|
||||
},
|
||||
"passTLSClientCert": {
|
||||
"pem": true,
|
||||
"info": {
|
||||
"notAfter": true,
|
||||
"notBefore": true,
|
||||
"sans": true,
|
||||
"subject": {
|
||||
"country": true,
|
||||
"province": true,
|
||||
"locality": true,
|
||||
"organization": true,
|
||||
"commonName": true,
|
||||
"serialNumber": true,
|
||||
"domainComponent": true
|
||||
},
|
||||
"issuer": {
|
||||
"country": true,
|
||||
"province": true,
|
||||
"locality": true,
|
||||
"organization": true,
|
||||
"commonName": true,
|
||||
"serialNumber": true,
|
||||
"domainComponent": true
|
||||
},
|
||||
"serialNumber": true
|
||||
}
|
||||
},
|
||||
"retry": {
|
||||
"attempts": 42,
|
||||
"initialInterval": 42
|
||||
},
|
||||
"contentType": {
|
||||
"autoDetect": true
|
||||
},
|
||||
"plugin": {
|
||||
"foo": {
|
||||
"answer": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"models": {
|
||||
"foo": {
|
||||
"middlewares": [
|
||||
"foo"
|
||||
],
|
||||
"tls": {
|
||||
"options": "foo",
|
||||
"certResolver": "foo",
|
||||
"domains": [
|
||||
{
|
||||
"main": "xxxx",
|
||||
"sans": [
|
||||
"xxxx"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"serversTransports": {
|
||||
"foo": {
|
||||
"serverName": "xxxx",
|
||||
"insecureSkipVerify": true,
|
||||
"rootCAs": [
|
||||
"xxxx"
|
||||
],
|
||||
"certificates": [
|
||||
{
|
||||
"certFile": "xxxx",
|
||||
"keyFile": "xxxx"
|
||||
}
|
||||
],
|
||||
"maxIdleConnsPerHost": 42,
|
||||
"forwardingTimeouts": {
|
||||
"dialTimeout": 42,
|
||||
"responseHeaderTimeout": 42,
|
||||
"idleConnTimeout": 42
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tcp": {
|
||||
"routers": {
|
||||
"foo": {
|
||||
"entryPoints": [
|
||||
"foo"
|
||||
],
|
||||
"service": "foo",
|
||||
"rule": "xxxx",
|
||||
"tls": {
|
||||
"passthrough": true,
|
||||
"options": "foo",
|
||||
"certResolver": "foo",
|
||||
"domains": [
|
||||
{
|
||||
"main": "xxxx",
|
||||
"sans": [
|
||||
"xxxx"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"bar": {
|
||||
"weighted": {
|
||||
"services": [
|
||||
{
|
||||
"name": "foo",
|
||||
"weight": 42
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"foo": {
|
||||
"loadBalancer": {
|
||||
"terminationDelay": 42,
|
||||
"proxyProtocol": {
|
||||
"version": 42
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"address": "xxxx"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"udp": {
|
||||
"routers": {
|
||||
"foo": {
|
||||
"entryPoints": [
|
||||
"foo"
|
||||
],
|
||||
"service": "foo"
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"bar": {
|
||||
"weighted": {
|
||||
"services": [
|
||||
{
|
||||
"name": "foo",
|
||||
"weight": 42
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"foo": {
|
||||
"loadBalancer": {
|
||||
"servers": [
|
||||
{
|
||||
"address": "xxxx"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tls": {
|
||||
"certificates": [
|
||||
{
|
||||
"certFile": "xxxx",
|
||||
"keyFile": "xxxx",
|
||||
"stores": [
|
||||
"foo"
|
||||
]
|
||||
}
|
||||
],
|
||||
"options": {
|
||||
"foo": {
|
||||
"minVersion": "foo",
|
||||
"maxVersion": "foo",
|
||||
"cipherSuites": [
|
||||
"foo"
|
||||
],
|
||||
"curvePreferences": [
|
||||
"foo"
|
||||
],
|
||||
"clientAuth": {},
|
||||
"sniStrict": true,
|
||||
"preferServerCipherSuites": true
|
||||
}
|
||||
},
|
||||
"stores": {
|
||||
"foo": {
|
||||
"defaultCertificate": {
|
||||
"certFile": "xxxx",
|
||||
"keyFile": "xxxx"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue