Skip to content

Commit

Permalink
add ssl-always-follow-redirect option
Browse files Browse the repository at this point in the history
Controller v0.11 and older had an incorrect behavior compared with
ingress spec, regarding HTTPS configuration and SSL redirect.

v0.12 added ssl-always-add-https option that adds the host in the HTTPS
map when enabled, regardless of the tls attribute configuration. This
was a v0.11 and older behavior.

This update adds the ability to avoid SSL redirect when TLS was added
automatically. This was also a v0.11 and older behavior.
  • Loading branch information
jcmoraisjr committed May 4, 2024
1 parent 10a90d2 commit e5449ab
Show file tree
Hide file tree
Showing 10 changed files with 67 additions and 47 deletions.
14 changes: 10 additions & 4 deletions docs/content/en/docs/configuration/keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ The table below describes all supported configuration keys.
| [`slots-min-free`](#dynamic-scaling) | minimum number of free slots | Backend | `0` |
| [`source-address-intf`](#source-address-intf) | `<intf1>[,<intf2>...]` | Backend | |
| [`ssl-always-add-https`](#ssl-always-add-https) | [true\|false] | Host | `false` |
| [`ssl-always-follow-redirect`](#ssl-always-add-https) | [true\|false] | Host | `true` |
| [`ssl-cipher-suites`](#ssl-ciphers) | colon-separated list | Host | [see description](#ssl-ciphers) |
| [`ssl-cipher-suites-backend`](#ssl-ciphers) | colon-separated list | Backend | [see description](#ssl-ciphers) |
| [`ssl-ciphers`](#ssl-ciphers) | colon-separated list | Host | [see description](#ssl-ciphers) |
Expand Down Expand Up @@ -2488,15 +2489,20 @@ See also:

## SSL always add HTTPS

| Configuration key | Scope | Default | Since |
|------------------------|-------|---------|---------|
| `ssl-always-add-https` | Host | `false` | v0.12.4 |
| Configuration key | Scope | Default | Since |
|------------------------------|-------|---------|---------|
| `ssl-always-add-https` | Host | `false` | v0.12.4 |
| `ssl-always-follow-redirect` | Host | `true` | v0.14.7 |

Every hostname declared on an Ingress resource is added to an internal HTTP map. If at least one Ingress adds the hostname in the `tls` attribute, the hostname is also added to an internal HTTPS map and does ssl offload using the default certificate. A secret name can also be added in the `tls` attribute, overriding the certificate used in the TLS handshake.

`ssl-always-add-https` asks the controller to always add the domain in the internal HTTP and HTTPS maps, even if the `tls` attribute isn't declared. If `false`, a missing `tls` attribute will only declare the domain in the HTTP map and `ssl-redirect` is ignored. If `true`, a missing `tls` attribute adds the domain in the HTTPS map, and the TLS handshake will use the default certificate. If `tls` attribute is used, this configuration is ignored.

The default value is `false` since v0.13 to correctly implement Ingress spec. The default value can be globally changed in the global ConfigMap.
`ssl-always-follow-redirect` configures how the `ssl-redirect` option should be used when the `tls` attribute is missing, but the host is added in the HTTPS map. When `false`, it makes the controller to mimic a v0.11 and older behavior by not redirecting to HTTPS if the ingress does not declare the `tls` attribute. When `true`, SSL redirect will happen if configured, regardless the presence of the `tls` attribute. This option is ignored if `ssl-always-add-https` is false.

The default value for `ssl-always-add-https` is `false` since v0.13 to correctly implement Ingress spec. The default value can be globally changed in the global ConfigMap.

These options are implemented to help teams upgrade from older controller versions without disruptions. It is suggested not to be changed, and if so, it is also suggested to evolve ingress resources to a state that does not depend on it in the mid term.

---

Expand Down
2 changes: 1 addition & 1 deletion pkg/converters/ingress/annotations/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -961,7 +961,7 @@ func (c *updater) buildBackendSSL(d *backData) {
func (c *updater) buildBackendSSLRedirect(d *backData) {
noTLSRedir := utils.Split(d.mapper.Get(ingtypes.GlobalNoTLSRedirectLocations).Value, ",")
for _, path := range d.backend.Paths {
redir := path.Host != nil && path.Host.HasTLS() &&
redir := path.Host != nil && path.Host.UseTLS() &&
d.mapper.GetConfig(path.Link).Get(ingtypes.BackSSLRedirect).Bool()
if redir {
for _, noredir := range noTLSRedir {
Expand Down
1 change: 1 addition & 0 deletions pkg/converters/ingress/annotations/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ func (c *updater) UpdateHostConfig(host *hatypes.Host, mapper *Mapper) {
host.Alias.AliasName = mapper.Get(ingtypes.HostServerAlias).Value
host.Alias.AliasRegex = mapper.Get(ingtypes.HostServerAliasRegex).Value
host.TLS.UseDefaultCrt = mapper.Get(ingtypes.HostSSLAlwaysAddHTTPS).Bool()
host.TLS.FollowRedirect = mapper.Get(ingtypes.HostSSLAlwaysFollowRedirect).Bool()
host.VarNamespace = mapper.Get(ingtypes.HostVarNamespace).Bool()
c.buildHostAuthExternal(data)
c.buildHostAuthTLS(data)
Expand Down
2 changes: 1 addition & 1 deletion pkg/converters/ingress/annotations/updater_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ const testingHostname = "host.local"

type hostResolver struct{}

func (h *hostResolver) HasTLS() bool {
func (h *hostResolver) UseTLS() bool {
return true
}

Expand Down
13 changes: 7 additions & 6 deletions pkg/converters/ingress/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ func createDefaults() map[string]string {
return map[string]string{
types.TCPTCPServiceLogFormat: "default",
//
types.HostAuthTLSStrict: "true",
types.HostSSLAlwaysAddHTTPS: "false",
types.HostSSLCiphers: defaultSSLCiphers,
types.HostSSLCipherSuites: defaultSSLCipherSuites,
types.HostSSLOptionsHost: "",
types.HostTLSALPN: "h2,http/1.1",
types.HostAuthTLSStrict: "true",
types.HostSSLAlwaysAddHTTPS: "false",
types.HostSSLAlwaysFollowRedirect: "true",
types.HostSSLCiphers: defaultSSLCiphers,
types.HostSSLCipherSuites: defaultSSLCipherSuites,
types.HostSSLOptionsHost: "",
types.HostTLSALPN: "h2,http/1.1",
//
types.BackAuthExternalPlacement: "backend",
types.BackAuthHeadersFail: "*",
Expand Down
39 changes: 20 additions & 19 deletions pkg/converters/ingress/types/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,26 @@ var (

// Host Annotations
const (
HostAcmePreferredChain = "acme-preferred-chain"
HostAppRoot = "app-root"
HostAuthTLSErrorPage = "auth-tls-error-page"
HostAuthTLSSecret = "auth-tls-secret"
HostAuthTLSStrict = "auth-tls-strict"
HostAuthTLSVerifyClient = "auth-tls-verify-client"
HostCertSigner = "cert-signer"
HostRedirectFrom = "redirect-from"
HostRedirectFromRegex = "redirect-from-regex"
HostServerAlias = "server-alias"
HostServerAliasRegex = "server-alias-regex"
HostSSLAlwaysAddHTTPS = "ssl-always-add-https"
HostSSLCiphers = "ssl-ciphers"
HostSSLCipherSuites = "ssl-cipher-suites"
HostSSLOptionsHost = "ssl-options-host"
HostSSLPassthrough = "ssl-passthrough"
HostSSLPassthroughHTTPPort = "ssl-passthrough-http-port"
HostTLSALPN = "tls-alpn"
HostVarNamespace = "var-namespace"
HostAcmePreferredChain = "acme-preferred-chain"
HostAppRoot = "app-root"
HostAuthTLSErrorPage = "auth-tls-error-page"
HostAuthTLSSecret = "auth-tls-secret"
HostAuthTLSStrict = "auth-tls-strict"
HostAuthTLSVerifyClient = "auth-tls-verify-client"
HostCertSigner = "cert-signer"
HostRedirectFrom = "redirect-from"
HostRedirectFromRegex = "redirect-from-regex"
HostServerAlias = "server-alias"
HostServerAliasRegex = "server-alias-regex"
HostSSLAlwaysAddHTTPS = "ssl-always-add-https"
HostSSLAlwaysFollowRedirect = "ssl-always-follow-redirect"
HostSSLCiphers = "ssl-ciphers"
HostSSLCipherSuites = "ssl-cipher-suites"
HostSSLOptionsHost = "ssl-options-host"
HostSSLPassthrough = "ssl-passthrough"
HostSSLPassthroughHTTPPort = "ssl-passthrough-http-port"
HostTLSALPN = "tls-alpn"
HostVarNamespace = "var-namespace"
)

var (
Expand Down
16 changes: 10 additions & 6 deletions pkg/haproxy/types/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,9 @@ func (h *Host) AddRedirect(path string, match MatchType, redirTo string) {
}

type hostResolver struct {
useDefaultCrt *bool
crtFilename *string
useDefaultCrt *bool
followRedirect *bool
crtFilename *string
}

func (h *Host) addPath(path string, match MatchType, backend *Backend, redirTo string) *HostPath {
Expand All @@ -281,8 +282,9 @@ func (h *Host) addLink(backend *Backend, link *PathLink, redirTo string) *HostPa
}
bpath := backend.AddBackendPath(link)
bpath.Host = &hostResolver{
useDefaultCrt: &h.TLS.UseDefaultCrt,
crtFilename: &h.TLS.TLSFilename,
useDefaultCrt: &h.TLS.UseDefaultCrt,
followRedirect: &h.TLS.FollowRedirect,
crtFilename: &h.TLS.TLSFilename,
}
} else if redirTo == "" {
hback = HostBackend{ID: "_error404"}
Expand Down Expand Up @@ -327,8 +329,10 @@ func (h *Host) HasTLS() bool {
return h.TLS.UseDefaultCrt || h.TLS.TLSHash != ""
}

func (h *hostResolver) HasTLS() bool {
return *h.useDefaultCrt || *h.crtFilename != ""
func (h *hostResolver) UseTLS() bool {
// hasTLS - whether the host should be added or not in the HTTPS map.
// useTLS - whether the proxy should be used or not the `hasTLS` info to configure a ssl-redirect.
return (*h.useDefaultCrt && *h.followRedirect) || *h.crtFilename != ""
}

// HasTLSAuth ...
Expand Down
7 changes: 4 additions & 3 deletions pkg/haproxy/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -570,8 +570,9 @@ type HostRedirectConfig struct {
// HostTLSConfig ...
type HostTLSConfig struct {
TLSConfig
CAErrorPage string
UseDefaultCrt bool
CAErrorPage string
UseDefaultCrt bool
FollowRedirect bool
}

// EndpointNaming ...
Expand Down Expand Up @@ -691,7 +692,7 @@ type BackendPathItem struct {

// HostResolver ...
type HostResolver interface {
HasTLS() bool
UseTLS() bool
}

// BackendPath ...
Expand Down
14 changes: 10 additions & 4 deletions tests/framework/framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ const (
PublishSvcName = "default/publish"
PublishAddress = "10.0.1.1"
PublishHostname = "ingress.local"

TestPortHTTP = 28080
TestPortHTTPS = 28443
TestPortStat = 21936
TestPortTCPService = 25432
)

func NewFramework(ctx context.Context, t *testing.T, o ...options.Framework) *framework {
Expand Down Expand Up @@ -164,8 +169,9 @@ func (f *framework) StartController(ctx context.Context, t *testing.T) {
global.Namespace = "default"
global.Name = "ingress-controller"
global.Data = map[string]string{
"http-port": "18080",
"https-port": "18443",
"http-port": strconv.Itoa(TestPortHTTP),
"https-port": strconv.Itoa(TestPortHTTPS),
"stats-port": strconv.Itoa(TestPortStat),
"max-connections": "20",
}
err = f.cli.Create(ctx, &global)
Expand Down Expand Up @@ -218,9 +224,9 @@ func (f *framework) Request(ctx context.Context, t *testing.T, method, host, pat
t.Logf("request method=%s host=%s path=%s\n", method, host, path)
opt := options.ParseRequestOptions(o...)

url := "http://127.0.0.1:18080"
url := fmt.Sprintf("http://127.0.0.1:%d", TestPortHTTP)
if opt.HTTPS {
url = "https://127.0.0.1:18443"
url = fmt.Sprintf("https://127.0.0.1:%d", TestPortHTTPS)
}
req, err := http.NewRequestWithContext(ctx, method, url, nil)
require.NoError(t, err)
Expand Down
6 changes: 3 additions & 3 deletions tests/integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,12 +286,12 @@ func TestIntegrationGateway(t *testing.T) {

t.Run("expose TCPRoute", func(t *testing.T) {
t.Parallel()
gw := f.CreateGatewayV1(ctx, t, gc, options.Listener("pgserver", "TCP", 15432))
gw := f.CreateGatewayV1(ctx, t, gc, options.Listener("pgserver", "TCP", framework.TestPortTCPService))
svc := f.CreateService(ctx, t, tcpServerPort)
_ = f.CreateTCPRouteA2(ctx, t, gw, svc)
res1 := f.TCPRequest(ctx, t, 15432, "ping")
res1 := f.TCPRequest(ctx, t, framework.TestPortTCPService, "ping")
assert.Equal(t, "ping", res1)
res2 := f.TCPRequest(ctx, t, 15432, "reply")
res2 := f.TCPRequest(ctx, t, framework.TestPortTCPService, "reply")
assert.Equal(t, "reply", res2)
})
})
Expand Down

0 comments on commit e5449ab

Please sign in to comment.