Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ssl-always-follow-redirect option #1118

Merged
merged 1 commit into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -977,7 +977,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
20 changes: 14 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,14 @@ 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 {
// whether the ingress resource has the `tls:` entry for the host
hasTLSEntry := *h.crtFilename != ""

// whether the user has globally or locally configured auto TLS for the host
autoTLSEnabled := *h.useDefaultCrt && *h.followRedirect

return hasTLSEntry || autoTLSEnabled
}

// 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 @@ -572,8 +572,9 @@ type HostRedirectConfig struct {
// HostTLSConfig ...
type HostTLSConfig struct {
TLSConfig
CAErrorPage string
UseDefaultCrt bool
CAErrorPage string
UseDefaultCrt bool
FollowRedirect bool
}

// EndpointNaming ...
Expand Down Expand Up @@ -693,7 +694,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 @@ -304,12 +304,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
Loading