Skip to content

Commit

Permalink
blueprint: customizations for 'cacert'
Browse files Browse the repository at this point in the history
  • Loading branch information
lzap authored and mvo5 committed Nov 21, 2024
1 parent bea8478 commit 6153567
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 0 deletions.
5 changes: 5 additions & 0 deletions pkg/blueprint/ca_customizations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package blueprint

type CACustomization struct {
PEMCerts []string `json:"pem_certs,omitempty" toml:"pem_certs,omitempty"`
}
31 changes: 31 additions & 0 deletions pkg/blueprint/customizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"slices"
"strings"

"github.com/osbuild/images/pkg/cert"
"github.com/osbuild/images/pkg/customizations/anaconda"
)

Expand All @@ -32,6 +33,7 @@ type Customizations struct {
Installer *InstallerCustomization `json:"installer,omitempty" toml:"installer,omitempty"`
RPM *RPMCustomization `json:"rpm,omitempty" toml:"rpm,omitempty"`
RHSM *RHSMCustomization `json:"rhsm,omitempty" toml:"rhsm,omitempty"`
CACerts *CACustomization `json:"cacerts,omitempty" toml:"ca,omitempty"`
}

type IgnitionCustomization struct {
Expand Down Expand Up @@ -433,3 +435,32 @@ func (c *Customizations) GetRHSM() *RHSMCustomization {
}
return c.RHSM
}

func (c *Customizations) checkCACerts() error {
if c == nil {
return nil
}

if c.CACerts != nil {
for _, bundle := range c.CACerts.PEMCerts {
_, err := cert.ParseCerts(bundle)
if err != nil {
return err
}
}
}

return nil
}

func (c *Customizations) GetCACerts() (*CACustomization, error) {
if c == nil {
return nil, nil
}

if err := c.checkCACerts(); err != nil {
return nil, err
}

return c.CACerts, nil
}
46 changes: 46 additions & 0 deletions pkg/cert/parsecerts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package cert

import (
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
)

var ErrNoValidPEMCertificatesFound = errors.New("no valid PEM certificates found")

// ParseCerts parses a PEM-encoded certificate chain formatted as concatenated strings
// and returns a slice of x509.Certificate. In case of unparsable certificates, the
// function returns an empty slice.
// Returns an error when a cert cannot be parsed, or when no certificates are recognized
// in the input.
func ParseCerts(cert string) ([]*x509.Certificate, error) {
result := make([]*x509.Certificate, 0, 1)
block := []byte(cert)
var blocks [][]byte
for {
var certDERBlock *pem.Block
certDERBlock, block = pem.Decode(block)
if certDERBlock == nil {
break
}

if certDERBlock.Type == "CERTIFICATE" {
blocks = append(blocks, certDERBlock.Bytes)
}
}

for _, block := range blocks {
cert, err := x509.ParseCertificate(block)
if err != nil {
return nil, fmt.Errorf("failed to parse certificate: %w", err)
}
result = append(result, cert)
}

if len(result) == 0 {
return nil, fmt.Errorf("%w in: %s", ErrNoValidPEMCertificatesFound, cert)
}

return result, nil
}
52 changes: 52 additions & 0 deletions pkg/cert/parsecerts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package cert

import (
"testing"

"github.com/stretchr/testify/assert"
)

// taken from osbuild:test/data/certs/cert1.pem
const exampleCert = `
-----BEGIN CERTIFICATE-----
MIIDhTCCAm2gAwIBAgIUVya7VJ3O8W8SqwuEa0BZ4HSsXvAwDQYJKoZIhvcNAQEL
BQAwUTELMAkGA1UEBhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVy
bGluMQwwCgYDVQQKDANPcmcxEjAQBgNVBAMMCWxvY2FsaG9zdDAgFw0yNDA4MjYx
MDQyNDBaGA8yMTI0MDgwMjEwNDI0MFowUTELMAkGA1UEBhMCREUxDzANBgNVBAgM
BkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMQwwCgYDVQQKDANPcmcxEjAQBgNVBAMM
CWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnGjlvN
O3F/Z7Lr/r+6Xp2DosnNwoPHhG2e61KnFzgZfaxbklal5ORpuV/gLIg7lrbpdZe7
WvK+16RanL6fLitis/tYVFyvz1MXqBYYrEoFGvVg9fOiis7hjpdZcpNDH9SngoAN
O0Wvv4T6LQS0cC7ZAFZjvmJ+RiZEbzRkNG5pUddZXbotE6htNfLgA5L1wIBgllrM
4DVkG0yNKmzqPNzfPTbdUgWCfjaQShHy1GP8KNEwFxM31F2wvQxsEb77o1S44Out
mlsi83tti6P7KjDk7w2j2zZO1X0xI8pflv3TBkJT1Am8vnk6rVnNO4pCpop3+kma
pDUEzBQmSQA5R1ECAwEAAaNTMFEwHQYDVR0OBBYEFDxFcFgPEsgsDixfKxB0uYGN
aJmzMB8GA1UdIwQYMBaAFDxFcFgPEsgsDixfKxB0uYGNaJmzMA8GA1UdEwEB/wQF
MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFih4lUbLlhKwIAV9x3/W7Mih8xUEdZr
olquZgaHedFet+ByAHvoES3pec7AVYTOD53mjgyZubD6INnVHzKyS4AG9ydD73o4
cmm3DKxBaesvlHeTn0MOKsoM8QCxeyFJmiUPpgDBok/PFnbGR9+JcsrlGJAnsSKD
vWpiwYcBauZ9nnK5yDe5M9XNFPkNDZzbKvWU7Sw3ziMT/+bRJse5vTrYcyOnNGgy
gZNz2nimKy1U8XZVAVwOV0rdGEFrfMln8DkRW86rGK/EncaVsl0SSP/rmjQgiX8Q
3CZraQGujJP932HSwUfdCX9yh+rTjE3MEnbqMoLzJa4BXB2aDQWtywU=
-----END CERTIFICATE-----
`

func TestParseCerts(t *testing.T) {
certs, err := ParseCerts(exampleCert)
assert.Nil(t, err)
assert.Equal(t, len(certs), 1)
assert.Equal(t, "localhost", certs[0].Subject.CommonName)
}

func TestParseConcatenatedCerts(t *testing.T) {
certs, err := ParseCerts(exampleCert + "\n" + exampleCert)
assert.Nil(t, err)
assert.Equal(t, len(certs), 2)
assert.Equal(t, "localhost", certs[0].Subject.CommonName)
}

func TestParseGarbageCerts(t *testing.T) {
_, err := ParseCerts("garbage")
assert.NotNil(t, err)
assert.Equal(t, "no valid PEM certificates found in: garbage", err.Error())
}
8 changes: 8 additions & 0 deletions pkg/distro/fedora/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,14 @@ func osCustomizations(
osc.Files = append(osc.Files, imageConfig.Files...)
osc.Directories = append(osc.Directories, imageConfig.Directories...)

ca, err := c.GetCACerts()
if err != nil {
panic(fmt.Sprintf("unexpected error checking CA certs: %v", err))
}
if ca != nil {
osc.CACerts = ca.PEMCerts
}

return osc, nil
}

Expand Down
8 changes: 8 additions & 0 deletions pkg/distro/rhel/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,14 @@ func osCustomizations(
osc.NoBLS = *imageConfig.NoBLS
}

ca, err := c.GetCACerts()
if err != nil {
panic(fmt.Sprintf("unexpected error checking CA certs: %v", err))
}
if ca != nil {
osc.CACerts = ca.PEMCerts
}

return osc, nil
}

Expand Down

0 comments on commit 6153567

Please sign in to comment.