From 19271a542ba855b69f9622a0015bffdc0c5e9f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Budai?= Date: Tue, 3 Nov 2020 10:21:09 +0100 Subject: [PATCH] test/auth: rework the test to use the new openssl setup Previous commits introduces a new way to generate all X.509 certificates needed for testing. This commit reuses the same method for auth tests. This has two benefits: 1) The new code generates certificates with Subject Alternative Name which means we can use it on systems with Go 1.15 (Fedora 33, RHEL 8.4). 2) The new code generates much saner certificates. --- cmd/osbuild-auth-tests/certificates.go | 174 +++++++++++++++++++------ cmd/osbuild-auth-tests/main_test.go | 32 +++-- 2 files changed, 156 insertions(+), 50 deletions(-) diff --git a/cmd/osbuild-auth-tests/certificates.go b/cmd/osbuild-auth-tests/certificates.go index 27099fcd6..120c094d4 100644 --- a/cmd/osbuild-auth-tests/certificates.go +++ b/cmd/osbuild-auth-tests/certificates.go @@ -9,6 +9,12 @@ import ( "path" ) +const ( + opensslConfig = "/usr/share/tests/osbuild-composer/x509/openssl.cnf" + osbuildCAExt = "osbuild_ca_ext" + osbuildClientExt = "osbuild_client_ext" +) + type certificateKeyPair struct { baseDir string } @@ -28,44 +34,6 @@ func (ckp certificateKeyPair) key() string { return path.Join(ckp.baseDir, "key") } -func newCertificateKeyPair(CA, CAkey, subj string) (*certificateKeyPair, error) { - dir, err := ioutil.TempDir("", "osbuild-auth-tests-") - if err != nil { - return nil, fmt.Errorf("cannot create a temporary directory for the certificate: %v", err) - } - - ckp := certificateKeyPair{baseDir: dir} - certificateRequest := path.Join(dir, "csr") - - cmd := exec.Command( - "openssl", "req", "-new", "-nodes", - "-subj", subj, - "-keyout", ckp.key(), - "-out", certificateRequest, - ) - - err = cmd.Run() - if err != nil { - return nil, fmt.Errorf("cannot generate a private key and a certificate request: %v", err) - } - - defer os.Remove(certificateRequest) - - cmd = exec.Command( - "openssl", "x509", "-req", "-CAcreateserial", - "-in", certificateRequest, - "-CA", CA, - "-CAkey", CAkey, - "-out", ckp.certificate(), - ) - err = cmd.Run() - if err != nil { - return nil, fmt.Errorf("cannot sign the certificate: %v", err) - } - - return &ckp, nil -} - func newSelfSignedCertificateKeyPair(subj string) (*certificateKeyPair, error) { dir, err := ioutil.TempDir("", "osbuild-auth-tests-") if err != nil { @@ -87,3 +55,133 @@ func newSelfSignedCertificateKeyPair(subj string) (*certificateKeyPair, error) { return &ckp, nil } + +type ca struct { + BaseDir string +} + +func (c ca) remove() { + err := os.RemoveAll(c.BaseDir) + if err != nil { + log.Printf("cannot delete the ca: %v", err) + } +} + +func (c ca) certificate() string { + return path.Join(c.BaseDir, "ca.cert.pem") +} + +func (c ca) key() string { + return path.Join(c.BaseDir, "private", "ca.key.pem") +} + +func newCA(subj string) (*ca, error) { + baseDir, err := ioutil.TempDir("", "osbuild-auth-tests-ca") + if err != nil { + return nil, fmt.Errorf("cannot create a temporary dir for a new CA: %v", err) + } + + err = os.Mkdir(path.Join(baseDir, "certs"), 0700) + if err != nil { + innerErr := os.RemoveAll(baseDir) + if innerErr != nil { + log.Print(innerErr) + } + return nil, fmt.Errorf("cannot create certs dir for the new CA: %v", err) + } + + err = os.Mkdir(path.Join(baseDir, "private"), 0700) + if err != nil { + innerErr := os.RemoveAll(baseDir) + if innerErr != nil { + log.Print(innerErr) + } + return nil, fmt.Errorf("cannot create private dir for the new CA: %v", err) + } + + f, err := os.Create(path.Join(baseDir, "index.txt")) + if err != nil { + innerErr := os.RemoveAll(baseDir) + if innerErr != nil { + log.Print(innerErr) + } + return nil, fmt.Errorf("cannot create index file for the new CA: %v", err) + } + f.Close() + + c := ca{ + BaseDir: baseDir, + } + + cmd := exec.Command( + "openssl", "req", + "-config", opensslConfig, + "-new", "-nodes", "-x509", "-extensions", osbuildCAExt, + "-subj", subj, + "-keyout", c.key(), + "-out", c.certificate(), + ) + + err = cmd.Run() + if err != nil { + innerErr := os.RemoveAll(baseDir) + if innerErr != nil { + log.Print(innerErr) + } + return nil, fmt.Errorf("cannot create the CA: %v", err) + } + + return &c, nil +} + +func (c ca) newCertificateKeyPair(subj, extensions, addext string) (*certificateKeyPair, error) { + dir, err := ioutil.TempDir("", "osbuild-auth-tests-") + if err != nil { + return nil, fmt.Errorf("cannot create a temporary directory for the certificate: %v", err) + } + + ckp := certificateKeyPair{baseDir: dir} + certificateRequest := path.Join(dir, "csr") + + args := []string{ + "req", "-new", "-nodes", + "-subj", subj, + "-keyout", ckp.key(), + "-out", certificateRequest, + "-config", opensslConfig, + } + + if addext != "" { + args = append(args, "-addext", addext) + } + + cmd := exec.Command( + "openssl", + args..., + ) + + err = cmd.Run() + if err != nil { + return nil, fmt.Errorf("cannot generate a private key and a certificate request: %v", err) + } + + defer os.Remove(certificateRequest) + + cmd = exec.Command( + "openssl", "ca", + "-batch", + "-config", opensslConfig, + "-extensions", extensions, + "-in", certificateRequest, + "-out", ckp.certificate(), + ) + // this command must be run in the CA base directory + cmd.Dir = c.BaseDir + + err = cmd.Run() + if err != nil { + return nil, fmt.Errorf("cannot sign the certificate: %v", err) + } + + return &ckp, nil +} diff --git a/cmd/osbuild-auth-tests/main_test.go b/cmd/osbuild-auth-tests/main_test.go index fcfe9cd58..a5b2c884f 100644 --- a/cmd/osbuild-auth-tests/main_test.go +++ b/cmd/osbuild-auth-tests/main_test.go @@ -14,6 +14,8 @@ import ( "github.com/stretchr/testify/require" ) +const trustedCADir = "/etc/osbuild-composer-test/ca" + type connectionConfig struct { CACertFile string ClientKeyFile string @@ -48,16 +50,19 @@ func TestWorkerAPIAuth(t *testing.T) { cases := []struct { caseDesc string subj string + addext string success bool }{ - {"valid CN 1", "/CN=worker.osbuild.org", true}, - {"valid CN 2", "/CN=localhost", true}, - {"invalid CN", "/CN=example.com", false}, + {"valid CN 1", "/CN=worker.osbuild.org/emailAddress=osbuild@example.com", "subjectAltName=DNS:example.com,DNS:worker.osbuild.org", true}, + {"valid CN 2", "/CN=localhost/emailAddress=osbuild@example.com", "subjectAltName=DNS:example.com,DNS:localhost", true}, + {"invalid CN", "/CN=example.com/emailAddress=osbuild@example.com", "subjectAltName=DNS:example.com", false}, } + authority := &ca{BaseDir: trustedCADir} + for _, c := range cases { t.Run(c.caseDesc, func(t *testing.T) { - ckp, err := newCertificateKeyPair("/etc/osbuild-composer/ca-crt.pem", "/etc/osbuild-composer/ca-key.pem", c.subj) + ckp, err := authority.newCertificateKeyPair(c.subj, osbuildClientExt, c.addext) require.NoError(t, err) defer ckp.remove() @@ -68,12 +73,12 @@ func TestWorkerAPIAuth(t *testing.T) { t.Run("certificate signed by an untrusted CA", func(t *testing.T) { // generate a new CA - ca, err := newSelfSignedCertificateKeyPair("/CN=osbuild.org") + ca, err := newCA("/CN=untrusted.osbuild.org") require.NoError(t, err) defer ca.remove() // create a new certificate and signed it with the new CA - ckp, err := newCertificateKeyPair(ca.certificate(), ca.key(), "/CN=localhost") + ckp, err := ca.newCertificateKeyPair("/CN=localhost/emailAddress=osbuild@example.com", osbuildClientExt, "") require.NoError(t, err) defer ckp.remove() @@ -95,16 +100,19 @@ func TestKojiAPIAuth(t *testing.T) { cases := []struct { caseDesc string subj string + addext string success bool }{ - {"valid CN 1", "/CN=client.osbuild.org", true}, - {"valid CN 2", "/CN=localhost", true}, - {"invalid CN", "/CN=example.com", false}, + {"valid CN and SAN 1", "/CN=client.osbuild.org/emailAddress=osbuild@example.com", "subjectAltName=DNS:example.com,DNS:client.osbuild.org", true}, + {"valid CN and SAN 2", "/CN=localhost/emailAddress=osbuild@example.com", "subjectAltName=DNS:example.com,DNS:localhost", true}, + {"invalid CN and SAN", "/CN=example.com/emailAddress=osbuild@example.com", "subjectAltName=DNS:example.com", false}, } + authority := &ca{BaseDir: trustedCADir} + for _, c := range cases { t.Run(c.caseDesc, func(t *testing.T) { - ckp, err := newCertificateKeyPair("/etc/osbuild-composer/ca-crt.pem", "/etc/osbuild-composer/ca-key.pem", c.subj) + ckp, err := authority.newCertificateKeyPair(c.subj, osbuildClientExt, c.addext) require.NoError(t, err) defer ckp.remove() @@ -115,12 +123,12 @@ func TestKojiAPIAuth(t *testing.T) { t.Run("certificate signed by an untrusted CA", func(t *testing.T) { // generate a new CA - ca, err := newSelfSignedCertificateKeyPair("/CN=osbuild.org") + ca, err := newCA("/CN=osbuild.org") require.NoError(t, err) defer ca.remove() // create a new certificate and signed it with the new CA - ckp, err := newCertificateKeyPair(ca.certificate(), ca.key(), "/CN=localhost") + ckp, err := ca.newCertificateKeyPair("/CN=localhost/emailAddress=osbuild@example.com", osbuildClientExt, "subjectAltName=DNS:localhost") require.NoError(t, err) defer ckp.remove()