Skip to content
This repository has been archived by the owner on Aug 5, 2020. It is now read-only.

Commit

Permalink
Merge pull request #179 from phylake/haproxy
Browse files Browse the repository at this point in the history
configurable haproxy timeouts
  • Loading branch information
phylake authored May 17, 2017
2 parents f492bfe + 59783df commit 6abe4a4
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 23 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
`porter` is [semantically versioned](http://semver.org/spec/v2.0.0.html)

### v4.8.0

- HAProxy `timeout client` is configurable
- HAProxy `timeout server` is configurable
- HAProxy `timeout tunnel` is configurable
- HAProxy `timeout http-request` is configurable
- HAProxy `timeout http-keep-alive` is configurable

### v4.7.0

- build porter with Go 1.8.1
Expand Down
6 changes: 6 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ See the [CHANGELOG](CHANGELOG.md) for a complete list of changes.

`porter` is [semantically versioned](http://semver.org/spec/v2.0.0.html)

v4.8
====

Some [HAProxy timeouts](docs/detailed_design/config-reference.md#timeout)
are configurable.

v4.7
====

Expand Down
39 changes: 25 additions & 14 deletions commands/host/haproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ type (
ResHeaderCaptures []conf.HeaderCapture
HTTPS_Redirect bool
HaveELB bool

TimeoutClient uint64
TimeoutServer uint64
TimeoutTunnel uint64
TimeoutHttpRequest uint64
TimeoutHttpKeepAlive uint64
}

hapPort struct {
Expand Down Expand Up @@ -205,20 +211,25 @@ func hotswap(log log15.Logger, environmentStr, regionStr string, hapStdin HAPStd
}

context := haProxyConfigContext{
ServiceName: config.ServiceName,
FrontEndPorts: frontendPorts,
HAPStdin: hapStdin,
StatsUsername: config.HAProxyStatsUsername,
StatsPassword: config.HAProxyStatsPassword,
StatsUri: constants.HAProxyStatsUri,
IpBlacklistPath: ipBlacklistPath,
Log: (environment.HAProxy.Log == nil || *environment.HAProxy.Log == true),
Compression: environment.HAProxy.Compression,
CompressTypes: strings.Join(environment.HAProxy.CompressTypes, " "),
ReqHeaderCaptures: environment.HAProxy.ReqHeaderCaptures,
ResHeaderCaptures: environment.HAProxy.ResHeaderCaptures,
HTTPS_Redirect: environment.HAProxy.SSL.HTTPS_Redirect,
HaveELB: region.HasELB(),
ServiceName: config.ServiceName,
FrontEndPorts: frontendPorts,
HAPStdin: hapStdin,
StatsUsername: config.HAProxyStatsUsername,
StatsPassword: config.HAProxyStatsPassword,
StatsUri: constants.HAProxyStatsUri,
IpBlacklistPath: ipBlacklistPath,
Log: (environment.HAProxy.Log == nil || *environment.HAProxy.Log == true),
Compression: environment.HAProxy.Compression,
CompressTypes: strings.Join(environment.HAProxy.CompressTypes, " "),
ReqHeaderCaptures: environment.HAProxy.ReqHeaderCaptures,
ResHeaderCaptures: environment.HAProxy.ResHeaderCaptures,
HTTPS_Redirect: environment.HAProxy.SSL.HTTPS_Redirect,
HaveELB: region.HasELB(),
TimeoutClient: uint64(environment.HAProxy.Timeout.Client_.Seconds() * 1000),
TimeoutServer: uint64(environment.HAProxy.Timeout.Server_.Seconds() * 1000),
TimeoutTunnel: uint64(environment.HAProxy.Timeout.Tunnel_.Seconds() * 1000),
TimeoutHttpRequest: uint64(environment.HAProxy.Timeout.HttpRequest_.Seconds() * 1000),
TimeoutHttpKeepAlive: uint64(environment.HAProxy.Timeout.HttpKeepAlive_.Seconds() * 1000),
}

if !healthCheckContainers(log, context.HAPStdin) {
Expand Down
42 changes: 40 additions & 2 deletions conf/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"os"
"path/filepath"
"regexp"
"time"

"github.com/adobe-platform/porter/constants"
"github.com/adobe-platform/porter/stdin"
Expand Down Expand Up @@ -65,8 +66,8 @@ type (
Slack Slack `yaml:"slack"`
Hooks map[string][]Hook `yaml:"hooks"`

HAProxyStatsUsername string `yaml:"haproxy_stats_username"`
HAProxyStatsPassword string `yaml:"haproxy_stats_password"`
HAProxyStatsUsername string
HAProxyStatsPassword string
}

Container struct {
Expand Down Expand Up @@ -122,6 +123,20 @@ type (
Compression bool `yaml:"compression"`
CompressTypes []string `yaml:"compress_types"`
SSL SSL `yaml:"ssl"`
Timeout Timeout `yaml:"timeout"`
}

Timeout struct {
Client *string `yaml:"client"`
Client_ time.Duration
Server *string `yaml:"server"`
Server_ time.Duration
Tunnel *string `yaml:"tunnel"`
Tunnel_ time.Duration
HttpRequest *string `yaml:"http_request"`
HttpRequest_ time.Duration
HttpKeepAlive *string `yaml:"http_keep_alive"`
HttpKeepAlive_ time.Duration
}

SSL struct {
Expand Down Expand Up @@ -252,6 +267,29 @@ func (recv *Config) SetDefaults() {
env.HAProxy.SSL.CertDirectory = "/etc/ssl/certs/"
}

if env.HAProxy.Timeout.Client == nil || *env.HAProxy.Timeout.Client == "" {
env.HAProxy.Timeout.Client = new(string)
*env.HAProxy.Timeout.Client = "7s"
}

if env.HAProxy.Timeout.Server == nil || *env.HAProxy.Timeout.Server == "" {
env.HAProxy.Timeout.Server = env.HAProxy.Timeout.Client
}

if env.HAProxy.Timeout.Tunnel == nil || *env.HAProxy.Timeout.Tunnel == "" {
env.HAProxy.Timeout.Tunnel = env.HAProxy.Timeout.Client
}

if env.HAProxy.Timeout.HttpRequest == nil || *env.HAProxy.Timeout.HttpRequest == "" {
env.HAProxy.Timeout.HttpRequest = new(string)
*env.HAProxy.Timeout.HttpRequest = "4s"
}

if env.HAProxy.Timeout.HttpKeepAlive == nil || *env.HAProxy.Timeout.HttpKeepAlive == "" {
env.HAProxy.Timeout.HttpKeepAlive = new(string)
*env.HAProxy.Timeout.HttpKeepAlive = "60s"
}

env.HAProxy.SSL.CertPath = filepath.Join(env.HAProxy.SSL.CertDirectory, "porter.pem")

// this is only for porterd which isn't currently informed of HTTPS_Only
Expand Down
28 changes: 27 additions & 1 deletion conf/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"fmt"
"os"
"strings"
"time"

"github.com/adobe-platform/porter/constants"
)
Expand Down Expand Up @@ -188,7 +189,7 @@ func (recv *Config) ValidateEnvironments() error {

if environment.HAProxy.UsingSSL() {
if environment.HAProxy.SSL.Pem == nil || environment.HAProxy.SSL.Pem.SecretsExecName == "" {
return errors.New("haproxy ssl common_name defined but no pem field with a valid secrets_exec_name was defined")
return errors.New("haproxy ssl pem defined but no pem secrets_exec_name was defined")
}

if environment.HAProxy.SSL.HTTPS_Only {
Expand All @@ -201,6 +202,31 @@ func (recv *Config) ValidateEnvironments() error {
}
}

var err error
if environment.HAProxy.Timeout.Client_, err = time.ParseDuration(*environment.HAProxy.Timeout.Client); err != nil {
return errors.New("ParseDuration(timeout_client) " + err.Error())
}

if environment.HAProxy.Timeout.Server_, err = time.ParseDuration(*environment.HAProxy.Timeout.Server); err != nil {
return errors.New("ParseDuration(timeout_server) " + err.Error())
}

if environment.HAProxy.Timeout.Tunnel_, err = time.ParseDuration(*environment.HAProxy.Timeout.Tunnel); err != nil {
return errors.New("ParseDuration(timeout_tunnel) " + err.Error())
}

if environment.HAProxy.Timeout.HttpRequest_, err = time.ParseDuration(*environment.HAProxy.Timeout.HttpRequest); err != nil {
return errors.New("ParseDuration(timeout_http_request) " + err.Error())
}

if environment.HAProxy.Timeout.HttpKeepAlive_, err = time.ParseDuration(*environment.HAProxy.Timeout.HttpKeepAlive); err != nil {
return errors.New("ParseDuration(timeout_http_keep_alive) " + err.Error())
}

if environment.HAProxy.Timeout.Client_ != environment.HAProxy.Timeout.Server_ {
return errors.New("timeout_client != timeout_server")
}

if environment.Hotswap {

for _, region := range environment.Regions {
Expand Down
36 changes: 36 additions & 0 deletions docs/detailed_design/config-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ For each field the following notation is used
- [log](#log) (==1?)
- [compression](#compression) (==1?)
- [compress_types](#compress_types) (==1?)
- [timeout](#timeout)
- [ssl](#ssl) (==1?)
- [cert_directory](#cert_directory) (==1?)
- [pem](#pem) (==?!)
Expand Down Expand Up @@ -320,6 +321,41 @@ environments:
compress_types: text/plain text/html application/json
```

### timeout

Set HAProxy timeouts.

**Warning: misconfigured timeouts can have an adverse effect on your service and
may make you more vulnurable to slowloris and DoS attacks. Change these at your
own risk**

The values in the following example are also the defaults if unset:

```yaml
environments:
- name: dev
haproxy:
timeout:
client: 7s
server: 7s
tunnel: 7s
http_request: 4s
http_keep_alive: 60s
```

Durations are parsed by https://golang.org/pkg/time/#ParseDuration

`client` must equal `server`

`server` equals `client` if `server` is left unset

`tunnel` equals `client` if `tunnel` is left unset

Disable any timeout by setting the value to `0`

Refer to the [HAProxy docs](https://cbonte.github.io/haproxy-dconv/1.5/configuration.html#4.2-timeout%20client)
for what these timeouts mean

### ssl

SSL support via HAProxy
Expand Down
21 changes: 16 additions & 5 deletions files/haproxy.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ defaults
option redispatch

# Time to connect to backends
# N * 3 + 1 where I chose N=4 based on load testing
# N * 3 + 1 where N=4 was chosen based on load testing
timeout connect 13s

{{ if .HaveELB }}
Expand All @@ -39,10 +39,21 @@ defaults
timeout client 3600s
timeout server 3600s
{{- else }}
timeout http-keep-alive 60s
timeout http-request 7s
timeout client 7s
timeout server 3s
{{- if gt .TimeoutClient 0 }}
timeout client {{ .TimeoutClient }}
{{- end }}
{{- if gt .TimeoutServer 0 }}
timeout server {{ .TimeoutServer }}
{{- end }}
{{- if gt .TimeoutTunnel 0 }}
timeout tunnel {{ .TimeoutTunnel }}
{{- end }}
{{- if gt .TimeoutHttpRequest 0 }}
timeout http-request {{ .TimeoutHttpRequest }}
{{- end }}
{{- if gt .TimeoutHttpKeepAlive 0 }}
timeout http-keep-alive {{ .TimeoutHttpKeepAlive }}
{{- end }}
{{- end }}

{{ if .Compression }}
Expand Down
2 changes: 1 addition & 1 deletion testintegration/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func main() {
http.HandleFunc("/logrotate", TestLogRotate)

fmt.Println("listening on " + port)
http.ListenAndServe(":"+ port, nil)
http.ListenAndServe(":"+port, nil)
}

type Garbage struct {
Expand Down

0 comments on commit 6abe4a4

Please sign in to comment.