Skip to content

Commit 1ea670f

Browse files
committed
MINOR: add client-strict-sni annotation
When enbaled HAProxy will only accept TLS client connections where the provided SNI matchs an existing certificate. Otherwise the default certificate will be provided when the provided SNI does not match.
1 parent ca0d9e2 commit 1ea670f

File tree

6 files changed

+56
-15
lines changed

6 files changed

+56
-15
lines changed

controller/handler/https.go

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,15 @@ import (
3131
)
3232

3333
type HTTPS struct {
34-
Enabled bool
35-
IPv4 bool
36-
IPv6 bool
37-
Port int64
38-
AddrIPv4 string
39-
AddrIPv6 string
40-
CertDir string
41-
Alpn string
34+
Enabled bool
35+
IPv4 bool
36+
IPv6 bool
37+
strictSNI bool
38+
Port int64
39+
AddrIPv4 string
40+
AddrIPv6 string
41+
CertDir string
42+
alpn string
4243
}
4344

4445
func (h HTTPS) bindList(passhthrough bool) (binds []models.Bind) {
@@ -147,12 +148,15 @@ func (h HTTPS) Update(k store.K8s, cfg *config.ControllerCfg, api api.HAProxyCli
147148
}
148149

149150
// Fetch tls-alpn value for when SSL offloading is enabled
150-
h.Alpn = annotations.String("tls-alpn", k.ConfigMaps.Main.Annotations)
151+
h.alpn = annotations.String("tls-alpn", k.ConfigMaps.Main.Annotations)
152+
153+
h.strictSNI, err = annotations.Bool("client-strict-sni", k.ConfigMaps.Main.Annotations)
154+
logger.Error(err)
151155

152156
// ssl-offload
153157
if cfg.Certificates.FrontendCertsEnabled() {
154158
if !cfg.HTTPS {
155-
logger.Panic(api.FrontendEnableSSLOffload(cfg.FrontHTTPS, h.CertDir, h.Alpn))
159+
logger.Panic(api.FrontendEnableSSLOffload(cfg.FrontHTTPS, h.CertDir, h.alpn, h.strictSNI))
156160
cfg.HTTPS = true
157161
reload = true
158162
logger.Debug("SSLOffload enabeld, reload required")
@@ -253,7 +257,7 @@ func (h HTTPS) toggleSSLPassthrough(passthrough bool, cfg *config.ControllerCfg,
253257
}
254258
}
255259
if cfg.HTTPS {
256-
logger.Panic(api.FrontendEnableSSLOffload(cfg.FrontHTTPS, h.CertDir, h.Alpn))
260+
logger.Panic(api.FrontendEnableSSLOffload(cfg.FrontHTTPS, h.CertDir, h.alpn, h.strictSNI))
257261
}
258262
return nil
259263
}

controller/handler/tcp-services.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ func (t TCPServices) createTCPFrontend(api api.HAProxyClient, frontendName, bind
142142
}))
143143
}
144144
if sslOffload {
145-
errors.Add(api.FrontendEnableSSLOffload(frontend.Name, t.CertDir, ""))
145+
errors.Add(api.FrontendEnableSSLOffload(frontend.Name, t.CertDir, "", false))
146146
}
147147
if errors.Result() != nil {
148148
err = fmt.Errorf("error configuring tcp frontend: %w", err)
@@ -159,7 +159,7 @@ func (t TCPServices) updateTCPFrontend(k store.K8s, cfg *config.ControllerCfg, a
159159
return
160160
}
161161
if !binds[0].Ssl && p.sslOffload {
162-
err = api.FrontendEnableSSLOffload(frontend.Name, t.CertDir, "")
162+
err = api.FrontendEnableSSLOffload(frontend.Name, t.CertDir, "", false)
163163
if err != nil {
164164
err = fmt.Errorf("failed to enable SSL offload: %w", err)
165165
return

controller/haproxy/api/api.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type HAProxyClient interface {
3737
FrontendsGet() (models.Frontends, error)
3838
FrontendGet(frontendName string) (models.Frontend, error)
3939
FrontendEdit(frontend models.Frontend) error
40-
FrontendEnableSSLOffload(frontendName string, certDir string, alpn string) (err error)
40+
FrontendEnableSSLOffload(frontendName string, certDir string, alpn string, strictSNI bool) (err error)
4141
FrontendDisableSSLOffload(frontendName string) (err error)
4242
FrontendBindsGet(frontend string) (models.Binds, error)
4343
FrontendBindCreate(frontend string, bind models.Bind) error

controller/haproxy/api/frontend.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func (c *clientNative) FrontendEdit(frontend models.Frontend) error {
5151
return c.nativeAPI.Configuration.EditFrontend(frontend.Name, &frontend, c.activeTransaction, 0)
5252
}
5353

54-
func (c *clientNative) FrontendEnableSSLOffload(frontendName string, certDir string, alpn string) (err error) {
54+
func (c *clientNative) FrontendEnableSSLOffload(frontendName string, certDir string, alpn string, strictSNI bool) (err error) {
5555
binds, err := c.FrontendBindsGet(frontendName)
5656
if err != nil {
5757
return err
@@ -61,6 +61,7 @@ func (c *clientNative) FrontendEnableSSLOffload(frontendName string, certDir str
6161
bind.SslCertificate = certDir
6262
if alpn != "" {
6363
bind.Alpn = alpn
64+
bind.StrictSni = strictSNI
6465
}
6566
err = c.FrontendBindEdit(frontendName, *bind)
6667
}
@@ -81,6 +82,7 @@ func (c *clientNative) FrontendDisableSSLOffload(frontendName string) (err error
8182
bind.Verify = ""
8283
bind.SslCertificate = ""
8384
bind.Alpn = ""
85+
bind.StrictSni = false
8486
err = c.FrontendBindEdit(frontendName, *bind)
8587
}
8688
if err != nil {

documentation/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ This is autogenerated from [doc.yaml](doc.yaml). Description can be found in [ge
2424
| [clean-certs](#clean-certs) | [bool](#bool) | "true" | |:large_blue_circle:|:white_circle:|:white_circle:|
2525
| [client-ca](#authentication) | string | | ssl-offloading |:large_blue_circle:|:white_circle:|:white_circle:|
2626
| [client-crt-optional](#authentication) | [bool](#bool) | "false" | client-ca |:large_blue_circle:|:white_circle:|:white_circle:|
27+
| [client-strict-sni](#ssl-offloading) | [bool](#bool) | "false" | client-ca |:large_blue_circle:|:white_circle:|:white_circle:|
2728
| [cors-enable](#CORS) | [bool](#bool) | "false" | |:large_blue_circle:|:large_blue_circle:|:white_circle:|
2829
| [cors-allow-origin](#CORS) | string | "*" | cors-enable |:large_blue_circle:|:large_blue_circle:|:white_circle:|
2930
| [cors-allow-methods](#CORS) | string | "*" | cors-enable |:large_blue_circle:|:large_blue_circle:|:white_circle:|
@@ -1513,6 +1514,24 @@ src-ip-header: "True-Client-IP"
15131514
- Certificates can be defined in Ingress object: `spec.tls[].secretName`
15141515

15151516

1517+
##### `client-strict-sni`
1518+
1519+
If enabled, HAProxy will only accept TLS client connections where the provided SNI matchs an existing certificate.
1520+
If disabled HAProxy will service the default certificate when the provided SNI does not match.
1521+
1522+
Available on: `configmap`
1523+
1524+
Possible values:
1525+
1526+
- true
1527+
- false `default`
1528+
1529+
Example:
1530+
1531+
```yaml
1532+
client-strict-sni: true
1533+
```
1534+
15161535
##### `ssl-certificate`
15171536

15181537
Sets the name of the Kubernetes secret that contains both the TLS key and certificate.

documentation/doc.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,22 @@ annotations:
614614
version_min: "1.6"
615615
example:
616616
- "client-crt-optional: true"
617+
- title: client-strict-sni
618+
type: bool
619+
group: ssl-offloading
620+
dependencies: client-ca
621+
default: "false"
622+
description:
623+
- If enabled, HAProxy will only accept TLS client connections where the provided SNI matchs an existing certificate.
624+
- If disabled HAProxy will service the default certificate when the provided SNI does not match.
625+
values:
626+
- "true"
627+
- "false"
628+
applies_to:
629+
- configmap
630+
version_min: "1.7"
631+
example:
632+
- "client-strict-sni: true"
617633
- title: cors-enable
618634
type: bool
619635
group: CORS

0 commit comments

Comments
 (0)