Skip to content
Open
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
27 changes: 27 additions & 0 deletions documentation/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This is autogenerated from [doc.yaml](doc.yaml). Description can be found in [ge
| [client-ca](#authentication) | string | | ssl-offloading |:large_blue_circle:|:white_circle:|:white_circle:|
| [client-crt-optional](#authentication) | [bool](#bool) | "false" | client-ca |:large_blue_circle:|:white_circle:|:white_circle:|
| [client-strict-sni](#ssl-offloading) | [bool](#bool) | "false" | client-ca |:large_blue_circle:|:white_circle:|:white_circle:|
| [generate-certificates-signer](#ssl-offloading) | string | | |:large_blue_circle:|:white_circle:|:white_circle:|
| [cors-enable](#CORS) | [bool](#bool) | "false" | |:large_blue_circle:|:large_blue_circle:|:white_circle:|
| [cors-allow-origin](#CORS) | string | "*" | cors-enable |:large_blue_circle:|:large_blue_circle:|:white_circle:|
| [cors-allow-methods](#CORS) | string | "*" | cors-enable |:large_blue_circle:|:large_blue_circle:|:white_circle:|
Expand Down Expand Up @@ -1698,6 +1699,32 @@ Example:
client-strict-sni: true
```

##### `generate-certificates-signer`

Specifies the Kubernetes secret containing the CA certificate file used to sign automatically generated certificates.
When this annotation is set, HAProxy's generate-certificates feature is automatically enabled on the HTTPS frontend bind line,
allowing HAProxy to automatically generate certificates for incoming TLS connections using the provided CA for signing.

Available on: `configmap`

:information_source: The secret should contain the CA certificate and key in PEM format.

:information_source: The secret format should be namespace/secret-name.

: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.

:information_source: HAProxy will use this CA to sign certificates generated for incoming TLS connections.

Possible values:

- Name of Kubernetes secret in format namespace/secret-name

Example:

```yaml
generate-certificates-signer: "default/ca-signing-cert"
```

##### `ssl-certificate`

Sets the name of the Kubernetes secret that contains both the TLS key and certificate.
Expand Down
21 changes: 21 additions & 0 deletions documentation/doc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,27 @@ annotations:
version_min: "1.8"
example:
- "client-strict-sni: true"
- title: generate-certificates-signer
type: string
group: ssl-offloading
dependencies: ""
default: ""
description:
- Specifies the Kubernetes kubernetes.io/tls type secret containing the CA certificate file used to sign automatically generated certificates.
- When this annotation is set, HAProxy's generate-certificates feature is automatically enabled on the HTTPS frontend bind line.
- This allows HAProxy to automatically generate certificates for incoming TLS connections using the provided CA for signing.
- The secret should contain the CA certificate and key in PEM format.
tip:
- The secret format should be namespace/secret-name or just secret-name.
- 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.
- HAProxy will use this CA to sign certificates generated for incoming TLS connections.
values:
- Name of Kubernetes secret in format namespace/secret-name
applies_to:
- configmap
version_min: "1.11"
example:
- 'generate-certificates-signer: "default/ca-signing-cert"'
- title: cors-enable
type: bool
group: CORS
Expand Down
44 changes: 33 additions & 11 deletions pkg/handler/https.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,16 @@ import (
)

type HTTPS struct {
AddrIPv4 string
AddrIPv6 string
CertDir string
alpn string
Port int64
Enabled bool
IPv4 bool
IPv6 bool
strictSNI bool
AddrIPv4 string
AddrIPv6 string
CertDir string
alpn string
Port int64
Enabled bool
IPv4 bool
IPv6 bool
strictSNI bool
generateCertificatesSigner string
}

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

// Handle generate-certificates-signer secret
handler.generateCertificatesSigner = ""
var notFound store.ErrNotFound
secret, annErr := annotations.Secret("generate-certificates-signer", "", k, k.ConfigMaps.Main.Annotations)
if annErr != nil {
if errors.Is(annErr, notFound) {
logger.Debugf("generate-certificates-signer not configured: %s", annErr)
} else {
err = fmt.Errorf("generate-certificates-signer: %w", annErr)
return err
}
}
if secret != nil {
caFile, certErr := h.Certificates.AddSecret(secret, certs.FT_CERT)
if certErr != nil {
err = fmt.Errorf("generate-certificates-signer: %w", certErr)
return err
}
handler.generateCertificatesSigner = caFile
}

// ssl-offload
sslOffloadEnabled := h.FrontendSSLOffloadEnabled(h.FrontHTTPS)
if h.FrontCertsInUse() {
if !sslOffloadEnabled {
logger.Panic(h.FrontendEnableSSLOffload(h.FrontHTTPS, handler.CertDir, handler.alpn, handler.strictSNI))
logger.Panic(h.FrontendEnableSSLOffload(h.FrontHTTPS, handler.CertDir, handler.alpn, handler.strictSNI, handler.generateCertificatesSigner))
instance.Reload("SSL offload enabled")
}
err := handler.handleClientTLSAuth(k, h)
Expand Down Expand Up @@ -268,7 +290,7 @@ func (handler *HTTPS) toggleSSLPassthrough(passthrough bool, h haproxy.HAProxy)
}
}
if h.FrontendSSLOffloadEnabled(h.FrontHTTPS) || h.FrontCertsInUse() {
logger.Panic(h.FrontendEnableSSLOffload(h.FrontHTTPS, handler.CertDir, handler.alpn, handler.strictSNI))
logger.Panic(h.FrontendEnableSSLOffload(h.FrontHTTPS, handler.CertDir, handler.alpn, handler.strictSNI, handler.generateCertificatesSigner))
}
return nil
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/handler/tcp-services.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func (handler TCPServices) createTCPFrontend(h haproxy.HAProxy, frontend models.
}))
}
if sslOffload {
errors.Add(h.FrontendEnableSSLOffload(frontend.Name, handler.CertDir, "", false))
errors.Add(h.FrontendEnableSSLOffload(frontend.Name, handler.CertDir, "", false, ""))
}
if errors.Result() != nil {
err = fmt.Errorf("error configuring tcp frontend: %w", err)
Expand Down Expand Up @@ -191,7 +191,7 @@ func (handler TCPServices) updateTCPFrontend(k store.K8s, h haproxy.HAProxy, fro
return err
}
if !binds[0].Ssl && p.sslOffload {
err = h.FrontendEnableSSLOffload(frontend.Name, handler.CertDir, "", false)
err = h.FrontendEnableSSLOffload(frontend.Name, handler.CertDir, "", false, "")
if err != nil {
err = fmt.Errorf("failed to enable SSL offload: %w", err)
return err
Expand Down
2 changes: 1 addition & 1 deletion pkg/haproxy/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ type HAProxyClient interface { //nolint:interfacebloat
FrontendsGet() (models.Frontends, error)
FrontendGet(frontendName string) (models.Frontend, error)
FrontendEdit(frontend models.FrontendBase) error
FrontendEnableSSLOffload(frontendName string, certDir string, alpn string, strictSNI bool) (err error)
FrontendEnableSSLOffload(frontendName string, certDir string, alpn string, strictSNI bool, generateCertificatesSigner string) (err error)
FrontendDisableSSLOffload(frontendName string) (err error)
FrontendSSLOffloadEnabled(frontendName string) bool
Bind
Expand Down
10 changes: 9 additions & 1 deletion pkg/haproxy/api/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,20 @@ func (c *clientNative) FrontendEdit(frontend models.FrontendBase) error {
return configuration.EditFrontend(frontend.Name, f, c.activeTransaction, 0)
}

func (c *clientNative) FrontendEnableSSLOffload(frontendName string, certDir string, alpn string, strictSNI bool) (err error) {
func (c *clientNative) FrontendEnableSSLOffload(frontendName string, certDir string, alpn string, strictSNI bool, generateCertificatesSigner string) (err error) {
binds, err := c.FrontendBindsGet(frontendName)
if err != nil {
return err
}
for _, bind := range binds {
bind.Ssl = true
bind.SslCertificate = certDir
if generateCertificatesSigner != "" {
bind.GenerateCertificates = true
bind.CaSignFile = generateCertificatesSigner
} else {
bind.GenerateCertificates = false
}
if alpn != "" {
bind.Alpn = alpn
bind.StrictSni = strictSNI
Expand All @@ -103,8 +109,10 @@ func (c *clientNative) FrontendDisableSSLOffload(frontendName string) (err error
bind.SslCafile = ""
bind.Verify = ""
bind.SslCertificate = ""
bind.CaSignFile = ""
bind.Alpn = ""
bind.StrictSni = false
bind.GenerateCertificates = false
err = c.FrontendBindEdit(frontendName, *bind)
}
if err != nil {
Expand Down