Skip to content

Commit 1c93947

Browse files
committed
generate-certificates-signer option to configure haproxy generate-certificates and ca-sign-file bind options in https frontend
1 parent feb2569 commit 1c93947

File tree

6 files changed

+93
-15
lines changed

6 files changed

+93
-15
lines changed

documentation/annotations.md

Lines changed: 27 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
| [client-ca](#authentication) | string | | ssl-offloading |:large_blue_circle:|:white_circle:|:white_circle:|
2525
| [client-crt-optional](#authentication) | [bool](#bool) | "false" | client-ca |:large_blue_circle:|:white_circle:|:white_circle:|
2626
| [client-strict-sni](#ssl-offloading) | [bool](#bool) | "false" | client-ca |:large_blue_circle:|:white_circle:|:white_circle:|
27+
| [generate-certificates-signer](#ssl-offloading) | string | | |: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:|
@@ -1698,6 +1699,32 @@ Example:
16981699
client-strict-sni: true
16991700
```
17001701

1702+
##### `generate-certificates-signer`
1703+
1704+
Specifies the Kubernetes secret containing the CA certificate file used to sign automatically generated certificates.
1705+
When this annotation is set, HAProxy's generate-certificates feature is automatically enabled on the HTTPS frontend bind line,
1706+
allowing HAProxy to automatically generate certificates for incoming TLS connections using the provided CA for signing.
1707+
1708+
Available on: `configmap`
1709+
1710+
:information_source: The secret should contain the CA certificate and key in PEM format.
1711+
1712+
:information_source: The secret format should be namespace/secret-name.
1713+
1714+
:information_source: When this annotation is configured, the generate-certificates option is automatically added to the bind line in the HTTPS frontend when SSL offload is enabled.
1715+
1716+
:information_source: HAProxy will use this CA to sign certificates generated for incoming TLS connections.
1717+
1718+
Possible values:
1719+
1720+
- Name of Kubernetes secret in format namespace/secret-name
1721+
1722+
Example:
1723+
1724+
```yaml
1725+
generate-certificates-signer: "default/ca-signing-cert"
1726+
```
1727+
17011728
##### `ssl-certificate`
17021729

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

documentation/doc.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,27 @@ annotations:
686686
version_min: "1.8"
687687
example:
688688
- "client-strict-sni: true"
689+
- title: generate-certificates-signer
690+
type: string
691+
group: ssl-offloading
692+
dependencies: ""
693+
default: ""
694+
description:
695+
- Specifies the Kubernetes kubernetes.io/tls type secret containing the CA certificate file used to sign automatically generated certificates.
696+
- When this annotation is set, HAProxy's generate-certificates feature is automatically enabled on the HTTPS frontend bind line.
697+
- This allows HAProxy to automatically generate certificates for incoming TLS connections using the provided CA for signing.
698+
- The secret should contain the CA certificate and key in PEM format.
699+
tip:
700+
- The secret format should be namespace/secret-name or just secret-name.
701+
- When this annotation is configured, the generate-certificates option is automatically added to the bind line in the HTTPS frontend when SSL offload is enabled.
702+
- HAProxy will use this CA to sign certificates generated for incoming TLS connections.
703+
values:
704+
- Name of Kubernetes secret in format namespace/secret-name
705+
applies_to:
706+
- configmap
707+
version_min: "1.11"
708+
example:
709+
- 'generate-certificates-signer: "default/ca-signing-cert"'
689710
- title: cors-enable
690711
type: bool
691712
group: CORS

pkg/handler/https.go

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,16 @@ import (
3434
)
3535

3636
type HTTPS struct {
37-
AddrIPv4 string
38-
AddrIPv6 string
39-
CertDir string
40-
alpn string
41-
Port int64
42-
Enabled bool
43-
IPv4 bool
44-
IPv6 bool
45-
strictSNI bool
37+
AddrIPv4 string
38+
AddrIPv6 string
39+
CertDir string
40+
alpn string
41+
Port int64
42+
Enabled bool
43+
IPv4 bool
44+
IPv6 bool
45+
strictSNI bool
46+
generateCertificatesSigner string
4647
}
4748

4849
//nolint:golint, stylecheck
@@ -170,11 +171,32 @@ func (handler *HTTPS) Update(k store.K8s, h haproxy.HAProxy, a annotations.Annot
170171
handler.strictSNI, err = annotations.Bool("client-strict-sni", k.ConfigMaps.Main.Annotations)
171172
logger.Error(err)
172173

174+
// Handle generate-certificates-signer secret
175+
handler.generateCertificatesSigner = ""
176+
var notFound store.ErrNotFound
177+
secret, annErr := annotations.Secret("generate-certificates-signer", "", k, k.ConfigMaps.Main.Annotations)
178+
if annErr != nil {
179+
if errors.Is(annErr, notFound) {
180+
logger.Debugf("generate-certificates-signer not configured: %s", annErr)
181+
} else {
182+
err = fmt.Errorf("generate-certificates-signer: %w", annErr)
183+
return err
184+
}
185+
}
186+
if secret != nil {
187+
caFile, certErr := h.Certificates.AddSecret(secret, certs.FT_CERT)
188+
if certErr != nil {
189+
err = fmt.Errorf("generate-certificates-signer: %w", certErr)
190+
return err
191+
}
192+
handler.generateCertificatesSigner = caFile
193+
}
194+
173195
// ssl-offload
174196
sslOffloadEnabled := h.FrontendSSLOffloadEnabled(h.FrontHTTPS)
175197
if h.FrontCertsInUse() {
176198
if !sslOffloadEnabled {
177-
logger.Panic(h.FrontendEnableSSLOffload(h.FrontHTTPS, handler.CertDir, handler.alpn, handler.strictSNI))
199+
logger.Panic(h.FrontendEnableSSLOffload(h.FrontHTTPS, handler.CertDir, handler.alpn, handler.strictSNI, handler.generateCertificatesSigner))
178200
instance.Reload("SSL offload enabled")
179201
}
180202
err := handler.handleClientTLSAuth(k, h)
@@ -268,7 +290,7 @@ func (handler *HTTPS) toggleSSLPassthrough(passthrough bool, h haproxy.HAProxy)
268290
}
269291
}
270292
if h.FrontendSSLOffloadEnabled(h.FrontHTTPS) || h.FrontCertsInUse() {
271-
logger.Panic(h.FrontendEnableSSLOffload(h.FrontHTTPS, handler.CertDir, handler.alpn, handler.strictSNI))
293+
logger.Panic(h.FrontendEnableSSLOffload(h.FrontHTTPS, handler.CertDir, handler.alpn, handler.strictSNI, handler.generateCertificatesSigner))
272294
}
273295
return nil
274296
}

pkg/handler/tcp-services.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ func (handler TCPServices) createTCPFrontend(h haproxy.HAProxy, frontend models.
160160
}))
161161
}
162162
if sslOffload {
163-
errors.Add(h.FrontendEnableSSLOffload(frontend.Name, handler.CertDir, "", false))
163+
errors.Add(h.FrontendEnableSSLOffload(frontend.Name, handler.CertDir, "", false, ""))
164164
}
165165
if errors.Result() != nil {
166166
err = fmt.Errorf("error configuring tcp frontend: %w", err)
@@ -191,7 +191,7 @@ func (handler TCPServices) updateTCPFrontend(k store.K8s, h haproxy.HAProxy, fro
191191
return err
192192
}
193193
if !binds[0].Ssl && p.sslOffload {
194-
err = h.FrontendEnableSSLOffload(frontend.Name, handler.CertDir, "", false)
194+
err = h.FrontendEnableSSLOffload(frontend.Name, handler.CertDir, "", false, "")
195195
if err != nil {
196196
err = fmt.Errorf("failed to enable SSL offload: %w", err)
197197
return err

pkg/haproxy/api/api.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ type HAProxyClient interface { //nolint:interfacebloat
6666
FrontendsGet() (models.Frontends, error)
6767
FrontendGet(frontendName string) (models.Frontend, error)
6868
FrontendEdit(frontend models.FrontendBase) error
69-
FrontendEnableSSLOffload(frontendName string, certDir string, alpn string, strictSNI bool) (err error)
69+
FrontendEnableSSLOffload(frontendName string, certDir string, alpn string, strictSNI bool, generateCertificatesSigner string) (err error)
7070
FrontendDisableSSLOffload(frontendName string) (err error)
7171
FrontendSSLOffloadEnabled(frontendName string) bool
7272
Bind

pkg/haproxy/api/frontend.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,20 @@ func (c *clientNative) FrontendEdit(frontend models.FrontendBase) error {
7373
return configuration.EditFrontend(frontend.Name, f, c.activeTransaction, 0)
7474
}
7575

76-
func (c *clientNative) FrontendEnableSSLOffload(frontendName string, certDir string, alpn string, strictSNI bool) (err error) {
76+
func (c *clientNative) FrontendEnableSSLOffload(frontendName string, certDir string, alpn string, strictSNI bool, generateCertificatesSigner string) (err error) {
7777
binds, err := c.FrontendBindsGet(frontendName)
7878
if err != nil {
7979
return err
8080
}
8181
for _, bind := range binds {
8282
bind.Ssl = true
8383
bind.SslCertificate = certDir
84+
if generateCertificatesSigner != "" {
85+
bind.GenerateCertificates = true
86+
bind.CaSignFile = generateCertificatesSigner
87+
} else {
88+
bind.GenerateCertificates = false
89+
}
8490
if alpn != "" {
8591
bind.Alpn = alpn
8692
bind.StrictSni = strictSNI
@@ -103,8 +109,10 @@ func (c *clientNative) FrontendDisableSSLOffload(frontendName string) (err error
103109
bind.SslCafile = ""
104110
bind.Verify = ""
105111
bind.SslCertificate = ""
112+
bind.CaSignFile = ""
106113
bind.Alpn = ""
107114
bind.StrictSni = false
115+
bind.GenerateCertificates = false
108116
err = c.FrontendBindEdit(frontendName, *bind)
109117
}
110118
if err != nil {

0 commit comments

Comments
 (0)