From 0ee0a767171fa766eebf1132978c1dd9c2f5e658 Mon Sep 17 00:00:00 2001 From: Viktor Date: Sun, 4 Mar 2018 22:15:06 +0100 Subject: [PATCH] [release]Indexable sslVerifyNone Starting from this release, field can be indexed. Fixes #443. --- actions/reconfigure_test.go | 2 +- docs/usage.md | 6 +++--- proxy/template.go | 8 ++++---- proxy/types.go | 22 +++++++++++++--------- proxy/types_test.go | 22 +++++++++++++++++----- server/server.go | 2 ++ server/server_test.go | 11 +++++------ 7 files changed, 45 insertions(+), 28 deletions(-) diff --git a/actions/reconfigure_test.go b/actions/reconfigure_test.go index a38a71e1..667d2220 100644 --- a/actions/reconfigure_test.go +++ b/actions/reconfigure_test.go @@ -280,7 +280,7 @@ backend https-myService-be1234_32 func (s ReconfigureTestSuite) Test_GetTemplates_AddSllVerifyNone_WhenSslVerifyNoneIsSet() { s.reconfigure.Service.ServiceDest[0].Port = "1234" s.reconfigure.Service.ServiceDest[0].Index = 6 - s.reconfigure.SslVerifyNone = true + s.reconfigure.Service.ServiceDest[0].SslVerifyNone = true expected := ` backend myService-be1234_6 mode http diff --git a/docs/usage.md b/docs/usage.md index f7c2bc77..347119ff 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -38,7 +38,7 @@ The following query parameters can be used to send a *reconfigure* request to *D |timeoutTunnel |The tunnel timeout in seconds.
**Default:** `3600`
**Example:** `3600`| |userDef |User defined value. This value is not used with current template. It is designed as a way to provide additional data that can be used with **custom templates**. The parameter must be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `userDef.1`, `userDef.2`, and so on).| -Multiple destinations for a single service can be specified by adding index as a suffix to `servicePath`, `servicePathExclude`, `srcPort`, `port`, `userAgent`, `ignoreAuthorization`, `serviceDomain`, `allowedMethods`, `deniedMethods`, `denyHttp`, `httpsOnly`, `redirectFromDomain`, `reqMode`, `reqPathSearchReplace`, `outboundHostname`, or `userDef` parameters. In that case, `srcPort` is required. +Multiple destinations for a single service can be specified by adding index as a suffix to `servicePath`, `servicePathExclude`, `srcPort`, `port`, `userAgent`, `ignoreAuthorization`, `serviceDomain`, `allowedMethods`, `deniedMethods`, `denyHttp`, `httpsOnly`, `redirectFromDomain`, `reqMode`, `reqPathSearchReplace`, `outboundHostname`, `sslVerifyNone`, or `userDef` parameters. In that case, `srcPort` is required. ### HTTP Mode Query Parameters @@ -64,7 +64,7 @@ The following query parameters can be used only when `reqMode` is set to `http` |servicePath |The URL path of the service. Multiple values should be separated with comma (`,`). The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `servicePath.1`, `servicePath.2`, and so on). This parameter **is mandatory** unless `serviceDomain` is specified.
**Example:** `/api/v1/books`| |servicePathExclude|The URL path that should be excluded from the rules. Multiple values should be separated with comma (`,`). The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `servicePathExclude.1`, `servicePathExclude.2`, and so on).
**Example:** `/metrics`| |sessionType |Determines the type of sticky sessions. If set to `sticky-server`, session cookie will be set by the proxy. Any other value means that sticky sessions are not used and load balancing is performed by Docker's Overlay network.
**Example:** `sticky-server`| -|sslVerifyNone|If set to true, backend server certificates are not verified. This flag should be set for SSL enabled backend services.
**Example:** `true`
**Default Value:** `false`| +|sslVerifyNone|If set to true, backend server certificates are not verified. This flag should be set for SSL enabled backend services. The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `sslVerifyNone.1`, `sslVerifyNone.2`, and so on).
**Example:** `true`
**Default Value:** `false`| |templateBePath|The path to the template representing a snippet of the backend configuration. If specified, the backend template will be loaded from the specified file. See the [Templates](#templates) section for more info.
**Example:** `/tmpl/be.tmpl`| |templateFePath|The path to the template representing a snippet of the frontend configuration. If specified, the frontend template will be loaded from the specified file. See the [Templates](#templates) section for more info.
**Example:** `/tmpl/fe.tmpl`| |userAgent |A comma-separated list of user agents. only requests with the same User-Agent will be forwarded to the backend. The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `userAgent.1`, `userAgent.2`, and so on). If the same service is used for multiple agents, it is recommended to use indexes with the last one being without `userAgent`. That way, if no match is found, the last indexed destination will be used as catch-all.
**Example:** `googlebot,iphone`| @@ -73,7 +73,7 @@ The following query parameters can be used only when `reqMode` is set to `http` |usersPassEncrypted|Indicates whether passwords provided by `users` or `usersSecret` contain encrypted data. Passwords can be encrypted with the command `mkpasswd -m sha-512 password1`.
**Example:** `true`
**Default Value:** `false`| |verifyClientSsl|Whether to verify client SSL and, if it is not valid, deny request and return 403 Forbidden status code. SSL is validated against the `ca-file` specified through the environment variable `CA_FILE`.
**Example:** true
**Default Value:** `false`| -Multiple destinations for a single service can be specified by adding index as a suffix to `servicePath`, `servicePathExclude`, `srcPort`, `port`, `userAgent`, `ignoreAuthorization`, `serviceDomain`, `allowedMethods`, `deniedMethods`, `denyHttp`, `httpsOnly`, `redirectFromDomain`, `ReqMode`, `reqPathSearchReplace`, `outboundHostname`, or `userDef` parameters. In that case, `srcPort` is required. +Multiple destinations for a single service can be specified by adding index as a suffix to `servicePath`, `servicePathExclude`, `srcPort`, `port`, `userAgent`, `ignoreAuthorization`, `serviceDomain`, `allowedMethods`, `deniedMethods`, `denyHttp`, `httpsOnly`, `redirectFromDomain`, `ReqMode`, `reqPathSearchReplace`, `outboundHostname`, `sslVerifyNone`, or `userDef` parameters. In that case, `srcPort` is required. ### TCP Mode HTTP Query Parameters diff --git a/proxy/template.go b/proxy/template.go index e114a607..f93d3130 100644 --- a/proxy/template.go +++ b/proxy/template.go @@ -172,10 +172,10 @@ backend {{$.AclName}}-be{{.Port}}_{{.Index}} cookie {{$.ServiceName}} insert indirect nocache {{- end}} {{- range $i, $t := $.Tasks}} - server {{$.ServiceName}}_{{$i}} {{$t}}:{{$sd.Port}} check cookie {{$.ServiceName}}_{{$i}}{{if eq $.SslVerifyNone true}} ssl verify none{{end}} + server {{$.ServiceName}}_{{$i}} {{$t}}:{{$sd.Port}} check cookie {{$.ServiceName}}_{{$i}}{{if eq $sd.SslVerifyNone true}} ssl verify none{{end}} {{- end}} {{- if not $.Tasks}} - server {{$.ServiceName}} {{if eq $sd.OutboundHostname ""}}{{$.ServiceName}}{{end}}{{if ne $sd.OutboundHostname ""}}{{$sd.OutboundHostname}}{{end}}:{{$sd.Port}}{{if eq $.CheckResolvers true}} check resolvers docker{{end}}{{if eq $.SslVerifyNone true}} ssl verify none{{end}} + server {{$.ServiceName}} {{if eq $sd.OutboundHostname ""}}{{$.ServiceName}}{{end}}{{if ne $sd.OutboundHostname ""}}{{$sd.OutboundHostname}}{{end}}:{{$sd.Port}}{{if eq $.CheckResolvers true}} check resolvers docker{{end}}{{if eq $sd.SslVerifyNone true}} ssl verify none{{end}} {{- end}} {{- if not .IgnoreAuthorization}} {{- if and ($.Users) (not .IgnoreAuthorization)}} @@ -235,10 +235,10 @@ backend https-{{$.AclName}}-be{{.Port}}_{{.Index}} cookie {{$.ServiceName}} insert indirect nocache {{- end}} {{- range $i, $t := $.Tasks}} - server {{$.ServiceName}}_{{$i}} {{$t}}:{{$.HttpsPort}} check cookie {{$.ServiceName}}_{{$i}}{{if eq $.SslVerifyNone true}} ssl verify none{{end}} + server {{$.ServiceName}}_{{$i}} {{$t}}:{{$.HttpsPort}} check cookie {{$.ServiceName}}_{{$i}}{{if eq $sd.SslVerifyNone true}} ssl verify none{{end}} {{- end}} {{- if not $.Tasks}} - server {{$.ServiceName}} {{if eq $sd.OutboundHostname ""}}{{$.ServiceName}}{{end}}{{if ne $sd.OutboundHostname ""}}{{$sd.OutboundHostname}}{{end}}:{{$.HttpsPort}}{{if eq $.CheckResolvers true}} check resolvers docker{{end}}{{if eq $.SslVerifyNone true}} ssl verify none{{end}} + server {{$.ServiceName}} {{if eq $sd.OutboundHostname ""}}{{$.ServiceName}}{{end}}{{if ne $sd.OutboundHostname ""}}{{$sd.OutboundHostname}}{{end}}:{{$.HttpsPort}}{{if eq $.CheckResolvers true}} check resolvers docker{{end}}{{if eq $sd.SslVerifyNone true}} ssl verify none{{end}} {{- end}} {{- if not .IgnoreAuthorization}} {{- if $.Users}} diff --git a/proxy/types.go b/proxy/types.go index 3ef218fc..4dbee1dd 100644 --- a/proxy/types.go +++ b/proxy/types.go @@ -57,6 +57,8 @@ type ServiceDest struct { SrcPortAcl string // Internal use only. Do not modify. SrcPortAclName string + // If set to true, server certificates are not verified. This flag should be set for SSL enabled backend services. + SslVerifyNone bool // Whether to verify client SSL and deny request when it is invalid VerifyClientSsl bool // If specified, only requests with the same agent will be forwarded to the backend. @@ -149,8 +151,6 @@ type Service struct { SetReqHeader []string `split_words:"true"` // Additional headers that will be set to the response before forwarding it to the client. If a specified header exists, it will be replaced with the new one. SetResHeader []string `split_words:"true"` - // If set to true, server certificates are not verified. This flag should be set for SSL enabled backend services. - SslVerifyNone bool `split_words:"true"` // The path to the template representing a snippet of the backend configuration. // If specified, the backend template will be loaded from the specified file. // If specified, `templateFePath` must be set as well. @@ -325,7 +325,7 @@ func GetServiceFromProvider(provider ServiceParameterProvider) *Service { sr.ServiceName, provider.GetString("users"), provider.GetString("usersSecret"), - getBoolParam(provider, "usersPassEncrypted"), + getBoolParam(provider, "usersPassEncrypted", ""), globalUsersString, globalUsersEncrypted, ) @@ -430,10 +430,10 @@ func getServiceDest(sr *Service, provider ServiceParameterProvider, index int) S return ServiceDest{ AllowedMethods: getSliceFromString(provider, fmt.Sprintf("allowedMethods%s", suffix)), DeniedMethods: getSliceFromString(provider, fmt.Sprintf("deniedMethods%s", suffix)), - DenyHttp: getBoolParam(provider, fmt.Sprintf("denyHttp%s", suffix)), - HttpsOnly: getBoolParam(provider, fmt.Sprintf("httpsOnly%s", suffix)), + DenyHttp: getBoolParam(provider, "denyHttp", suffix), + HttpsOnly: getBoolParam(provider, "httpsOnly", suffix), HttpsRedirectCode: provider.GetString(fmt.Sprintf("httpsRedirectCode%s", suffix)), - IgnoreAuthorization: getBoolParam(provider, fmt.Sprintf("ignoreAuthorization%s", suffix)), + IgnoreAuthorization: getBoolParam(provider, "ignoreAuthorization", suffix), OutboundHostname: outboundHostname, Port: provider.GetString(fmt.Sprintf("port%s", suffix)), RedirectFromDomain: getSliceFromString(provider, fmt.Sprintf("redirectFromDomain%s", suffix)), @@ -445,7 +445,8 @@ func getServiceDest(sr *Service, provider ServiceParameterProvider, index int) S ServicePath: getSliceFromString(provider, fmt.Sprintf("servicePath%s", suffix)), ServicePathExclude: getSliceFromString(provider, fmt.Sprintf("servicePathExclude%s", suffix)), SrcPort: srcPort, - VerifyClientSsl: getBoolParam(provider, fmt.Sprintf("verifyClientSsl%s", suffix)), + SslVerifyNone: getBoolParam(provider, "sslVerifyNone", suffix), + VerifyClientSsl: getBoolParam(provider, "verifyClientSsl", suffix), UserAgent: userAgent, UserDef: provider.GetString(fmt.Sprintf("userDef%s", suffix)), Index: sdIndex, @@ -465,9 +466,12 @@ func isServiceDestValid(sd *ServiceDest) bool { return len(sd.ServicePath) > 0 || len(sd.Port) > 0 } -func getBoolParam(req ServiceParameterProvider, param string) bool { +func getBoolParam(req ServiceParameterProvider, param, index string) bool { value := false - if len(req.GetString(param)) > 0 { + key := fmt.Sprintf("%s%s", param, index) + if len(req.GetString(key)) > 0 { + value, _ = strconv.ParseBool(req.GetString(key)) + } else if len(req.GetString(param)) > 0 { value, _ = strconv.ParseBool(req.GetString(param)) } return value diff --git a/proxy/types_test.go b/proxy/types_test.go index 16021372..04ec19f3 100644 --- a/proxy/types_test.go +++ b/proxy/types_test.go @@ -192,6 +192,18 @@ func (s *TypesTestSuite) Test_GetServiceFromProvider_ReturnsProxyServiceWithInde s.Equal(expected, *actual) } +func (s *TypesTestSuite) Test_GetServiceFromProvider_UsesDefaultValues_WhenBoolIndexIsNotPresent() { + expected := s.getExpectedService() + serviceMap := s.getServiceMap(expected, ".1", ",") + delete(serviceMap, "sslVerifyNone.1") + serviceMap["sslVerifyNone"] = "true" + provider := mapParameterProvider{&serviceMap} + + actual := GetServiceFromProvider(&provider) + + s.Equal(expected, *actual) +} + func (s *TypesTestSuite) Test_GetServiceFromProvider_UsesSeparatorFromEnvVar() { separatorOrig := os.Getenv("SEPARATOR") defer func() { os.Setenv("SEPARATOR", separatorOrig) }() @@ -374,7 +386,6 @@ func (s *TypesTestSuite) getServiceMap(expected Service, indexSuffix, separator "sessionType": expected.SessionType, "setReqHeader": strings.Join(expected.SetReqHeader, separator), "setResHeader": strings.Join(expected.SetResHeader, separator), - "sslVerifyNone": strconv.FormatBool(expected.SslVerifyNone), "templateBePath": expected.TemplateBePath, "templateFePath": expected.TemplateFePath, "timeoutServer": expected.TimeoutServer, @@ -396,6 +407,7 @@ func (s *TypesTestSuite) getServiceMap(expected Service, indexSuffix, separator "serviceDomain" + indexSuffix: strings.Join(expected.ServiceDest[0].ServiceDomain, separator), "serviceHeader" + indexSuffix: header, "servicePath" + indexSuffix: strings.Join(expected.ServiceDest[0].ServicePath, separator), + "sslVerifyNone" + indexSuffix: strconv.FormatBool(expected.ServiceDest[0].SslVerifyNone), "userAgent" + indexSuffix: strings.Join(expected.ServiceDest[0].UserAgent.Value, separator), "userDef" + indexSuffix: expected.ServiceDest[0].UserDef, "verifyClientSsl" + indexSuffix: strconv.FormatBool(expected.ServiceDest[0].VerifyClientSsl), @@ -429,13 +441,14 @@ func (s *TypesTestSuite) getExpectedService() Service { OutboundHostname: "outboundHostname", Port: "1234", RedirectFromDomain: []string{"sub.domain1", "sub.domain2"}, + ReqMode: "reqMode", + ReqPathSearchReplace: "something,else:foo,bar", + ReqPathSearchReplaceFormatted: []string{"reqPathSearch,reqPathReplace", "something,else", "foo,bar"}, ServiceDomain: []string{"domain1", "domain2"}, ServiceHeader: map[string]string{"X-Version": "3", "name": "Viktor"}, ServicePath: []string{"/"}, ServicePathExclude: []string{}, - ReqMode: "reqMode", - ReqPathSearchReplace: "something,else:foo,bar", - ReqPathSearchReplaceFormatted: []string{"reqPathSearch,reqPathReplace", "something,else", "foo,bar"}, + SslVerifyNone: true, UserAgent: UserAgent{Value: []string{"agent-1", "agent-2/replace-with_"}, AclName: "agent_1_agent_2_replace_with_"}, UserDef: "userDef", VerifyClientSsl: true, @@ -444,7 +457,6 @@ func (s *TypesTestSuite) getExpectedService() Service { ServiceName: "serviceName", SetReqHeader: []string{"set-header-1", "set-header-2"}, SetResHeader: []string{"set-header-1", "set-header-2"}, - SslVerifyNone: true, TemplateBePath: "templateBePath", TemplateFePath: "templateFePath", TimeoutServer: "timeoutServer", diff --git a/server/server.go b/server/server.go index 33b47ed0..c4277f22 100644 --- a/server/server.go +++ b/server/server.go @@ -270,6 +270,7 @@ func (m *serve) getServiceFromEnvVars(prefix string) (proxy.Service, error) { verifyClientSsl, _ := strconv.ParseBool(os.Getenv(prefix + "_VERIFY_CLIENT_SSL")) denyHTTP, _ := strconv.ParseBool(os.Getenv(prefix + "_DENY_HTTP")) ignoreAuthorization, _ := strconv.ParseBool(os.Getenv(prefix + "_IGNORE_AUTHORIZATION")) + sslVerifyNone, _ := strconv.ParseBool(os.Getenv(prefix + "_SSL_VERIFY_NONE")) if len(path) > 0 || len(port) > 0 { sd = append( @@ -291,6 +292,7 @@ func (m *serve) getServiceFromEnvVars(prefix string) (proxy.Service, error) { ServicePath: path, ServicePathExclude: servicePathExclude, SrcPort: srcPort, + SslVerifyNone: sslVerifyNone, VerifyClientSsl: verifyClientSsl, }, ) diff --git a/server/server_test.go b/server/server_test.go index 8ebdb9c8..0038907a 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -547,12 +547,12 @@ func (s *ServerTestSuite) Test_GetServiceFromUrl_ReturnsProxyService() { ServiceHeader: map[string]string{"X-Version": "3", "name": "Viktor"}, ServicePath: []string{"/"}, ServicePathExclude: []string{"/excluded-path"}, + SslVerifyNone: true, }}, ServiceDomainAlgo: "hdr_dom", ServiceName: "serviceName", SetReqHeader: []string{"set-header-1", "set-header-2"}, SetResHeader: []string{"set-header-1", "set-header-2"}, - SslVerifyNone: true, TemplateBePath: "templateBePath", TemplateFePath: "templateFePath", TimeoutServer: "timeoutServer", @@ -585,7 +585,7 @@ func (s *ServerTestSuite) Test_GetServiceFromUrl_ReturnsProxyService() { strings.Join(expected.ServiceDest[0].ServiceDomain, ","), strings.Join(expected.ServiceDest[0].RedirectFromDomain, ","), expected.Distribute, - expected.SslVerifyNone, + expected.ServiceDest[0].SslVerifyNone, expected.ServiceDomainAlgo, strings.Join(expected.AddReqHeader, ","), strings.Join(expected.AddResHeader, ","), @@ -600,7 +600,6 @@ func (s *ServerTestSuite) Test_GetServiceFromUrl_ReturnsProxyService() { ) req, _ := http.NewRequest("GET", addr, nil) srv := serve{} - actual := srv.GetServiceFromUrl(req) s.Equal(expected, *actual) @@ -679,7 +678,6 @@ func (s *ServerTestSuite) Test_GetServicesFromEnvVars_ReturnsServices() { ServiceName: "my-ServiceName", SetReqHeader: []string{"set-header-1", "set-header-2"}, SetResHeader: []string{"set-header-1", "set-header-2"}, - SslVerifyNone: true, TemplateBePath: "my-TemplateBePath", TemplateFePath: "my-TemplateFePath", TimeoutServer: "my-TimeoutServer", @@ -688,6 +686,7 @@ func (s *ServerTestSuite) Test_GetServicesFromEnvVars_ReturnsServices() { { HttpsOnly: true, HttpsRedirectCode: "302", + IgnoreAuthorization: true, OutboundHostname: "my-OutboundHostname", Port: "1111", ReqPathSearchReplace: "/something,/else:/this,/that", @@ -702,7 +701,7 @@ func (s *ServerTestSuite) Test_GetServicesFromEnvVars_ReturnsServices() { ServicePathExclude: []string{"some-path", "some-path2"}, VerifyClientSsl: true, DenyHttp: true, - IgnoreAuthorization: true, + SslVerifyNone: true, }, }, } @@ -734,7 +733,7 @@ func (s *ServerTestSuite) Test_GetServicesFromEnvVars_ReturnsServices() { os.Setenv("DFP_SERVICE_SERVICE_DOMAIN_ALGO", service.ServiceDomainAlgo) os.Setenv("DFP_SERVICE_SERVICE_NAME", service.ServiceName) os.Setenv("DFP_SERVICE_SERVICE_PATH_EXCLUDE", strings.Join(service.ServiceDest[0].ServicePathExclude, ",")) - os.Setenv("DFP_SERVICE_SSL_VERIFY_NONE", strconv.FormatBool(service.SslVerifyNone)) + os.Setenv("DFP_SERVICE_SSL_VERIFY_NONE", strconv.FormatBool(service.ServiceDest[0].SslVerifyNone)) os.Setenv("DFP_SERVICE_TEMPLATE_BE_PATH", service.TemplateBePath) os.Setenv("DFP_SERVICE_TEMPLATE_FE_PATH", service.TemplateFePath) os.Setenv("DFP_SERVICE_TIMEOUT_SERVER", service.TimeoutServer)