2016-11-10 22:56:29 +00:00
|
|
|
/*
|
|
|
|
Copyright 2015 The Kubernetes Authors.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package template
|
|
|
|
|
|
|
|
import (
|
2017-08-25 02:24:32 +00:00
|
|
|
"fmt"
|
|
|
|
"net"
|
2016-11-10 22:56:29 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2018-06-04 01:10:41 +00:00
|
|
|
"time"
|
2016-11-10 22:56:29 +00:00
|
|
|
|
2018-12-05 16:27:55 +00:00
|
|
|
"k8s.io/klog"
|
2017-09-17 18:42:31 +00:00
|
|
|
|
2018-06-21 14:50:57 +00:00
|
|
|
"github.com/mitchellh/hashstructure"
|
2016-11-10 22:56:29 +00:00
|
|
|
"github.com/mitchellh/mapstructure"
|
|
|
|
|
2018-01-19 18:44:31 +00:00
|
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
2018-11-27 16:12:17 +00:00
|
|
|
"k8s.io/ingress-nginx/internal/ingress/annotations/authreq"
|
2020-02-05 02:06:07 +00:00
|
|
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
2017-11-07 22:02:12 +00:00
|
|
|
"k8s.io/ingress-nginx/internal/ingress/controller/config"
|
|
|
|
ing_net "k8s.io/ingress-nginx/internal/net"
|
2018-08-25 21:08:12 +00:00
|
|
|
"k8s.io/ingress-nginx/internal/runtime"
|
2016-11-10 22:56:29 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2018-11-27 16:12:17 +00:00
|
|
|
customHTTPErrors = "custom-http-errors"
|
|
|
|
skipAccessLogUrls = "skip-access-log-urls"
|
|
|
|
whitelistSourceRange = "whitelist-source-range"
|
|
|
|
proxyRealIPCIDR = "proxy-real-ip-cidr"
|
|
|
|
bindAddress = "bind-address"
|
|
|
|
httpRedirectCode = "http-redirect-code"
|
|
|
|
blockCIDRs = "block-cidrs"
|
|
|
|
blockUserAgents = "block-user-agents"
|
|
|
|
blockReferers = "block-referers"
|
|
|
|
proxyStreamResponses = "proxy-stream-responses"
|
|
|
|
hideHeaders = "hide-headers"
|
|
|
|
nginxStatusIpv4Whitelist = "nginx-status-ipv4-whitelist"
|
|
|
|
nginxStatusIpv6Whitelist = "nginx-status-ipv6-whitelist"
|
|
|
|
proxyHeaderTimeout = "proxy-protocol-header-timeout"
|
|
|
|
workerProcesses = "worker-processes"
|
|
|
|
globalAuthURL = "global-auth-url"
|
|
|
|
globalAuthMethod = "global-auth-method"
|
|
|
|
globalAuthSignin = "global-auth-signin"
|
|
|
|
globalAuthResponseHeaders = "global-auth-response-headers"
|
|
|
|
globalAuthRequestRedirect = "global-auth-request-redirect"
|
|
|
|
globalAuthSnippet = "global-auth-snippet"
|
2019-07-07 16:34:56 +00:00
|
|
|
globalAuthCacheKey = "global-auth-cache-key"
|
|
|
|
globalAuthCacheDuration = "global-auth-cache-duration"
|
2019-08-14 23:23:20 +00:00
|
|
|
luaSharedDictsKey = "lua-shared-dicts"
|
2017-11-30 14:59:39 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2019-08-14 23:23:20 +00:00
|
|
|
validRedirectCodes = sets.NewInt([]int{301, 302, 307, 308}...)
|
|
|
|
defaultLuaSharedDicts = map[string]int{
|
2019-08-15 15:04:32 +00:00
|
|
|
"configuration_data": 20,
|
|
|
|
"certificate_data": 20,
|
|
|
|
"balancer_ewma": 10,
|
|
|
|
"balancer_ewma_last_touched_at": 10,
|
|
|
|
"balancer_ewma_locks": 1,
|
2019-08-26 14:58:44 +00:00
|
|
|
"certificate_servers": 5,
|
2019-08-14 23:23:20 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
maxAllowedLuaDictSize = 200
|
|
|
|
maxNumberOfLuaDicts = 100
|
2016-11-10 22:56:29 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// ReadConfig obtains the configuration defined by the user merged with the defaults.
|
2017-01-20 22:37:59 +00:00
|
|
|
func ReadConfig(src map[string]string) config.Configuration {
|
|
|
|
conf := map[string]string{}
|
2017-11-05 01:18:28 +00:00
|
|
|
// we need to copy the configmap data because the content is altered
|
|
|
|
for k, v := range src {
|
|
|
|
conf[k] = v
|
2017-01-20 22:37:59 +00:00
|
|
|
}
|
|
|
|
|
2018-03-28 12:27:34 +00:00
|
|
|
to := config.NewDefault()
|
2016-11-16 18:24:26 +00:00
|
|
|
errors := make([]int, 0)
|
|
|
|
skipUrls := make([]string, 0)
|
2018-03-16 16:32:45 +00:00
|
|
|
whiteList := make([]string, 0)
|
|
|
|
proxyList := make([]string, 0)
|
|
|
|
hideHeadersList := make([]string, 0)
|
2018-01-18 18:37:22 +00:00
|
|
|
|
2017-08-25 02:24:32 +00:00
|
|
|
bindAddressIpv4List := make([]string, 0)
|
|
|
|
bindAddressIpv6List := make([]string, 0)
|
2016-11-10 22:56:29 +00:00
|
|
|
|
2018-08-27 13:50:04 +00:00
|
|
|
blockCIDRList := make([]string, 0)
|
|
|
|
blockUserAgentList := make([]string, 0)
|
|
|
|
blockRefererList := make([]string, 0)
|
2018-11-27 16:12:17 +00:00
|
|
|
responseHeaders := make([]string, 0)
|
2019-08-14 23:23:20 +00:00
|
|
|
luaSharedDicts := make(map[string]int)
|
2019-08-06 11:21:59 +00:00
|
|
|
|
|
|
|
//parse lua shared dict values
|
2019-08-14 23:23:20 +00:00
|
|
|
if val, ok := conf[luaSharedDictsKey]; ok {
|
|
|
|
delete(conf, luaSharedDictsKey)
|
2019-08-06 11:21:59 +00:00
|
|
|
lsd := strings.Split(val, ",")
|
|
|
|
for _, v := range lsd {
|
|
|
|
v = strings.Replace(v, " ", "", -1)
|
|
|
|
results := strings.SplitN(v, ":", 2)
|
2019-08-14 23:23:20 +00:00
|
|
|
dictName := results[0]
|
|
|
|
size, err := strconv.Atoi(results[1])
|
2019-08-06 11:21:59 +00:00
|
|
|
if err != nil {
|
2019-08-14 23:23:20 +00:00
|
|
|
klog.Errorf("Ignoring non integer value %v for Lua dictionary %v: %v.", results[1], dictName, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if size > maxAllowedLuaDictSize {
|
|
|
|
klog.Errorf("Ignoring %v for Lua dictionary %v: maximum size is %v.", size, dictName, maxAllowedLuaDictSize)
|
|
|
|
continue
|
2019-08-06 11:21:59 +00:00
|
|
|
}
|
2019-08-14 23:23:20 +00:00
|
|
|
if len(luaSharedDicts)+1 > maxNumberOfLuaDicts {
|
|
|
|
klog.Errorf("Ignoring %v for Lua dictionary %v: can not configure more than %v dictionaries.",
|
|
|
|
size, dictName, maxNumberOfLuaDicts)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
luaSharedDicts[dictName] = size
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// set default Lua shared dicts
|
|
|
|
for k, v := range defaultLuaSharedDicts {
|
|
|
|
if _, ok := luaSharedDicts[k]; !ok {
|
|
|
|
luaSharedDicts[k] = v
|
2019-08-06 11:21:59 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-20 22:37:59 +00:00
|
|
|
if val, ok := conf[customHTTPErrors]; ok {
|
|
|
|
delete(conf, customHTTPErrors)
|
2016-11-10 22:56:29 +00:00
|
|
|
for _, i := range strings.Split(val, ",") {
|
|
|
|
j, err := strconv.Atoi(i)
|
|
|
|
if err != nil {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Warningf("%v is not a valid http code: %v", i, err)
|
2016-11-10 22:56:29 +00:00
|
|
|
} else {
|
|
|
|
errors = append(errors, j)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-18 18:37:22 +00:00
|
|
|
if val, ok := conf[hideHeaders]; ok {
|
|
|
|
delete(conf, hideHeaders)
|
2018-03-16 16:32:45 +00:00
|
|
|
hideHeadersList = strings.Split(val, ",")
|
2018-01-18 18:37:22 +00:00
|
|
|
}
|
2017-01-20 22:37:59 +00:00
|
|
|
if val, ok := conf[skipAccessLogUrls]; ok {
|
|
|
|
delete(conf, skipAccessLogUrls)
|
2016-11-10 22:56:29 +00:00
|
|
|
skipUrls = strings.Split(val, ",")
|
|
|
|
}
|
2017-01-20 22:37:59 +00:00
|
|
|
if val, ok := conf[whitelistSourceRange]; ok {
|
|
|
|
delete(conf, whitelistSourceRange)
|
2018-03-16 16:32:45 +00:00
|
|
|
whiteList = append(whiteList, strings.Split(val, ",")...)
|
2016-11-10 22:56:29 +00:00
|
|
|
}
|
2017-06-28 14:53:08 +00:00
|
|
|
if val, ok := conf[proxyRealIPCIDR]; ok {
|
|
|
|
delete(conf, proxyRealIPCIDR)
|
2018-03-16 16:32:45 +00:00
|
|
|
proxyList = append(proxyList, strings.Split(val, ",")...)
|
2017-06-28 14:53:08 +00:00
|
|
|
} else {
|
2018-03-16 16:32:45 +00:00
|
|
|
proxyList = append(proxyList, "0.0.0.0/0")
|
2017-07-06 20:17:46 +00:00
|
|
|
}
|
2017-08-25 02:24:32 +00:00
|
|
|
if val, ok := conf[bindAddress]; ok {
|
|
|
|
delete(conf, bindAddress)
|
|
|
|
for _, i := range strings.Split(val, ",") {
|
|
|
|
ns := net.ParseIP(i)
|
|
|
|
if ns != nil {
|
|
|
|
if ing_net.IsIPV6(ns) {
|
|
|
|
bindAddressIpv6List = append(bindAddressIpv6List, fmt.Sprintf("[%v]", ns))
|
|
|
|
} else {
|
|
|
|
bindAddressIpv4List = append(bindAddressIpv4List, fmt.Sprintf("%v", ns))
|
|
|
|
}
|
|
|
|
} else {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Warningf("%v is not a valid textual representation of an IP address", i)
|
2017-08-25 02:24:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-11-10 22:56:29 +00:00
|
|
|
|
2018-08-27 13:50:04 +00:00
|
|
|
if val, ok := conf[blockCIDRs]; ok {
|
|
|
|
delete(conf, blockCIDRs)
|
|
|
|
blockCIDRList = strings.Split(val, ",")
|
|
|
|
}
|
|
|
|
if val, ok := conf[blockUserAgents]; ok {
|
|
|
|
delete(conf, blockUserAgents)
|
|
|
|
blockUserAgentList = strings.Split(val, ",")
|
|
|
|
}
|
|
|
|
if val, ok := conf[blockReferers]; ok {
|
|
|
|
delete(conf, blockReferers)
|
|
|
|
blockRefererList = strings.Split(val, ",")
|
|
|
|
}
|
|
|
|
|
2017-11-30 14:59:39 +00:00
|
|
|
if val, ok := conf[httpRedirectCode]; ok {
|
|
|
|
delete(conf, httpRedirectCode)
|
|
|
|
j, err := strconv.Atoi(val)
|
|
|
|
if err != nil {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Warningf("%v is not a valid HTTP code: %v", val, err)
|
2017-11-30 14:59:39 +00:00
|
|
|
} else {
|
2018-01-19 18:44:31 +00:00
|
|
|
if validRedirectCodes.Has(j) {
|
2018-03-28 12:27:34 +00:00
|
|
|
to.HTTPRedirectCode = j
|
2017-11-30 14:59:39 +00:00
|
|
|
} else {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Warningf("The code %v is not a valid as HTTP redirect code. Using the default.", val)
|
2017-11-30 14:59:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-27 16:12:17 +00:00
|
|
|
// Verify that the configured global external authorization URL is parsable as URL. if not, set the default value
|
|
|
|
if val, ok := conf[globalAuthURL]; ok {
|
|
|
|
delete(conf, globalAuthURL)
|
|
|
|
|
2020-02-05 02:06:07 +00:00
|
|
|
authURL, message := parser.StringToURL(val)
|
2018-11-27 16:12:17 +00:00
|
|
|
if authURL == nil {
|
|
|
|
klog.Warningf("Global auth location denied - %v.", message)
|
|
|
|
} else {
|
|
|
|
to.GlobalExternalAuth.URL = val
|
|
|
|
to.GlobalExternalAuth.Host = authURL.Hostname()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that the configured global external authorization method is a valid HTTP method. if not, set the default value
|
|
|
|
if val, ok := conf[globalAuthMethod]; ok {
|
|
|
|
delete(conf, globalAuthMethod)
|
|
|
|
|
|
|
|
if len(val) != 0 && !authreq.ValidMethod(val) {
|
|
|
|
klog.Warningf("Global auth location denied - %v.", "invalid HTTP method")
|
|
|
|
} else {
|
|
|
|
to.GlobalExternalAuth.Method = val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that the configured global external authorization error page is set and valid. if not, set the default value
|
|
|
|
if val, ok := conf[globalAuthSignin]; ok {
|
|
|
|
delete(conf, globalAuthSignin)
|
|
|
|
|
2020-02-05 02:06:07 +00:00
|
|
|
signinURL, _ := parser.StringToURL(val)
|
2018-11-27 16:12:17 +00:00
|
|
|
if signinURL == nil {
|
|
|
|
klog.Warningf("Global auth location denied - %v.", "global-auth-signin setting is undefined and will not be set")
|
|
|
|
} else {
|
|
|
|
to.GlobalExternalAuth.SigninURL = val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that the configured global external authorization response headers are valid. if not, set the default value
|
|
|
|
if val, ok := conf[globalAuthResponseHeaders]; ok {
|
|
|
|
delete(conf, globalAuthResponseHeaders)
|
|
|
|
|
|
|
|
if len(val) != 0 {
|
|
|
|
harr := strings.Split(val, ",")
|
|
|
|
for _, header := range harr {
|
|
|
|
header = strings.TrimSpace(header)
|
|
|
|
if len(header) > 0 {
|
|
|
|
if !authreq.ValidHeader(header) {
|
|
|
|
klog.Warningf("Global auth location denied - %v.", "invalid headers list")
|
|
|
|
} else {
|
|
|
|
responseHeaders = append(responseHeaders, header)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
to.GlobalExternalAuth.ResponseHeaders = responseHeaders
|
|
|
|
}
|
|
|
|
|
|
|
|
if val, ok := conf[globalAuthRequestRedirect]; ok {
|
|
|
|
delete(conf, globalAuthRequestRedirect)
|
|
|
|
|
|
|
|
to.GlobalExternalAuth.RequestRedirect = val
|
|
|
|
}
|
|
|
|
|
|
|
|
if val, ok := conf[globalAuthSnippet]; ok {
|
|
|
|
delete(conf, globalAuthSnippet)
|
|
|
|
|
|
|
|
to.GlobalExternalAuth.AuthSnippet = val
|
|
|
|
}
|
|
|
|
|
2019-07-07 16:34:56 +00:00
|
|
|
if val, ok := conf[globalAuthCacheKey]; ok {
|
|
|
|
delete(conf, globalAuthCacheKey)
|
|
|
|
|
|
|
|
to.GlobalExternalAuth.AuthCacheKey = val
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that the configured global external authorization cache duration is valid
|
|
|
|
if val, ok := conf[globalAuthCacheDuration]; ok {
|
|
|
|
delete(conf, globalAuthCacheDuration)
|
|
|
|
|
|
|
|
cacheDurations, err := authreq.ParseStringToCacheDurations(val)
|
|
|
|
if err != nil {
|
|
|
|
klog.Warningf("Global auth location denied - %s", err)
|
|
|
|
}
|
|
|
|
to.GlobalExternalAuth.AuthCacheDuration = cacheDurations
|
|
|
|
}
|
|
|
|
|
2018-06-04 01:10:41 +00:00
|
|
|
// Verify that the configured timeout is parsable as a duration. if not, set the default value
|
|
|
|
if val, ok := conf[proxyHeaderTimeout]; ok {
|
|
|
|
delete(conf, proxyHeaderTimeout)
|
|
|
|
duration, err := time.ParseDuration(val)
|
|
|
|
if err != nil {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Warningf("proxy-protocol-header-timeout of %v encountered an error while being parsed %v. Switching to use default value instead.", val, err)
|
2018-06-04 01:10:41 +00:00
|
|
|
} else {
|
|
|
|
to.ProxyProtocolHeaderTimeout = duration
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-30 20:53:23 +00:00
|
|
|
streamResponses := 1
|
|
|
|
if val, ok := conf[proxyStreamResponses]; ok {
|
|
|
|
delete(conf, proxyStreamResponses)
|
|
|
|
j, err := strconv.Atoi(val)
|
|
|
|
if err != nil {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Warningf("%v is not a valid number: %v", val, err)
|
2017-11-30 20:53:23 +00:00
|
|
|
} else {
|
|
|
|
streamResponses = j
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-06 09:59:03 +00:00
|
|
|
// Nginx Status whitelist
|
2018-03-28 12:27:34 +00:00
|
|
|
if val, ok := conf[nginxStatusIpv4Whitelist]; ok {
|
|
|
|
whitelist := make([]string, 0)
|
|
|
|
whitelist = append(whitelist, strings.Split(val, ",")...)
|
|
|
|
to.NginxStatusIpv4Whitelist = whitelist
|
|
|
|
|
|
|
|
delete(conf, nginxStatusIpv4Whitelist)
|
|
|
|
}
|
|
|
|
if val, ok := conf[nginxStatusIpv6Whitelist]; ok {
|
|
|
|
whitelist := make([]string, 0)
|
|
|
|
whitelist = append(whitelist, strings.Split(val, ",")...)
|
|
|
|
to.NginxStatusIpv6Whitelist = whitelist
|
|
|
|
|
|
|
|
delete(conf, nginxStatusIpv6Whitelist)
|
|
|
|
}
|
|
|
|
|
2018-08-25 21:08:12 +00:00
|
|
|
if val, ok := conf[workerProcesses]; ok {
|
|
|
|
to.WorkerProcesses = val
|
|
|
|
|
|
|
|
if val == "auto" {
|
|
|
|
to.WorkerProcesses = strconv.Itoa(runtime.NumCPU())
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(conf, workerProcesses)
|
|
|
|
}
|
|
|
|
|
2016-12-28 13:08:02 +00:00
|
|
|
to.CustomHTTPErrors = filterErrors(errors)
|
|
|
|
to.SkipAccessLogURLs = skipUrls
|
2018-03-16 16:32:45 +00:00
|
|
|
to.WhitelistSourceRange = whiteList
|
|
|
|
to.ProxyRealIPCIDR = proxyList
|
2017-08-25 02:24:32 +00:00
|
|
|
to.BindAddressIpv4 = bindAddressIpv4List
|
|
|
|
to.BindAddressIpv6 = bindAddressIpv6List
|
2018-08-27 13:50:04 +00:00
|
|
|
to.BlockCIDRs = blockCIDRList
|
|
|
|
to.BlockUserAgents = blockUserAgentList
|
|
|
|
to.BlockReferers = blockRefererList
|
2018-03-16 16:32:45 +00:00
|
|
|
to.HideHeaders = hideHeadersList
|
2017-11-30 20:53:23 +00:00
|
|
|
to.ProxyStreamResponses = streamResponses
|
2018-02-02 19:53:28 +00:00
|
|
|
to.DisableIpv6DNS = !ing_net.IsIPv6Enabled()
|
2019-08-14 23:23:20 +00:00
|
|
|
to.LuaSharedDicts = luaSharedDicts
|
2016-11-10 22:56:29 +00:00
|
|
|
|
2016-12-28 13:08:02 +00:00
|
|
|
config := &mapstructure.DecoderConfig{
|
|
|
|
Metadata: nil,
|
2016-11-10 22:56:29 +00:00
|
|
|
WeaklyTypedInput: true,
|
2016-12-28 13:08:02 +00:00
|
|
|
Result: &to,
|
|
|
|
TagName: "json",
|
|
|
|
}
|
2016-11-10 22:56:29 +00:00
|
|
|
|
2016-12-28 13:08:02 +00:00
|
|
|
decoder, err := mapstructure.NewDecoder(config)
|
2016-11-10 22:56:29 +00:00
|
|
|
if err != nil {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Warningf("unexpected error merging defaults: %v", err)
|
2016-11-10 22:56:29 +00:00
|
|
|
}
|
2017-01-20 22:37:59 +00:00
|
|
|
err = decoder.Decode(conf)
|
2016-12-22 03:00:27 +00:00
|
|
|
if err != nil {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Warningf("unexpected error merging defaults: %v", err)
|
2016-12-22 03:00:27 +00:00
|
|
|
}
|
|
|
|
|
2018-06-21 14:50:57 +00:00
|
|
|
hash, err := hashstructure.Hash(to, &hashstructure.HashOptions{
|
|
|
|
TagName: "json",
|
|
|
|
})
|
|
|
|
if err != nil {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Warningf("unexpected error obtaining hash: %v", err)
|
2018-06-21 14:50:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
to.Checksum = fmt.Sprintf("%v", hash)
|
|
|
|
|
2016-11-10 22:56:29 +00:00
|
|
|
return to
|
|
|
|
}
|
|
|
|
|
|
|
|
func filterErrors(codes []int) []int {
|
|
|
|
var fa []int
|
|
|
|
for _, code := range codes {
|
|
|
|
if code > 299 && code < 600 {
|
|
|
|
fa = append(fa, code)
|
|
|
|
} else {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Warningf("error code %v is not valid for custom error pages", code)
|
2016-11-10 22:56:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return fa
|
|
|
|
}
|