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
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,9 @@ spec:
- key
type: object
type: object
useCACertsOnly:
description: Use only CAs certificates in a resulting Bundle
type: boolean
required:
- sources
type: object
Expand Down
3 changes: 3 additions & 0 deletions deploy/crds/trust-manager.io_clusterbundles.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,9 @@ spec:
- message: 'any of the following fields must be provided: [configMap,
secret]'
rule: '[has(self.configMap), has(self.secret)].exists(x,x)'
useCACertsOnly:
description: Use only CAs certificates in a resulting Bundle
type: boolean
type: object
status:
description: Status of the Bundle. This is set and managed automatically.
Expand Down
3 changes: 3 additions & 0 deletions deploy/crds/trust.cert-manager.io_bundles.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,9 @@ spec:
- key
type: object
type: object
useCACertsOnly:
description: Use only CAs certificates in a resulting Bundle
type: boolean
required:
- sources
type: object
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/trust/v1alpha1/types_bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ type BundleSpec struct {
// Target is the target location in all namespaces to sync source data to.
// +optional
Target BundleTarget `json:"target,omitzero"`

// Use only CAs certificates in a resulting Bundle
// +optional
UseCACertsOnly *bool `json:"useCACertsOnly,omitempty"`
}

// BundleSource is the set of sources whose data will be appended and synced to
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/trust/v1alpha1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions pkg/apis/trust/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pkg/apis/trustmanager/v1alpha2/types_cluster_bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ type BundleSpec struct {
// Target is the target location in all namespaces to sync source data to.
// +optional
Target BundleTarget `json:"target,omitzero"`

// Use only CAs certificates in a resulting Bundle
// +optional
UseCACertsOnly *bool `json:"useCACertsOnly,omitempty"`
}

// BundleSource is the set of sources whose data will be appended and synced to
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/trustmanager/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions pkg/applyconfigurations/trust/v1alpha1/bundlespec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions pkg/applyconfigurations/trustmanager/v1alpha2/bundlespec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/bundle/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (b *bundle) reconcileBundle(ctx context.Context, req ctrl.Request) (statusP
statusPatch = &trustapi.BundleStatus{
DefaultCAPackageVersion: bundle.Status.DefaultCAPackageVersion,
}
resolvedBundle, err := b.bundleBuilder.BuildBundle(ctx, bundle.Spec.Sources)
resolvedBundle, err := b.bundleBuilder.BuildBundle(ctx, bundle.Spec)

if err != nil {
var reason, message string
Expand Down
8 changes: 7 additions & 1 deletion pkg/bundle/internal/source/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"

Expand Down Expand Up @@ -55,14 +56,19 @@ type BundleBuilder struct {
DefaultPackage *fspkg.Package

controller.Options

// Use only CAs certificates
UseCACertsOnly bool
}

// BuildBundle retrieves and concatenates all source bundle data for this Bundle object.
// Each source data is validated and pruned to ensure that all certificates within are valid.
func (b *BundleBuilder) BuildBundle(ctx context.Context, sources []trustapi.BundleSource) (BundleData, error) {
func (b *BundleBuilder) BuildBundle(ctx context.Context, bundle trustapi.BundleSpec) (BundleData, error) {
var resolvedBundle BundleData
var sources = bundle.Sources
resolvedBundle.CertPool = util.NewCertPool(
util.WithFilteredExpiredCerts(b.FilterExpiredCerts),
util.WithCACertsOnly(ptr.Deref(bundle.UseCACertsOnly, false)),
util.WithLogger(logf.FromContext(ctx).WithName("cert-pool")),
)

Expand Down
5 changes: 4 additions & 1 deletion pkg/bundle/internal/source/source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,10 @@ func Test_BuildBundle(t *testing.T) {
Options: controller.Options{FilterExpiredCerts: tt.filterExpired},
}

resolvedBundle, err := b.BuildBundle(t.Context(), tt.sources)
bundle := trustapi.BundleSpec{
Sources: tt.sources,
}
resolvedBundle, err := b.BuildBundle(t.Context(), bundle)

if (err != nil) != tt.expError {
t.Errorf("unexpected error, exp=%t got=%v", tt.expError, err)
Expand Down
13 changes: 13 additions & 0 deletions pkg/util/cert_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ type CertPool struct {
filterExpired bool

logger logr.Logger

useCACertsOnly bool
}

type Option func(*CertPool)
Expand All @@ -53,6 +55,12 @@ func WithLogger(logger logr.Logger) Option {
}
}

func WithCACertsOnly(useCACertsOnly bool) Option {
return func(cp *CertPool) {
cp.useCACertsOnly = useCACertsOnly
}
}

// NewCertPool returns a new, empty CertPool.
// It will deduplicate certificates based on their SHA256 hash.
// Optionally, it can filter out expired certificates.
Expand Down Expand Up @@ -142,6 +150,11 @@ func (cp *CertPool) AddCert(certificate *x509.Certificate) bool {
return false
}

if cp.useCACertsOnly && !certificate.IsCA {
cp.logger.Info("ignoring non-CA certificate", "certificate", certificate.Subject)
return false
}

hash := sha256.Sum256(certificate.Raw)
cp.certificates[hash] = certificate
return true
Expand Down
43 changes: 43 additions & 0 deletions pkg/util/cert_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,46 @@ func TestAppendCertFromPEM(t *testing.T) {
})
}
}

// CA certificates only
func TestAppendCACertFromPEM(t *testing.T) {
tests := map[string]struct {
pemData string
filterExpired bool
expError string
expEmpty bool
useCACertsOnly bool
CACertsCount int
}{
"if multiple certificates, should return": {
pemData: dummy.JoinCerts(dummy.TestCertificate1, dummy.TestCertificateNonCA1, dummy.TestCertificate2, dummy.TestCertificate3, dummy.TestCertificateNonCA2),
CACertsCount: 3,
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
t.Parallel()

certPool := NewCertPool(WithCACertsOnly(true))

err := certPool.AddCertsFromPEM([]byte(test.pemData))
if test.expError != "" {
assert.Error(t, err, test.expError)
} else {
assert.NoError(t, err)
}

CACertsList := certPool.Certificates()
if len(CACertsList) != test.CACertsCount {
t.Fatalf("The number of CA certificates isn't equal to expected one: given %d, expected %d", len(CACertsList), test.CACertsCount)
}

for _, cert := range CACertsList {
if !cert.IsCA {
t.Fatalf("there are nonCA certificates in the certificates list")
}
}
})
}
}
124 changes: 124 additions & 0 deletions test/dummy/certificates.go
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,130 @@ PQ5qEvRB3rGmtpvWu/p8z4AlMSWFb9C+Qp4NiU2jiPgw0t1DL/vdrvLcYb/ExyJx
/+ZA+ONCt347Do/oMXy8iT4cmNOe28pHLYHkhkbP5d2ajpjSwqH2Q8Gr8AiMM5OO
HYjDRRens0uEsJFTfFBq0YbGiIAHZ1ESs/ipdisdgmLkIDjF8UKRNoBacodAsghV
z40l74JcR+GvcFZWz7/jmJq95YMZ7LawLAr1CaAXxCwsoLbJpbgg4lVo6odACzY=
-----END CERTIFICATE-----`

// Certificate:
// Data:
// Version: 3 (0x2)
// Serial Number:
// 67:bc:27:4c:38:bd:86:8e:64:64:b9:bc:e7:96:c6:fa:4e:78:57:a4
// Signature Algorithm: sha256WithRSAEncryption
// Issuer: C=US, L=Default City, O=Internet Security Research Group, CN=www.example.com
// Validity
// Not Before: Dec 20 15:32:26 2025 GMT
// Not After : Dec 18 15:32:26 2035 GMT
// Subject: C=US, L=Default City, O=Internet Security Research Group, CN=www.example.com
// Subject Public Key Info:
// Public Key Algorithm: rsaEncryption
// Public-Key: (4096 bit)
// Modulus:
// 00:a9:7c:2d:2a:b1:67:0e:90:f8:37:db:c4:d7:7b:
// 7b:00:40:72:8e:d9:44:70:b0:96:38:4c:4f:91:9f:
// ...
// 56:c9:bc:07:0f:87:f3:10:75:0e:3d:3a:1e:83:cc:
// 61:4d:fb
// Exponent: 65537 (0x10001)
// X509v3 extensions:
// X509v3 Subject Key Identifier:
// 6F:F8:A7:F7:66:75:D9:EB:02:25:05:A4:4E:62:C5:D9:85:53:F9:0B
// X509v3 Authority Key Identifier:
// 6F:F8:A7:F7:66:75:D9:EB:02:25:05:A4:4E:62:C5:D9:85:53:F9:0B
// X509v3 Basic Constraints:
// CA:FALSE
TestCertificateNonCA1 = `-----BEGIN CERTIFICATE-----
MIIFrTCCA5WgAwIBAgIUZ7wnTDi9ho5kZLm855bG+k54V6QwDQYJKoZIhvcNAQEL
BQAwaTELMAkGA1UEBhMCVVMxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEpMCcGA1UE
CgwgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxGDAWBgNVBAMMD3d3
dy5leGFtcGxlLmNvbTAeFw0yNTEyMjAxNTMyMjZaFw0zNTEyMTgxNTMyMjZaMGkx
CzAJBgNVBAYTAlVTMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxKTAnBgNVBAoMIElu
dGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRgwFgYDVQQDDA93d3cuZXhh
bXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCpfC0qsWcO
kPg328TXe3sAQHKO2URwsJY4TE+Rn99jMHtMwHtgmxPQ/sMlljrhPk5vdf55i0R9
iWm9sEvRynhv2hejr2hV0RAM7Q2+Q4We9Qmn8Bh9+sN60I5YVM1lOmVKUbxDQTFm
D1WrC3NMLuAhnkoJKE1ElAN3f6TWihWhlg4lmN8qbFcZIsGMXmEKXiPPFLqTn0h7
G5HTo0SCOqQnG4qdIW4hdOXVb3dIeX5/yVeKwQJcUWrIcOSONG5SAfbwvUFExp7d
10g/j5r4wWGvvzm9pJzpMLgGCwMoATxHdM7k2twSbIIrulfmCgiqzqqmctUEP3c4
AYbPr/LDtlRqEGxFHlfmnpT9nDETF44enBEeQ75tsj7sguqNOay+/lemM42SnfSt
mrcm14LAvZtpV5+zeyxA4ujczsUybWuWM6jtmRaBbQ4MtPCG3q+GMyroMtJ+AXYH
1sM67AppyTNaxYf5bns+mdCKce9BrOM1Uqqh8R0hikoVTW65WhDdh+iJHU9DAbvC
mIW5NDeyevs/utI3hppLZZ1dyhrQ5U1CjrWZ63sEjy0InCF4OJg8Ck4d8HYe1Ipk
I16pRBe9FAd0AvOZgUXE18o5si6LOOEWkAkf9aw3+CeUs3ijRmsg2vFWgivoz4uE
w8L7+b2cQFbJvAcPh/MQdQ49Oh6DzGFN+wIDAQABo00wSzAdBgNVHQ4EFgQUb/in
92Z12esCJQWkTmLF2YVT+QswHwYDVR0jBBgwFoAUb/in92Z12esCJQWkTmLF2YVT
+QswCQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAgEAfgAjHMI8Kt7uVxvRWo8W
DzmYT7FJYWxBGEng7DJcvb346C9o3Bo/4HDG4t0G/COwpbO5k/7GY9WmOCTi7hD9
s5itXH2Oxvnn2Vmnyf0ZcbzFnJWN7sHHvqB7HBCVCmJdzp1etjr7klXUalIRPp9w
Xpv6XUKBCRAWDVvIiZOfeTHrmF1GHcsbBiSZKn/sD7HbgOY4eHFbwWjEtOynW3i4
YBDmd7IZlUnxUJEGFq+8wuFt1QpjtzZp+zD2ZLKE2hdzoUmIWNPPWtrplvP68yTY
KGtggpbG4rTmaq4C7Y2g56hAbWARgtwa52keGl42vBeIoY6uDuByQG1+uxJlkRhh
vKKL8Nv1Bvv1QBpFZa3WckRbe+vUl+4lWQsaHWjydrv//rxTpKw7NO+y2O5YYwgz
kX7dbrSluYP9KjvawopYqaN362QA+ZDkq17u2jF0H3l78IMoVTEszEME5VF9GuP6
EeuzMl8ttrIwZig/t7TSpVzGyxbNAE2CJQ5Ydt22AaLnZX2BXiBRCNxmi58j/fBH
1dIomzU1yZwcXZwUCga0j9+xEhdG8gdr77G4O0P1BO8Tui6WeNU3DAWWWiuBLWT6
K6WVn0GVZR2gMUZs6Fdi1f5pguIQE10be9VM+m2ei9Cy3rjZLnAKN87D2JJJPm8C
plNZGnaNc9ms+ZvMgFUOQOQ=
-----END CERTIFICATE-----`

// Certificate:
// Data:
// Version: 3 (0x2)
// Serial Number:
// 26:0f:0c:cd:09:53:87:27:55:51:74:7b:d9:4e:c4:e1:ea:cd:6d:2c
// Signature Algorithm: sha256WithRSAEncryption
// Issuer: C=US, L=Default City, O=Internet Security Research Group, CN=mail.example.com
// Validity
// Not Before: Dec 20 15:36:45 2025 GMT
// Not After : Dec 18 15:36:45 2035 GMT
// Subject: C=US, L=Default City, O=Internet Security Research Group, CN=mail.example.com
// Subject Public Key Info:
// Public Key Algorithm: rsaEncryption
// Public-Key: (4096 bit)
// Modulus:
// 00:b6:81:5f:a9:f3:19:1f:a6:71:56:61:e6:53:e9:
// 8f:24:f4:05:4f:06:25:a9:8b:f3:4f:b3:82:9a:dd:
// ...
// c8:d1:ca:45:84:69:8e:f6:1d:bb:e0:43:24:73:7c:
// 2f:08:bd
// Exponent: 65537 (0x10001)
// X509v3 extensions:
// X509v3 Subject Key Identifier:
// 43:3E:01:3D:F9:34:CE:19:31:8A:F1:2F:82:68:CD:1D:F5:A8:61:87
// X509v3 Authority Key Identifier:
// 43:3E:01:3D:F9:34:CE:19:31:8A:F1:2F:82:68:CD:1D:F5:A8:61:87
// X509v3 Basic Constraints:
// CA:FALSE
TestCertificateNonCA2 = `-----BEGIN CERTIFICATE-----
MIIFrzCCA5egAwIBAgIUJg8MzQlThydVUXR72U7E4erNbSwwDQYJKoZIhvcNAQEL
BQAwajELMAkGA1UEBhMCVVMxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEpMCcGA1UE
CgwgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxGTAXBgNVBAMMEG1h
aWwuZXhhbXBsZS5jb20wHhcNMjUxMjIwMTUzNjQ1WhcNMzUxMjE4MTUzNjQ1WjBq
MQswCQYDVQQGEwJVUzEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MSkwJwYDVQQKDCBJ
bnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEZMBcGA1UEAwwQbWFpbC5l
eGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALaBX6nz
GR+mcVZh5lPpjyT0BU8GJamL80+zgprdJ/Xq1aMB709asF3o/gWlHVMke2JIX+lr
kvQLoSxr0AB7eOwaz+8KfWH3BLw399j9c88Lo+ZAot8CgpUz7YaeETOUveEGJVSb
PP/k0bmNxqBPnP3ba20xTRlW5MFnTXHxNJ5TghRC98FfeGwEkN8tb5YdFzoRKM/m
FSRevS4rcHBIwpU4l9xNIBfgI7P/Ib/UlrXTITVdAMriypU3AkNn25cUECa4sJ5a
YVvI/bv4r63x8gscFVwhleM5Ms9eENoAF/BbZq2Fc/la2DwZANkaq1WOz0us5NS2
UzIIhKM9OU/+FbPs1x2UiAjGLa5X/HVE2Y0HJx0JzAEb4jMaDHn1KoE2HImWCp5F
RdS2VEAJKoH0Dv6toP//exK/uQoQs3c/V668Dm4yRERWJ42ipeSHrVNkkwUSKgvp
qD2oaaE8CGgXunraE5hbKb4zOy/xBiw6FVdOZ3fyNGlWZ38lvUDLVjyK4GdKEWN5
b6T098qmUfl4vflAxvL0NeGb8zvT2n12REWbtSpPFjp2Tq+fBcPCq1OE3zDsca4d
hwiVNJ33O5VVhqnVd20VlAo+IxHi+IhdIotMhHnhXi0+86i/aRTTiUO2/h/BoHWK
zYp6VSKAFUu2yNHKRYRpjvYdu+BDJHN8Lwi9AgMBAAGjTTBLMB0GA1UdDgQWBBRD
PgE9+TTOGTGK8S+CaM0d9ahhhzAfBgNVHSMEGDAWgBRDPgE9+TTOGTGK8S+CaM0d
9ahhhzAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQA12GwhzZfNQBWbWmk1
2E6JdQFrsRIcgkHo54eyhoIjLsHKrwP99DcHTTFrOfJPrGRk5b/C8mnNlcQOey+I
qmpRrwvpvW4njxPR5bUzt4UsOrG7k+GsA4ovQzjvDCAdjLcvoFFYrMvoQ18rvxEH
NuJeRoqc5bnSO0k+B5lc9TCsfHQwLkw7s6K8l2scdJj7X0vg2KTn0FDRMQlvWcBK
lxbfHTDGK2+N841TIABalhT1ljMlhnfdDiVQGqRnmVdrTM4diJ1CaRm7CW36CwCE
yeYLc5Q/hdNd3AlsZo4Wu9xnjW5ozeoOKveJrm1KSl+BylkF+bHWFaq1w7EDQkVC
uv0BEdFyXcFL7sI3OsblXJibSqZJsZn/SeJja3LAvm5M5FIjoZ1uZzzdc0Hx1tzb
OdjK0cgXqcyz/stQ22kKnadZOi0lMoVUmP8TLo+2B6LvAYHClStEIhBet2W80wDe
PwfVKfLQ98Fpt7QHzpzcwje2I3dfyI1WHNPptJcryFdXcJHLHksv1rk+tbcaHN8K
rsE6P1NCjsAFn43+Kicmlj14IR1UZ54MmK+KXlr1I2/MAXvI86rXhTXC7xI+uS5v
/qf5aom1SWBJAQmexwbC+viyH/obwSNAygLNTyUBOx9TSPlsEazQ8TV+9OaWkbsh
rtDOMVCUoMGIbAJRLwKLL7djlw==
-----END CERTIFICATE-----`
)

Expand Down
Loading