Skip to content

Commit 5cc70f2

Browse files
committed
create self-signed crt if failing to read default
Ingress operator manages and exposes a default certificate for Router. A default certificate is used if the route does not provide one, or a request is made for a non matching route. Current approach uses a hardcoded certificate in the router image, which should lead to image scanners reporting the storage of its private key. This implementation removes the hardcoded certificate, as well as the template code that ensures a default certificate is provided. In the Go code side, we are ensuring that the default certificate is provided somehow, either by serving the certificate provided by the operator, or in the case of a failure, using a self-signed certificate created on the fly. https://issues.redhat.com/browse/OCPBUGS-77263
1 parent 8963907 commit 5cc70f2

5 files changed

Lines changed: 82 additions & 58 deletions

File tree

images/router/haproxy/conf/default_pub_keys.pem

Lines changed: 0 additions & 50 deletions
This file was deleted.

images/router/haproxy/conf/haproxy-config.template

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ frontend fe_sni
339339
# terminate ssl on edge
340340
bind unix@/var/lib/haproxy/run/haproxy-sni.sock ssl
341341
{{- if isTrue (env "ROUTER_STRICT_SNI") }} strict-sni {{ end }}
342-
{{- "" }} crt {{firstMatch ".+" .DefaultCertificate "/var/lib/haproxy/conf/default_pub_keys.pem" }}
342+
{{- "" }} crt {{ .DefaultCertificate }}
343343
{{- "" }} crt-list /var/lib/haproxy/conf/cert_config.map accept-proxy
344344
{{- with (env "ROUTER_MUTUAL_TLS_AUTH") }}
345345
{{- "" }} verify {{. }}
@@ -455,7 +455,7 @@ backend be_no_sni
455455

456456
frontend fe_no_sni
457457
# terminate ssl on edge
458-
bind unix@/var/lib/haproxy/run/haproxy-no-sni.sock ssl crt {{ firstMatch ".+" .DefaultCertificate "/var/lib/haproxy/conf/default_pub_keys.pem" }} accept-proxy
458+
bind unix@/var/lib/haproxy/run/haproxy-no-sni.sock ssl crt {{ .DefaultCertificate }} accept-proxy
459459
{{- with (env "ROUTER_MUTUAL_TLS_AUTH") }}
460460
{{- "" }} verify {{. }}
461461
{{- if (ne (env "ROUTER_MUTUAL_TLS_AUTH_CRL") "") }}

pkg/router/template/plugin.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,10 @@ func NewTemplatePlugin(cfg TemplatePluginConfig, lookupSvc ServiceLookup) (*Temp
166166
httpRequestHeaders: cfg.HTTPRequestHeaders,
167167
}
168168
router, err := newTemplateRouter(templateRouterCfg)
169-
return newDefaultTemplatePlugin(router, cfg.IncludeUDP, lookupSvc), err
169+
if err != nil {
170+
return nil, err
171+
}
172+
return newDefaultTemplatePlugin(router, cfg.IncludeUDP, lookupSvc), nil
170173
}
171174

172175
// Stop instructs the router plugin to stop invoking the reload method, and waits until no further

pkg/router/template/router.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -415,13 +415,25 @@ func (r *templateRouter) writeDefaultCert() error {
415415
// Just use the provided path
416416
return nil
417417
}
418+
419+
// Use the custom file name, to be created below, either with a
420+
// self signed certificate or one read from the certificate dir.
421+
r.defaultCertificatePath = outPath
422+
423+
// There is no default cert path as well. If cert dir is also missing ...
424+
if len(r.defaultCertificateDir) == 0 {
425+
// ... use the self signed certificate. Finish here since it won't be updated during the router lifecycle.
426+
return generateSelfSignedCert(outPath)
427+
}
428+
429+
// Use certificate from the provided dir, and return (finishes the process) in case of a failure.
430+
// This differs from previous versions: previously we were defaulting to the self signed one,
431+
// now we enforce that provided certificates should be valid.
418432
if err := secretToPem(r.defaultCertificateDir, outPath); err != nil {
419-
log.Error(err, "failed to write default cert")
420-
// no pem file, no default cert, use cert from container
421-
log.V(0).Info("using default cert from router container image")
422-
} else {
423-
r.defaultCertificatePath = outPath
433+
return fmt.Errorf("failed to write default cert: %w", err)
424434
}
435+
436+
// watching for changes
425437
reloadFn := func() {
426438
log.V(0).Info("updating default certificate", "path", outPath)
427439
os.Remove(outPath)

pkg/router/template/ssl.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package templaterouter
2+
3+
import (
4+
"crypto/rand"
5+
"crypto/rsa"
6+
"crypto/x509"
7+
"crypto/x509/pkix"
8+
"encoding/pem"
9+
"fmt"
10+
"math/big"
11+
"os"
12+
"time"
13+
)
14+
15+
func generateSelfSignedCert(outputFile string) error {
16+
const cn = "openshift-router"
17+
san := "localhost"
18+
if domain := os.Getenv("ROUTER_DOMAIN"); domain != "" {
19+
san = "*." + domain
20+
}
21+
crt, key, err := createX509Certificate(cn, san)
22+
if err != nil {
23+
return fmt.Errorf("error creating self-signed certificate: %w", err)
24+
}
25+
return os.WriteFile(outputFile, append(crt, key...), 0600)
26+
}
27+
28+
func createX509Certificate(cn string, san ...string) (crtpem, keypem []byte, err error) {
29+
serial, err := rand.Int(rand.Reader, big.NewInt(1<<63-1))
30+
if err != nil {
31+
return nil, nil, err
32+
}
33+
notBefore := time.Now().Add(-time.Hour)
34+
notAfter := notBefore.Add(3652 * 24 * time.Hour) // 10y
35+
template := x509.Certificate{
36+
SerialNumber: serial,
37+
Subject: pkix.Name{
38+
CommonName: cn,
39+
},
40+
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
41+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
42+
BasicConstraintsValid: true,
43+
NotBefore: notBefore,
44+
NotAfter: notAfter,
45+
DNSNames: san,
46+
}
47+
priv, err := rsa.GenerateKey(rand.Reader, 2048)
48+
if err != nil {
49+
return nil, nil, err
50+
}
51+
crtder, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
52+
if err != nil {
53+
return nil, nil, err
54+
}
55+
keyder := x509.MarshalPKCS1PrivateKey(priv)
56+
crtpem = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: crtder})
57+
keypem = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: keyder})
58+
return crtpem, keypem, nil
59+
}

0 commit comments

Comments
 (0)