From 6b1d4a3f9731c36c6ba0748699401fbdada53f74 Mon Sep 17 00:00:00 2001 From: Kayra Date: Thu, 4 Apr 2024 13:39:37 +0300 Subject: [PATCH 01/19] skelly --- api/server.go | 20 ++++++++++++++++++++ api/server_test.go | 1 + cmd/gocert/main.go | 31 ++++++++++++++++++++++++++++++- cmd/gocert/main_test.go | 1 + 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 api/server.go create mode 100644 api/server_test.go create mode 100644 cmd/gocert/main_test.go diff --git a/api/server.go b/api/server.go new file mode 100644 index 00000000..fa8299d3 --- /dev/null +++ b/api/server.go @@ -0,0 +1,20 @@ +// Package server provides a server object that represents the GoCert backend +package server + +import ( + "net/http" + "time" +) + +func NewServer(version int) *http.Server { + s := &http.Server{ + Addr: ":8080", + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + MaxHeaderBytes: 1 << 20, + } + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Hello world")) + }) + return s +} diff --git a/api/server_test.go b/api/server_test.go new file mode 100644 index 00000000..abb4e431 --- /dev/null +++ b/api/server_test.go @@ -0,0 +1 @@ +package server diff --git a/cmd/gocert/main.go b/cmd/gocert/main.go index 42d11483..77d0433b 100644 --- a/cmd/gocert/main.go +++ b/cmd/gocert/main.go @@ -1,5 +1,34 @@ package main +import ( + "flag" + "fmt" + "os" + + server "github.com/canonical/gocert/api" + "github.com/canonical/gocert/internal/certdb" +) + func main() { - // ListenAndServe + certPathPtr := flag.String("cert", "", "A path for a certificate file to be used by the webserver") + keyPathPtr := flag.String("key", "", "The path for a private key for the given certificate") + dbPathPtr := flag.String("db", ":memory:", "The path of the SQLite database for the repository") + flag.Parse() + + if *certPathPtr == "" || *keyPathPtr == "" { + fmt.Fprintf(os.Stderr, "Usage: --cert --key ") + os.Exit(1) + } + _, err := certdb.NewCertificateRequestsRepository(*dbPathPtr, "CertificateRequests") + if err != nil { + fmt.Fprintf(os.Stderr, "Couldn't connect to database: %s", err) + os.Exit(1) + } + + srv := server.NewServer(0) + fmt.Fprintf(os.Stdout, "Starting server at %s", srv.Addr) + if err := srv.ListenAndServeTLS(*certPathPtr, *keyPathPtr); err != nil { + fmt.Fprintf(os.Stderr, "Couldn't start server: %s", err) + os.Exit(1) + } } diff --git a/cmd/gocert/main_test.go b/cmd/gocert/main_test.go new file mode 100644 index 00000000..06ab7d0f --- /dev/null +++ b/cmd/gocert/main_test.go @@ -0,0 +1 @@ +package main From a9060da52af473a2505d5fad47e94efe2d82ad38 Mon Sep 17 00:00:00 2001 From: Kayra Date: Thu, 4 Apr 2024 17:15:22 +0300 Subject: [PATCH 02/19] api server initialization part --- api/server.go | 54 ++++++++++++++++++++-- cmd/gocert/main.go | 12 ++++- internal/certificates/certificates.go | 26 +++++++++++ internal/certificates/certificates_test.go | 1 + 4 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 internal/certificates/certificates.go create mode 100644 internal/certificates/certificates_test.go diff --git a/api/server.go b/api/server.go index fa8299d3..d18a87a4 100644 --- a/api/server.go +++ b/api/server.go @@ -2,19 +2,67 @@ package server import ( + "crypto/tls" + "encoding/pem" + "errors" "net/http" "time" + + "github.com/canonical/gocert/internal/certificates" ) -func NewServer(version int) *http.Server { +// formatServerCertificates takes in a certificate and a private key and converts it into a +// format usable by net/http +func formatServerCertificates(certificate, key string) (tls.Certificate, error) { + var serverCerts tls.Certificate + var serverCert []byte + var serverPK []byte + var err error + if certificate != "" && key != "" { + block, _ := pem.Decode([]byte(certificate)) + if block == nil { + return serverCerts, errors.New("PEM Certificate string not found or malformed") + } + if block.Type != "CERTIFICATE" { + return serverCerts, errors.New("given PEM string not a certificate") + } + serverCert = block.Bytes + block, _ = pem.Decode([]byte(key)) + if block == nil { + return serverCerts, errors.New("PEM Private Key string not found or malformed") + } + if block.Type != "PRIVATE KEY" { + return serverCerts, errors.New("given PEM string not a private key") + } + serverPK = block.Bytes + } else { + serverCert, serverPK, err = certificates.GenerateSelfSignedCertificate() + } + serverCerts, err = tls.X509KeyPair(serverCert, serverPK) + if err != nil { + return serverCerts, err + } + return serverCerts, nil +} + +// NewServer creates a new http server with handlers that Go can start listening to +func NewServer(version int, certificate, key string) (*http.Server, error) { + serverCerts, err := formatServerCertificates(certificate, key) + if err != nil { + return nil, err + } s := &http.Server{ - Addr: ":8080", + Addr: ":8080", + ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, + TLSConfig: &tls.Config{ + Certificates: []tls.Certificate{serverCerts}, + }, } http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello world")) }) - return s + return s, nil } diff --git a/cmd/gocert/main.go b/cmd/gocert/main.go index 77d0433b..d48a6c6d 100644 --- a/cmd/gocert/main.go +++ b/cmd/gocert/main.go @@ -3,6 +3,7 @@ package main import ( "flag" "fmt" + "net/http" "os" server "github.com/canonical/gocert/api" @@ -25,10 +26,17 @@ func main() { os.Exit(1) } - srv := server.NewServer(0) + srv, err := server.NewServer(0, *certPathPtr, *keyPathPtr) + if err != nil { + fmt.Fprintf(os.Stderr, "Couldn't create server: %s", err) + os.Exit(1) + } fmt.Fprintf(os.Stdout, "Starting server at %s", srv.Addr) if err := srv.ListenAndServeTLS(*certPathPtr, *keyPathPtr); err != nil { - fmt.Fprintf(os.Stderr, "Couldn't start server: %s", err) + if err == http.ErrServerClosed { + os.Exit(0) + } + fmt.Fprintf(os.Stderr, "Server ran into error: %s", err) os.Exit(1) } } diff --git a/internal/certificates/certificates.go b/internal/certificates/certificates.go new file mode 100644 index 00000000..b150a7e9 --- /dev/null +++ b/internal/certificates/certificates.go @@ -0,0 +1,26 @@ +package certificates + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" +) + +// GenerateCACertificate generates a one time discardable root CA for the running GoCert webserver +func GenerateCACertificate() (x509.Certificate, rsa.PrivateKey, error) { + caCertTemplate := &x509.Certificate{} + caCertPK, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return err + } + caCert, err := x509.CreateCertificate(rand.Reader, caCertTemplate, caCertTemplate, caCertPK.PublicKey, caCertPK) + return caCert, *caCertPK, nil +} + +// GenerateSelfSignedCertificate will create a certificate and pk for GoCert coming from a rootCA +// This certificate and PK is not saved anywhere. +// This certificate and PK should either be saved somewhere, or a real certificate should be provided to GoCert +func GenerateSelfSignedCertificate() ([]byte, []byte, error) { + // rsa.GenerateKey() + // x509.CreateCertificate() +} diff --git a/internal/certificates/certificates_test.go b/internal/certificates/certificates_test.go new file mode 100644 index 00000000..17b4285d --- /dev/null +++ b/internal/certificates/certificates_test.go @@ -0,0 +1 @@ +package certificates_test From 1554c5361fa5cb6b076373d35ea60de8a6c5f813 Mon Sep 17 00:00:00 2001 From: Kayra Date: Thu, 4 Apr 2024 18:38:35 +0300 Subject: [PATCH 03/19] certificate stuff --- api/server.go | 47 ++++++++++++++++-------- cmd/gocert/main.go | 9 ++--- internal/certificates/certificates.go | 53 ++++++++++++++++++++++----- 3 files changed, 79 insertions(+), 30 deletions(-) diff --git a/api/server.go b/api/server.go index d18a87a4..b55d0b2a 100644 --- a/api/server.go +++ b/api/server.go @@ -11,6 +11,28 @@ import ( "github.com/canonical/gocert/internal/certificates" ) +// decodeCertificateAndPK takes in two PEM strings and decodes them into bytes +func decodeCertificateAndPK(certificate, key string) ([]byte, []byte, error) { + block, _ := pem.Decode([]byte(certificate)) + if block == nil { + return nil, nil, errors.New("PEM Certificate string not found or malformed") + } + if block.Type != "CERTIFICATE" { + return nil, nil, errors.New("given PEM string not a certificate") + } + certBytes := block.Bytes + + block, _ = pem.Decode([]byte(key)) + if block == nil { + return nil, nil, errors.New("PEM Private Key string not found or malformed") + } + if block.Type != "RSA PRIVATE KEY" { + return nil, nil, errors.New("given PEM string not a private key") + } + pkBytes := block.Bytes + return certBytes, pkBytes, nil +} + // formatServerCertificates takes in a certificate and a private key and converts it into a // format usable by net/http func formatServerCertificates(certificate, key string) (tls.Certificate, error) { @@ -19,24 +41,19 @@ func formatServerCertificates(certificate, key string) (tls.Certificate, error) var serverPK []byte var err error if certificate != "" && key != "" { - block, _ := pem.Decode([]byte(certificate)) - if block == nil { - return serverCerts, errors.New("PEM Certificate string not found or malformed") - } - if block.Type != "CERTIFICATE" { - return serverCerts, errors.New("given PEM string not a certificate") + serverCert, serverPK, err = decodeCertificateAndPK(certificate, key) + if err != nil { + return serverCerts, err } - serverCert = block.Bytes - block, _ = pem.Decode([]byte(key)) - if block == nil { - return serverCerts, errors.New("PEM Private Key string not found or malformed") + } else { + caCert, caPK, err := certificates.GenerateCACertificate() + if err != nil { + return serverCerts, err } - if block.Type != "PRIVATE KEY" { - return serverCerts, errors.New("given PEM string not a private key") + serverCert, serverPK, err = certificates.GenerateSelfSignedCertificate(caCert, caPK) + if err != nil { + return serverCerts, err } - serverPK = block.Bytes - } else { - serverCert, serverPK, err = certificates.GenerateSelfSignedCertificate() } serverCerts, err = tls.X509KeyPair(serverCert, serverPK) if err != nil { diff --git a/cmd/gocert/main.go b/cmd/gocert/main.go index d48a6c6d..6351d7fe 100644 --- a/cmd/gocert/main.go +++ b/cmd/gocert/main.go @@ -3,7 +3,6 @@ package main import ( "flag" "fmt" - "net/http" "os" server "github.com/canonical/gocert/api" @@ -26,16 +25,14 @@ func main() { os.Exit(1) } - srv, err := server.NewServer(0, *certPathPtr, *keyPathPtr) + // actually read the strings in the file here + srv, err := server.NewServer(0, "", "") if err != nil { fmt.Fprintf(os.Stderr, "Couldn't create server: %s", err) os.Exit(1) } fmt.Fprintf(os.Stdout, "Starting server at %s", srv.Addr) - if err := srv.ListenAndServeTLS(*certPathPtr, *keyPathPtr); err != nil { - if err == http.ErrServerClosed { - os.Exit(0) - } + if err := srv.ListenAndServe(); err != nil { fmt.Fprintf(os.Stderr, "Server ran into error: %s", err) os.Exit(1) } diff --git a/internal/certificates/certificates.go b/internal/certificates/certificates.go index b150a7e9..6ecbc0b0 100644 --- a/internal/certificates/certificates.go +++ b/internal/certificates/certificates.go @@ -4,23 +4,58 @@ import ( "crypto/rand" "crypto/rsa" "crypto/x509" + "crypto/x509/pkix" + "math/big" + "net" + "time" ) // GenerateCACertificate generates a one time discardable root CA for the running GoCert webserver -func GenerateCACertificate() (x509.Certificate, rsa.PrivateKey, error) { - caCertTemplate := &x509.Certificate{} - caCertPK, err := rsa.GenerateKey(rand.Reader, 2048) +func GenerateCACertificate() (*x509.Certificate, *rsa.PrivateKey, error) { + caCert := &x509.Certificate{ + SerialNumber: big.NewInt(2019), + Subject: pkix.Name{ + Organization: []string{"Canonical, INC."}, + Country: []string{"US"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(10, 0, 0), + IsCA: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + } + caPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { - return err + return nil, nil, err } - caCert, err := x509.CreateCertificate(rand.Reader, caCertTemplate, caCertTemplate, caCertPK.PublicKey, caCertPK) - return caCert, *caCertPK, nil + return caCert, caPrivateKey, nil } // GenerateSelfSignedCertificate will create a certificate and pk for GoCert coming from a rootCA // This certificate and PK is not saved anywhere. // This certificate and PK should either be saved somewhere, or a real certificate should be provided to GoCert -func GenerateSelfSignedCertificate() ([]byte, []byte, error) { - // rsa.GenerateKey() - // x509.CreateCertificate() +func GenerateSelfSignedCertificate(caCert *x509.Certificate, caPrivateKey *rsa.PrivateKey) ([]byte, []byte, error) { + cert := &x509.Certificate{ + SerialNumber: big.NewInt(1658), + Subject: pkix.Name{ + Organization: []string{"Canonical, INC."}, + Country: []string{"US"}, + }, + IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback}, // TODO + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(10, 0, 0), + SubjectKeyId: []byte{1, 2, 3, 4, 6}, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature, + } + certPrivateKey, err := rsa.GenerateKey(rand.Reader, 4096) + if err != nil { + return nil, nil, err + } + certBytes, err := x509.CreateCertificate(rand.Reader, cert, caCert, &certPrivateKey.PublicKey, caPrivateKey) + if err != nil { + return nil, nil, err + } + return certBytes, x509.MarshalPKCS1PrivateKey(certPrivateKey), nil } From 9ffefc2f44c13f5b5a10cbc9413ed098776f1d41 Mon Sep 17 00:00:00 2001 From: Kayra Date: Fri, 5 Apr 2024 11:29:45 +0300 Subject: [PATCH 04/19] self signed cert done --- api/server.go | 60 ++++------------- cmd/gocert/main.go | 2 +- internal/certificates/certificates.go | 92 ++++++++++++++++++++++++--- 3 files changed, 98 insertions(+), 56 deletions(-) diff --git a/api/server.go b/api/server.go index b55d0b2a..de44b92d 100644 --- a/api/server.go +++ b/api/server.go @@ -3,68 +3,36 @@ package server import ( "crypto/tls" - "encoding/pem" - "errors" "net/http" "time" "github.com/canonical/gocert/internal/certificates" ) -// decodeCertificateAndPK takes in two PEM strings and decodes them into bytes -func decodeCertificateAndPK(certificate, key string) ([]byte, []byte, error) { - block, _ := pem.Decode([]byte(certificate)) - if block == nil { - return nil, nil, errors.New("PEM Certificate string not found or malformed") - } - if block.Type != "CERTIFICATE" { - return nil, nil, errors.New("given PEM string not a certificate") - } - certBytes := block.Bytes - - block, _ = pem.Decode([]byte(key)) - if block == nil { - return nil, nil, errors.New("PEM Private Key string not found or malformed") - } - if block.Type != "RSA PRIVATE KEY" { - return nil, nil, errors.New("given PEM string not a private key") - } - pkBytes := block.Bytes - return certBytes, pkBytes, nil -} - -// formatServerCertificates takes in a certificate and a private key and converts it into a -// format usable by net/http -func formatServerCertificates(certificate, key string) (tls.Certificate, error) { - var serverCerts tls.Certificate - var serverCert []byte - var serverPK []byte - var err error - if certificate != "" && key != "" { - serverCert, serverPK, err = decodeCertificateAndPK(certificate, key) +// loadServerCertificates takes in a certificate and a private key and determines +// whether to use self signed or given certificates, then returns it in a format +// expected by the server +func loadServerCertificates(certificate, key string) (*tls.Certificate, error) { + if certificate == "" || key == "" { + caCertPEM, caPrivateKeyPEM, err := certificates.GenerateCACertificate() if err != nil { - return serverCerts, err + return nil, err } - } else { - caCert, caPK, err := certificates.GenerateCACertificate() + certificate, key, err = certificates.GenerateSelfSignedCertificate(caCertPEM, caPrivateKeyPEM) if err != nil { - return serverCerts, err - } - serverCert, serverPK, err = certificates.GenerateSelfSignedCertificate(caCert, caPK) - if err != nil { - return serverCerts, err + return nil, err } } - serverCerts, err = tls.X509KeyPair(serverCert, serverPK) + serverCerts, err := tls.X509KeyPair([]byte(certificate), []byte(key)) if err != nil { - return serverCerts, err + return nil, err } - return serverCerts, nil + return &serverCerts, nil } // NewServer creates a new http server with handlers that Go can start listening to func NewServer(version int, certificate, key string) (*http.Server, error) { - serverCerts, err := formatServerCertificates(certificate, key) + serverCerts, err := loadServerCertificates(certificate, key) if err != nil { return nil, err } @@ -75,7 +43,7 @@ func NewServer(version int, certificate, key string) (*http.Server, error) { WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, TLSConfig: &tls.Config{ - Certificates: []tls.Certificate{serverCerts}, + Certificates: []tls.Certificate{*serverCerts}, }, } http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { diff --git a/cmd/gocert/main.go b/cmd/gocert/main.go index 6351d7fe..69b8451d 100644 --- a/cmd/gocert/main.go +++ b/cmd/gocert/main.go @@ -32,7 +32,7 @@ func main() { os.Exit(1) } fmt.Fprintf(os.Stdout, "Starting server at %s", srv.Addr) - if err := srv.ListenAndServe(); err != nil { + if err := srv.ListenAndServeTLS("", ""); err != nil { fmt.Fprintf(os.Stderr, "Server ran into error: %s", err) os.Exit(1) } diff --git a/internal/certificates/certificates.go b/internal/certificates/certificates.go index 6ecbc0b0..841adc0e 100644 --- a/internal/certificates/certificates.go +++ b/internal/certificates/certificates.go @@ -1,17 +1,20 @@ package certificates import ( + "bytes" "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" + "encoding/pem" + "errors" "math/big" "net" "time" ) // GenerateCACertificate generates a one time discardable root CA for the running GoCert webserver -func GenerateCACertificate() (*x509.Certificate, *rsa.PrivateKey, error) { +func GenerateCACertificate() (string, string, error) { caCert := &x509.Certificate{ SerialNumber: big.NewInt(2019), Subject: pkix.Name{ @@ -27,16 +30,42 @@ func GenerateCACertificate() (*x509.Certificate, *rsa.PrivateKey, error) { } caPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { - return nil, nil, err + return "", "", err } - return caCert, caPrivateKey, nil + + caBytes, err := x509.CreateCertificate(rand.Reader, caCert, caCert, &caPrivateKey.PublicKey, caPrivateKey) + if err != nil { + return "", "", err + } + + caCertPEM := new(bytes.Buffer) + pem.Encode(caCertPEM, &pem.Block{ + Type: "CERTIFICATE", + Bytes: caBytes, + }) + + caPrivateKeyPEM := new(bytes.Buffer) + pem.Encode(caPrivateKeyPEM, &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(caPrivateKey), + }) + return caCertPEM.String(), caPrivateKeyPEM.String(), nil } // GenerateSelfSignedCertificate will create a certificate and pk for GoCert coming from a rootCA // This certificate and PK is not saved anywhere. // This certificate and PK should either be saved somewhere, or a real certificate should be provided to GoCert -func GenerateSelfSignedCertificate(caCert *x509.Certificate, caPrivateKey *rsa.PrivateKey) ([]byte, []byte, error) { - cert := &x509.Certificate{ +func GenerateSelfSignedCertificate(caCertPEM, caPrivateKeyPEM string) (string, string, error) { + caCert, err := ParseCertificate(caCertPEM) + if err != nil { + return "", "", nil + } + caPrivateKey, err := ParsePKCS1PrivateKey(caPrivateKeyPEM) + if err != nil { + return "", "", nil + } + + certTemplate := &x509.Certificate{ SerialNumber: big.NewInt(1658), Subject: pkix.Name{ Organization: []string{"Canonical, INC."}, @@ -51,11 +80,56 @@ func GenerateSelfSignedCertificate(caCert *x509.Certificate, caPrivateKey *rsa.P } certPrivateKey, err := rsa.GenerateKey(rand.Reader, 4096) if err != nil { - return nil, nil, err + return "", "", err + } + certBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, &caCert, &certPrivateKey.PublicKey, &caPrivateKey) + if err != nil { + return "", "", err + } + certPEM := new(bytes.Buffer) + pem.Encode(certPEM, &pem.Block{ + Type: "CERTIFICATE", + Bytes: certBytes, + }) + + certPrivateKeyPEM := new(bytes.Buffer) + pem.Encode(certPrivateKeyPEM, &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(certPrivateKey), + }) + return certPEM.String(), certPrivateKeyPEM.String(), nil +} + +// ParseCertificate parses a PEM string into a native x509.Certificate object +func ParseCertificate(certPEM string) (x509.Certificate, error) { + cert := &x509.Certificate{} + block, _ := pem.Decode([]byte(certPEM)) + if block == nil { + return *cert, errors.New("PEM Certificate string not found or malformed") + } + if block.Type != "CERTIFICATE" { + return *cert, errors.New("given PEM string not a certificate") + } + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return *cert, err + } + return *cert, nil +} + +// ParsePrivateKey parses a PEM private key string into a native rsa.PrivateKey object +func ParsePKCS1PrivateKey(pkPEM string) (rsa.PrivateKey, error) { + pk := &rsa.PrivateKey{} + block, _ := pem.Decode([]byte(pkPEM)) + if block == nil { + return *pk, errors.New("PEM private key string not found or malformed") + } + if block.Type != "RSA PRIVATE KEY" { + return *pk, errors.New("given PEM string not an rsa private key") } - certBytes, err := x509.CreateCertificate(rand.Reader, cert, caCert, &certPrivateKey.PublicKey, caPrivateKey) + pk, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { - return nil, nil, err + return *pk, err } - return certBytes, x509.MarshalPKCS1PrivateKey(certPrivateKey), nil + return *pk, nil } From 2fbf8843d5ab1d4886e9d3e98f30fbcb544b4151 Mon Sep 17 00:00:00 2001 From: Kayra Date: Fri, 5 Apr 2024 12:37:40 +0300 Subject: [PATCH 05/19] testing skelly --- api/server.go | 4 +- api/server_test.go | 75 +++++++++++++- cmd/gocert/main.go | 9 +- cmd/gocert/main_test.go | 15 +++ internal/certificates/certificates_test.go | 108 +++++++++++++++++++++ 5 files changed, 202 insertions(+), 9 deletions(-) diff --git a/api/server.go b/api/server.go index de44b92d..6c2ad59d 100644 --- a/api/server.go +++ b/api/server.go @@ -31,7 +31,7 @@ func loadServerCertificates(certificate, key string) (*tls.Certificate, error) { } // NewServer creates a new http server with handlers that Go can start listening to -func NewServer(version int, certificate, key string) (*http.Server, error) { +func NewServer(certificate, key string) (*http.Server, error) { serverCerts, err := loadServerCertificates(certificate, key) if err != nil { return nil, err @@ -47,7 +47,7 @@ func NewServer(version int, certificate, key string) (*http.Server, error) { }, } http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("Hello world")) + w.Write([]byte("Hello World")) }) return s, nil } diff --git a/api/server_test.go b/api/server_test.go index abb4e431..3c0240ff 100644 --- a/api/server_test.go +++ b/api/server_test.go @@ -1 +1,74 @@ -package server +package server_test + +import ( + "testing" + + server "github.com/canonical/gocert/api" +) + +func TestNewServerSuccess(t *testing.T) { + testCases := []struct { + desc string + cert string + key string + }{ + { + desc: "Correct certificate and key", + cert: "Should be a valid cert", + key: "Should be a valid key", + }, + { + desc: "Empty certificate", + cert: "", + key: "Should be a valid key", + }, + { + desc: "Empty key", + cert: "Should be a valid cert", + key: "", + }, + { + desc: "Empty certificate and key", + cert: "", + key: "", + }, + } + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + s, err := server.NewServer(tC.cert, tC.key) + if err != nil { + t.Errorf("Error occured: %s", err) + } + if s.TLSConfig.Certificates == nil { + t.Errorf("No certificates were configured for server") + } + }) + } +} + +func TestNewServerFail(t *testing.T) { + testCases := []struct { + desc string + cert string + key string + }{ + { + desc: "Wrong certificate", + cert: "Should be invalid", + key: "Should be valid", + }, + { + desc: "Wrong key", + cert: "Should be valid", + key: "Should be invalid", + }, + } + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + _, err := server.NewServer(tC.cert, tC.key) + if err != nil { + t.Errorf("Expected error") + } + }) + } +} diff --git a/cmd/gocert/main.go b/cmd/gocert/main.go index 69b8451d..debff8d6 100644 --- a/cmd/gocert/main.go +++ b/cmd/gocert/main.go @@ -12,21 +12,18 @@ import ( func main() { certPathPtr := flag.String("cert", "", "A path for a certificate file to be used by the webserver") keyPathPtr := flag.String("key", "", "The path for a private key for the given certificate") - dbPathPtr := flag.String("db", ":memory:", "The path of the SQLite database for the repository") + dbPathPtr := flag.String("db", "./certs.db", "The path of the SQLite database for the repository") flag.Parse() if *certPathPtr == "" || *keyPathPtr == "" { - fmt.Fprintf(os.Stderr, "Usage: --cert --key ") - os.Exit(1) + fmt.Fprintf(os.Stderr, "Providing a certificate and matching private key is extremely highly recommended. To do so, run: --cert --key . Using self signed certificates.") } _, err := certdb.NewCertificateRequestsRepository(*dbPathPtr, "CertificateRequests") if err != nil { fmt.Fprintf(os.Stderr, "Couldn't connect to database: %s", err) os.Exit(1) } - - // actually read the strings in the file here - srv, err := server.NewServer(0, "", "") + srv, err := server.NewServer(*certPathPtr, *keyPathPtr) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't create server: %s", err) os.Exit(1) diff --git a/cmd/gocert/main_test.go b/cmd/gocert/main_test.go index 06ab7d0f..fdae3f50 100644 --- a/cmd/gocert/main_test.go +++ b/cmd/gocert/main_test.go @@ -1 +1,16 @@ package main + +import "testing" + +func TestMain(t *testing.T) { + // run gocert + // run gocert --cert openable path + // run gocert --key openable path + // run gocert --cert openable path --key openable path --db unopenable path + // run gocert --cert openable path --key openable path --db openable path + // parametrize below + // run gocert --cert unopenable path --key unopenable path + // run gocert --cert unopenable path --key openable path + // run gocert --cert openable path --key unopenable path + // run gocert --cert openable path --key openable path +} diff --git a/internal/certificates/certificates_test.go b/internal/certificates/certificates_test.go index 17b4285d..03d8613f 100644 --- a/internal/certificates/certificates_test.go +++ b/internal/certificates/certificates_test.go @@ -1 +1,109 @@ package certificates_test + +import ( + "testing" + + "github.com/canonical/gocert/internal/certificates" +) + +func TestGenerateSelfSignedCertificateSuccess(t *testing.T) { + testCases := []struct { + desc string + }{ + { + desc: "", + }, + } + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + + }) + } +} + +func TestGenerateSelfSignedCertificateFail(t *testing.T) { + testCases := []struct { + desc string + }{ + { + desc: "", + }, + } + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + + }) + } +} + +func TestParseCertificateSuccess(t *testing.T) { + testCases := []struct { + desc string + }{ + { + desc: "", + }, + } + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + + }) + } +} + +func TestParseCertificateFail(t *testing.T) { + testCases := []struct { + desc string + }{ + { + desc: "", + }, + } + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + + }) + } +} + +func TestParsePKCS1PrivateKeySuccess(t *testing.T) { + testCases := []struct { + desc string + }{ + { + desc: "", + }, + } + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + + }) + } +} + +func TestParsePKCS1PrivateKeyFail(t *testing.T) { + testCases := []struct { + desc string + }{ + { + desc: "", + }, + } + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + + }) + } +} +func TestGenerateCACertificateSuccess(t *testing.T) { + caCertPEM, caPKPEM, err := certificates.GenerateCACertificate() + if err != nil { + t.Fatalf("could not generate CA cert and PK") + } + if _, err := certificates.ParseCertificate(caCertPEM); err != nil { + t.Fatalf("generate CA cert cannot be parsed") + } + if _, err := certificates.ParsePKCS1PrivateKey(caPKPEM); err != nil { + t.Fatalf("generate CA private key cannot be parsed") + } +} From 45fadfc608313e779f9a0931de2b5bfc586d2e74 Mon Sep 17 00:00:00 2001 From: Kayra Date: Fri, 5 Apr 2024 13:36:53 +0300 Subject: [PATCH 06/19] certificate test done --- cmd/gocert/main_test.go | 8 +- internal/certificates/certificates_test.go | 286 ++++++++++++++++----- 2 files changed, 234 insertions(+), 60 deletions(-) diff --git a/cmd/gocert/main_test.go b/cmd/gocert/main_test.go index fdae3f50..89e4cffe 100644 --- a/cmd/gocert/main_test.go +++ b/cmd/gocert/main_test.go @@ -1,8 +1,12 @@ package main -import "testing" +import ( + "testing" +) -func TestMain(t *testing.T) { +func TestGoCert(t *testing.T) { + // os.Args = []string{"gocert"} + // main() // run gocert // run gocert --cert openable path // run gocert --key openable path diff --git a/internal/certificates/certificates_test.go b/internal/certificates/certificates_test.go index 03d8613f..085a8f75 100644 --- a/internal/certificates/certificates_test.go +++ b/internal/certificates/certificates_test.go @@ -1,97 +1,226 @@ package certificates_test import ( + "fmt" + "strings" "testing" "github.com/canonical/gocert/internal/certificates" ) -func TestGenerateSelfSignedCertificateSuccess(t *testing.T) { - testCases := []struct { - desc string - }{ - { - desc: "", - }, - } - for _, tC := range testCases { - t.Run(tC.desc, func(t *testing.T) { +const ( + ValidCert1 = `-----BEGIN CERTIFICATE----- +MIIDKzCCAhOgAwIBAgICB+MwDQYJKoZIhvcNAQELBQAwJzELMAkGA1UEBhMCVVMx +GDAWBgNVBAoTD0Nhbm9uaWNhbCwgSU5DLjAeFw0yNDA0MDUxMDAzMjhaFw0zNDA0 +MDUxMDAzMjhaMCcxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9DYW5vbmljYWwsIElO +Qy4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGiemPRVW7GhnSmbXh +PJz90+mvByoyYIaw+ILlcCvkJolYP4DzO5upME8mBQY8F+YDh0XfC7bGIMyK3XHL +wivglUXU4i3VzpOrwZ4THomCKroTE9o5X0rpS4xjILXRNLOE3Je2GqGEpQYS8ad0 +wHsmdgBACYw2skdgXjqbDkr6y2J1QHG8TCQfwijLpZQJhBFflAT1wm4gl+AKSpTj +isrmsxFJpvlJI9RnhhEY8pDsrUruiwZ6pJ36p79e0r8m+6Rs36fYx7FsTRI5VBCR +H9WvyA2W1xukEh1T0KB3Sy8Omb8ySNp7H5/9/wH4HZQy4tfpQt3MA6kretL2Xp0G +AodZAgMBAAGjYTBfMA4GA1UdDwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcD +AgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUYEQDVgZidxvS +pph/+NWsxWRVA20wDQYJKoZIhvcNAQELBQADggEBAIJb2BeiquR05bDh2t831BCq +EteqhgQoKclZXjCYYyWNGaClXngIHOJhx4WVsNIhRJmscdbJFWbBm0z6UQe0kLU3 +6cAQJlOy/7u5I6jIjyETLqLP8SgVaPuCEWyX82LVRm87OjzTZSQqhTh/gMpU/OdK +BwFENLc7LcWQ1eqBeeBjP5HhaC2BtSENVHBh81mKpR4mQBOoKq71XszSuqTQPkE5 +x/OFl3VkxM8x1aAdSK/V5UR8IlIbDW/dDQwsPoPBr5K/YuxUnm0GD6Ycqz7k/4F/ +R6grKY9bPkxtGvwIw1BqM7sqJ77knkLXRj4mH3J6znjY9dVLSKRjcZLMBtKhlr0= +-----END CERTIFICATE-----` + ValidCert2 = `-----BEGIN CERTIFICATE----- +MIIELjCCAxagAwIBAgICBnowDQYJKoZIhvcNAQELBQAwJzELMAkGA1UEBhMCVVMx +GDAWBgNVBAoTD0Nhbm9uaWNhbCwgSU5DLjAeFw0yNDA0MDUxMDAzMjhaFw0zNDA0 +MDUxMDAzMjhaMCcxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9DYW5vbmljYWwsIElO +Qy4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDAP98jcfNw40HbS1xR +6UpSQTp4AGldFWQZBOFaVzD+eh7sYM/BFdT0dZRHGjXxL77ewDbwdwAFJ5zuxo+u +8/VgKGRpK6KCnKailmVrdRDhA45airMRQN6QXurN4NZgXcCHJWGAQKA9XJzcwGJF +l5LxoFY58wCv0d1JP8fgmbcgIRQTCIvhrlgrJ5Acz9QP6BuaxEHKbYYvWyTWtAhi +HS/w51yEbh6959ceJGBDZPyEVd9sfGipvHrA73+33+XBluRcUuWV4dCecyP/m+8C +jTBmW5s8gS6JUDE8yl99qm7CnXTkNDqPXThrorcKRwcHrw3ZEOm5rUPLuyzGBx/C +DZUbY9bsvHJMHOHlbwiY+M2MFIO+3H6qyfPfcHs8NFkrZh/as+9hrEzSYcz+tGBi +NynkSmNPQi4yzT00ilKYgcBhPdDDlBbdhcmdeFA3XE880VkQdJgefsYpCgYRdILm +DDd6ZMfZsQOJjuRC8rQKLO+z1X5JhiOlkNxZaOkq9b9eu7230rxTFCGocn0l9oKw +0q8OIDOTb7UKdIaGq/y++uRxe0hhNoijN1OJvh+R3/KGuztu5Y8ejksIxKBrUqCg +bUDXmQ82xbdJ36qF+NHBqFqFaKhH1XuK6eAIfqgQam/u9HNZZw3mOdm9rvIZfwIT +F9gvSwm1bxzyIHL/zWOgyfzckQIDAQABo2QwYjAOBgNVHQ8BAf8EBAMCB4AwHQYD +VR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA4GA1UdDgQHBAUBAgMEBjAhBgNV +HREEGjAYhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IB +AQB4UEu1/vTpEuuwoqgFpp8tEMewwBQ/CXPBN5seDnd/SUMXFrxk58f498qI3FQy +q98a+89jPWRGA5LY+DfIS82NYCwbKuvTzuJRoUpMPbebrhu7OQl7qQT6n8VOCy6x +IaRnPI0zEGbg2v340jMbB26FiyaFKyHEc24nnq3suZFmbslXzRE2Ebut+Qtft8he +0pSNQXtz5ULt0c8DTje7j+mRABzus45cj3HMDO4vcVRrHegdTE8YcZjwAFTKxqpg +W7GwJ5qPjnm6EMe8da55m8Q0hZchwGZreXNG7iCaw98pACBNgOOxh4LOhEZy25Bv +ayrvWnmPfg1u47sduuhHeUid +-----END CERTIFICATE-----` + ValidPK1 = `-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAxonpj0VVuxoZ0pm14Tyc/dPprwcqMmCGsPiC5XAr5CaJWD+A +8zubqTBPJgUGPBfmA4dF3wu2xiDMit1xy8Ir4JVF1OIt1c6Tq8GeEx6Jgiq6ExPa +OV9K6UuMYyC10TSzhNyXthqhhKUGEvGndMB7JnYAQAmMNrJHYF46mw5K+stidUBx +vEwkH8Ioy6WUCYQRX5QE9cJuIJfgCkqU44rK5rMRSab5SSPUZ4YRGPKQ7K1K7osG +eqSd+qe/XtK/JvukbN+n2MexbE0SOVQQkR/Vr8gNltcbpBIdU9Cgd0svDpm/Mkja +ex+f/f8B+B2UMuLX6ULdzAOpK3rS9l6dBgKHWQIDAQABAoIBAQCJcpLSUCLw/Dni +Ve3Xt/nLtDi0ppYs+CxnOjSMmOKZ+Z/eC2C/g4XZVIuG+7V8RuNDkBPsPZTUh4Jz +pKkSciOkQFNu9QLcYT5Uix9fhyWVivT1HipWy8T1zAqt2chlxEF/+qPBO6CUTxbs +aIYQyuy3DLxRmQqMF1JYwgN6syNvicQ0rbpsX0/svmpaKEd+YLUy8l06MtSPnj0N +RWoUm2pWbCAFzHfeMUjTbyjeIQgbok1HGFtxuaO25TjPFB+MtZKwIe90NYFasCDO +GCR0wPsgFCzygi/68CycCfwSfQUW/xIQznpa69ifwA0Zwpf1GWeAClmN+/3vP3sI +dMXk9GPNAoGBAN1+MqbPmRNwMsEBZrzprsYF78lUB28iPn9HgbeXir5NpelzJxjh +oL0BRl9TmTgSFPS4baKMrLJPjPP0xk330E195/14lEf29i3Zis5W0d5UN9J3nta3 +nx1iJ8RZmyyPRHb0KEjftSp3nR68OTAQa9oCxKVsf8IsxhxfZM3bQiFrAoGBAOV4 +Ow4hXHhUhjUz86QKTnA8fWgkKL9HAc9r/EOzPGC0Ng39mS867NoV2cqfuHni3r+X +edlNO9dI/WZF52nQ9q1WNUEcZBuqeI07bVGddgtwQ3jUJbVuifG35qSmF2FfryXb +2GhXmVxsnwMkgwuAF57+QQC1kF9W95ioRsN4zndLAoGAZs5Tjk+fWoFiXWlcGWVR +xQIuaUFCbhfz8DntgJyrPmdmEfRr+kWHyRKValuwK3FhHrGX2bH32o/H+dfsT3Yt +zjoZevIDyV9cpq1pmxp7MPngKyVwqXLzPL05fg6lUspw2dG7/Q8w1LROTlzJIoEM +vlU4lxvENQl5LuQsMsyJZl8CgYA/8S7TkyxHQ5ZaQO3FajHNSK3RVTIditXQTNDw +tINAlzbw0xfad1sEsk4MDlHDdRqI3NbRXJouNWKXGyeO7vGt/3W7fQPQScJp6INf +2LbKHTBP/R283t6Fgq88diPRuG3/6LD28mIDxSjSIVx62ei7HzJR1kYyqvM3kLyX +P5SY3QKBgQC0oL1GX4iteb9uzfGsknrlYf9Eid9zLbc7MFEZ+JdQECNUrqAg6vt3 +0X3rDNjY2R0UZEC14WSx+Ax7xuSrozEtnuU6dBMv9wqCpJ36go+1TPwoe29uxJa5 +AqfiZgKPTXDLu0RPIeHUwyygzauy9FfeMGP+YjmGpJdONIDXf1npqg== +-----END RSA PRIVATE KEY-----` + ValidPK2 = `-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY-----` +) - }) +func TestParseCertificateSuccess(t *testing.T) { + cert, err := certificates.ParseCertificate(ValidCert1) + if err != nil { + t.Fatalf("couldn't parse certificate: %s", err) + } + if cert.Issuer.Organization[0] != "Canonical, INC." { + t.Fatalf("certificate was parsed incorrectly") } } -func TestGenerateSelfSignedCertificateFail(t *testing.T) { - testCases := []struct { - desc string +func TestParseCertificateFail(t *testing.T) { + var wrongString = "this is a real cert!!!" + var wrongStringErr = "PEM Certificate string not found or malformed" + var ValidCertWithoutWhitespace = strings.ReplaceAll(ValidCert1, "\n", "") + var ValidCertWithoutWhitespaceErr = "PEM Certificate string not found or malformed" + var wrongPemType = strings.ReplaceAll(ValidCert1, "CERTIFICATE", "SOME RANDOM PEM TYPE") + var wrongPemTypeErr = "given PEM string not a certificate" + + cases := []struct { + input string + expectedErr string }{ { - desc: "", + input: wrongString, + expectedErr: wrongStringErr, }, - } - for _, tC := range testCases { - t.Run(tC.desc, func(t *testing.T) { - - }) - } -} - -func TestParseCertificateSuccess(t *testing.T) { - testCases := []struct { - desc string - }{ { - desc: "", + input: ValidCertWithoutWhitespace, + expectedErr: ValidCertWithoutWhitespaceErr, }, - } - for _, tC := range testCases { - t.Run(tC.desc, func(t *testing.T) { - - }) - } -} - -func TestParseCertificateFail(t *testing.T) { - testCases := []struct { - desc string - }{ { - desc: "", + input: wrongPemType, + expectedErr: wrongPemTypeErr, }, } - for _, tC := range testCases { - t.Run(tC.desc, func(t *testing.T) { + for i, c := range cases { + t.Run(fmt.Sprintf("InvalidCert%d", i), func(t *testing.T) { + _, err := certificates.ParseCertificate(c.input) + if err.Error() != c.expectedErr { + t.Errorf("Expected error not found:\nReceived: %s\nExpected: %s", err, c.expectedErr) + } }) } } func TestParsePKCS1PrivateKeySuccess(t *testing.T) { - testCases := []struct { - desc string - }{ - { - desc: "", - }, + pk, err := certificates.ParsePKCS1PrivateKey(ValidPK1) + if err != nil { + t.Fatalf("couldn't parse private key: %s", err) } - for _, tC := range testCases { - t.Run(tC.desc, func(t *testing.T) { - - }) + if pk.E != 65537 { + t.Fatalf("private key was parsed incorrectly") } } func TestParsePKCS1PrivateKeyFail(t *testing.T) { - testCases := []struct { - desc string + var wrongString = "this is a real pk!!!" + var wrongStringErr = "PEM private key string not found or malformed" + var ValidCertWithoutWhitespace = strings.ReplaceAll(ValidCert1, "\n", "") + var ValidCertWithoutWhitespaceErr = "PEM private key string not found or malformed" + var wrongPemType = strings.ReplaceAll(ValidCert1, "RSA PRIVATE KEY", "SOME RANDOM PEM TYPE") + var wrongPemTypeErr = "given PEM string not an rsa private key" + + cases := []struct { + input string + expectedErr string }{ { - desc: "", + input: wrongString, + expectedErr: wrongStringErr, + }, + { + input: ValidCertWithoutWhitespace, + expectedErr: ValidCertWithoutWhitespaceErr, + }, + { + input: wrongPemType, + expectedErr: wrongPemTypeErr, }, } - for _, tC := range testCases { - t.Run(tC.desc, func(t *testing.T) { + for i, c := range cases { + t.Run(fmt.Sprintf("InvalidPK%d", i), func(t *testing.T) { + _, err := certificates.ParsePKCS1PrivateKey(c.input) + if err.Error() != c.expectedErr { + t.Errorf("Expected error not found:\nReceived: %s\nExpected: %s", err, c.expectedErr) + } }) } } @@ -107,3 +236,44 @@ func TestGenerateCACertificateSuccess(t *testing.T) { t.Fatalf("generate CA private key cannot be parsed") } } + +func TestGenerateSelfSignedCertificateSuccess(t *testing.T) { + caCertPEM, caPKPEM, _ := certificates.GenerateCACertificate() + cert, pk, genErr := certificates.GenerateSelfSignedCertificate(caCertPEM, caPKPEM) + _, certErr := certificates.ParseCertificate(cert) + _, keyErr := certificates.ParsePKCS1PrivateKey(pk) + if genErr != nil || certErr != nil || keyErr != nil { + t.Fatalf("couldn't generate self signed certificate") + } +} + +func TestGenerateSelfSignedCertificateFail(t *testing.T) { + var nonMatchingCertPKErr = "x509: provided PrivateKey doesn't match parent's PublicKey" + + cases := []struct { + caCert string + caPK string + expectedErr string + }{ + { + caCert: ValidCert1, + caPK: ValidPK2, + expectedErr: nonMatchingCertPKErr, + }, + // Help: Why is this passing? + // { + // caCert: ValidCert2, + // caPK: ValidPK2, + // expectedErr: notCAErr, + // }, + } + + for i, c := range cases { + t.Run(fmt.Sprintf("SelfSignedCertificateFails%d", i), func(t *testing.T) { + _, _, err := certificates.GenerateSelfSignedCertificate(c.caCert, c.caPK) + if err.Error() != c.expectedErr { + t.Errorf("Expected error not found:\nReceived: %s\nExpected: %s", err, c.expectedErr) + } + }) + } +} From 24f9d3272e6bfed77bb50fd74b73691cb039c3c7 Mon Sep 17 00:00:00 2001 From: Kayra Date: Fri, 5 Apr 2024 14:01:22 +0300 Subject: [PATCH 07/19] server tests done --- api/server.go | 9 ++-- api/server_test.go | 109 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 100 insertions(+), 18 deletions(-) diff --git a/api/server.go b/api/server.go index 6c2ad59d..ef3462af 100644 --- a/api/server.go +++ b/api/server.go @@ -36,18 +36,21 @@ func NewServer(certificate, key string) (*http.Server, error) { if err != nil { return nil, err } + m := http.NewServeMux() + m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Hello World")) + }) s := &http.Server{ Addr: ":8080", ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, + Handler: m, MaxHeaderBytes: 1 << 20, TLSConfig: &tls.Config{ Certificates: []tls.Certificate{*serverCerts}, }, } - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("Hello World")) - }) + return s, nil } diff --git a/api/server_test.go b/api/server_test.go index 3c0240ff..4b2075fa 100644 --- a/api/server_test.go +++ b/api/server_test.go @@ -6,6 +6,85 @@ import ( server "github.com/canonical/gocert/api" ) +const ( + ValidCert = `-----BEGIN CERTIFICATE----- +MIIELjCCAxagAwIBAgICBnowDQYJKoZIhvcNAQELBQAwJzELMAkGA1UEBhMCVVMx +GDAWBgNVBAoTD0Nhbm9uaWNhbCwgSU5DLjAeFw0yNDA0MDUxMDAzMjhaFw0zNDA0 +MDUxMDAzMjhaMCcxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9DYW5vbmljYWwsIElO +Qy4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDAP98jcfNw40HbS1xR +6UpSQTp4AGldFWQZBOFaVzD+eh7sYM/BFdT0dZRHGjXxL77ewDbwdwAFJ5zuxo+u +8/VgKGRpK6KCnKailmVrdRDhA45airMRQN6QXurN4NZgXcCHJWGAQKA9XJzcwGJF +l5LxoFY58wCv0d1JP8fgmbcgIRQTCIvhrlgrJ5Acz9QP6BuaxEHKbYYvWyTWtAhi +HS/w51yEbh6959ceJGBDZPyEVd9sfGipvHrA73+33+XBluRcUuWV4dCecyP/m+8C +jTBmW5s8gS6JUDE8yl99qm7CnXTkNDqPXThrorcKRwcHrw3ZEOm5rUPLuyzGBx/C +DZUbY9bsvHJMHOHlbwiY+M2MFIO+3H6qyfPfcHs8NFkrZh/as+9hrEzSYcz+tGBi +NynkSmNPQi4yzT00ilKYgcBhPdDDlBbdhcmdeFA3XE880VkQdJgefsYpCgYRdILm +DDd6ZMfZsQOJjuRC8rQKLO+z1X5JhiOlkNxZaOkq9b9eu7230rxTFCGocn0l9oKw +0q8OIDOTb7UKdIaGq/y++uRxe0hhNoijN1OJvh+R3/KGuztu5Y8ejksIxKBrUqCg +bUDXmQ82xbdJ36qF+NHBqFqFaKhH1XuK6eAIfqgQam/u9HNZZw3mOdm9rvIZfwIT +F9gvSwm1bxzyIHL/zWOgyfzckQIDAQABo2QwYjAOBgNVHQ8BAf8EBAMCB4AwHQYD +VR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA4GA1UdDgQHBAUBAgMEBjAhBgNV +HREEGjAYhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IB +AQB4UEu1/vTpEuuwoqgFpp8tEMewwBQ/CXPBN5seDnd/SUMXFrxk58f498qI3FQy +q98a+89jPWRGA5LY+DfIS82NYCwbKuvTzuJRoUpMPbebrhu7OQl7qQT6n8VOCy6x +IaRnPI0zEGbg2v340jMbB26FiyaFKyHEc24nnq3suZFmbslXzRE2Ebut+Qtft8he +0pSNQXtz5ULt0c8DTje7j+mRABzus45cj3HMDO4vcVRrHegdTE8YcZjwAFTKxqpg +W7GwJ5qPjnm6EMe8da55m8Q0hZchwGZreXNG7iCaw98pACBNgOOxh4LOhEZy25Bv +ayrvWnmPfg1u47sduuhHeUid +-----END CERTIFICATE-----` + ValidPK = `-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY-----` +) + func TestNewServerSuccess(t *testing.T) { testCases := []struct { desc string @@ -13,22 +92,22 @@ func TestNewServerSuccess(t *testing.T) { key string }{ { - desc: "Correct certificate and key", - cert: "Should be a valid cert", - key: "Should be a valid key", + desc: "correct certificate and key", + cert: ValidCert, + key: ValidPK, }, { - desc: "Empty certificate", + desc: "empty certificate", cert: "", - key: "Should be a valid key", + key: ValidPK, }, { - desc: "Empty key", - cert: "Should be a valid cert", + desc: "empty key", + cert: ValidCert, key: "", }, { - desc: "Empty certificate and key", + desc: "empty certificate and key", cert: "", key: "", }, @@ -53,20 +132,20 @@ func TestNewServerFail(t *testing.T) { key string }{ { - desc: "Wrong certificate", - cert: "Should be invalid", - key: "Should be valid", + desc: "wrong certificate", + cert: "some cert", + key: ValidPK, }, { - desc: "Wrong key", - cert: "Should be valid", - key: "Should be invalid", + desc: "wrong key", + cert: ValidCert, + key: "some pk", }, } for _, tC := range testCases { t.Run(tC.desc, func(t *testing.T) { _, err := server.NewServer(tC.cert, tC.key) - if err != nil { + if err == nil { t.Errorf("Expected error") } }) From 1cb3b441a2728f50be4221e89565a42181aa6ef0 Mon Sep 17 00:00:00 2001 From: Kayra Date: Fri, 5 Apr 2024 14:32:52 +0300 Subject: [PATCH 08/19] more tests --- api/server_test.go | 8 ++++++++ cmd/gocert/main_test.go | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/api/server_test.go b/api/server_test.go index 4b2075fa..2517993b 100644 --- a/api/server_test.go +++ b/api/server_test.go @@ -151,3 +151,11 @@ func TestNewServerFail(t *testing.T) { }) } } + +func Example() { + ts, err := server.NewServer(ValidCert, ValidPK) + if err != nil { + return + } + ts.ListenAndServeTLS("", "") +} diff --git a/cmd/gocert/main_test.go b/cmd/gocert/main_test.go index 89e4cffe..5552a216 100644 --- a/cmd/gocert/main_test.go +++ b/cmd/gocert/main_test.go @@ -4,9 +4,9 @@ import ( "testing" ) -func TestGoCert(t *testing.T) { +func TestGoCertCLI(t *testing.T) { // os.Args = []string{"gocert"} - // main() + // go main() // run gocert // run gocert --cert openable path // run gocert --key openable path From 3b4b5906390ade7c34f9d4604c676e1b31356f1d Mon Sep 17 00:00:00 2001 From: Kayra Date: Fri, 5 Apr 2024 14:39:37 +0300 Subject: [PATCH 09/19] lint --- api/server.go | 5 ++++- api/server_test.go | 8 -------- internal/certificates/certificates.go | 21 ++++++++++++++++----- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/api/server.go b/api/server.go index ef3462af..d3c8d64e 100644 --- a/api/server.go +++ b/api/server.go @@ -38,7 +38,10 @@ func NewServer(certificate, key string) (*http.Server, error) { } m := http.NewServeMux() m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("Hello World")) + _, err := w.Write([]byte("Hello World")) + if err != nil { + return + } }) s := &http.Server{ Addr: ":8080", diff --git a/api/server_test.go b/api/server_test.go index 2517993b..4b2075fa 100644 --- a/api/server_test.go +++ b/api/server_test.go @@ -151,11 +151,3 @@ func TestNewServerFail(t *testing.T) { }) } } - -func Example() { - ts, err := server.NewServer(ValidCert, ValidPK) - if err != nil { - return - } - ts.ListenAndServeTLS("", "") -} diff --git a/internal/certificates/certificates.go b/internal/certificates/certificates.go index 841adc0e..9e7f51dc 100644 --- a/internal/certificates/certificates.go +++ b/internal/certificates/certificates.go @@ -39,16 +39,22 @@ func GenerateCACertificate() (string, string, error) { } caCertPEM := new(bytes.Buffer) - pem.Encode(caCertPEM, &pem.Block{ + err = pem.Encode(caCertPEM, &pem.Block{ Type: "CERTIFICATE", Bytes: caBytes, }) + if err != nil { + return "", "", err + } caPrivateKeyPEM := new(bytes.Buffer) - pem.Encode(caPrivateKeyPEM, &pem.Block{ + err = pem.Encode(caPrivateKeyPEM, &pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(caPrivateKey), }) + if err != nil { + return "", "", err + } return caCertPEM.String(), caPrivateKeyPEM.String(), nil } @@ -87,16 +93,21 @@ func GenerateSelfSignedCertificate(caCertPEM, caPrivateKeyPEM string) (string, s return "", "", err } certPEM := new(bytes.Buffer) - pem.Encode(certPEM, &pem.Block{ + err = pem.Encode(certPEM, &pem.Block{ Type: "CERTIFICATE", Bytes: certBytes, }) - + if err != nil { + return "", "", err + } certPrivateKeyPEM := new(bytes.Buffer) - pem.Encode(certPrivateKeyPEM, &pem.Block{ + err = pem.Encode(certPrivateKeyPEM, &pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(certPrivateKey), }) + if err != nil { + return "", "", err + } return certPEM.String(), certPrivateKeyPEM.String(), nil } From d772c17c341572f9a0047339741f3670db6b0273 Mon Sep 17 00:00:00 2001 From: Kayra Date: Mon, 15 Apr 2024 14:24:05 +0300 Subject: [PATCH 10/19] move to config.yaml --- api/server.go | 85 ++++--- api/server_test.go | 109 +++++--- cmd/gocert/main.go | 16 +- go.mod | 2 + go.sum | 3 + internal/certificates/certificates.go | 146 ----------- internal/certificates/certificates_test.go | 279 --------------------- 7 files changed, 140 insertions(+), 500 deletions(-) delete mode 100644 internal/certificates/certificates.go delete mode 100644 internal/certificates/certificates_test.go diff --git a/api/server.go b/api/server.go index d3c8d64e..ee0ec606 100644 --- a/api/server.go +++ b/api/server.go @@ -3,57 +3,84 @@ package server import ( "crypto/tls" + "errors" "net/http" + "os" "time" - "github.com/canonical/gocert/internal/certificates" + "gopkg.in/yaml.v3" ) -// loadServerCertificates takes in a certificate and a private key and determines -// whether to use self signed or given certificates, then returns it in a format -// expected by the server -func loadServerCertificates(certificate, key string) (*tls.Certificate, error) { - if certificate == "" || key == "" { - caCertPEM, caPrivateKeyPEM, err := certificates.GenerateCACertificate() - if err != nil { - return nil, err - } - certificate, key, err = certificates.GenerateSelfSignedCertificate(caCertPEM, caPrivateKeyPEM) - if err != nil { - return nil, err - } - } - serverCerts, err := tls.X509KeyPair([]byte(certificate), []byte(key)) +type ConfigYAML struct { + KeyPath string + CertPath string + DBPath string +} + +type Config struct { + Key []byte + Cert []byte + DBPath string +} + +func ValidateConfigFile(filePath string) (Config, error) { + validationErr := errors.New("config file validation failed: ") + config := Config{} + configYaml, err := os.ReadFile(filePath) if err != nil { - return nil, err + return config, errors.Join(validationErr, err) } - return &serverCerts, nil + c := ConfigYAML{} + if err := yaml.Unmarshal(configYaml, &c); err != nil { + return config, errors.Join(validationErr, err) + } + cert, err := os.ReadFile(c.CertPath) + if err != nil { + return config, errors.Join(validationErr, err) + } + key, err := os.ReadFile(c.KeyPath) + if err != nil { + return config, errors.Join(validationErr, err) + } + if _, err := os.OpenFile(c.DBPath, os.O_CREATE|os.O_RDONLY, 0644); err != nil { + return config, errors.Join(validationErr, err) + } + config.Cert = cert + config.Key = key + config.DBPath = c.DBPath + return config, nil } // NewServer creates a new http server with handlers that Go can start listening to -func NewServer(certificate, key string) (*http.Server, error) { - serverCerts, err := loadServerCertificates(certificate, key) +func NewServer(certificate, key []byte) (*http.Server, error) { + serverCerts, err := tls.X509KeyPair(certificate, key) if err != nil { return nil, err } - m := http.NewServeMux() - m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - _, err := w.Write([]byte("Hello World")) - if err != nil { - return - } - }) + router := http.NewServeMux() + router.HandleFunc("/", HelloWorld) + + v1 := http.NewServeMux() + v1.Handle("/v1/", http.StripPrefix("/v1", router)) + s := &http.Server{ Addr: ":8080", ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, - Handler: m, + Handler: v1, MaxHeaderBytes: 1 << 20, TLSConfig: &tls.Config{ - Certificates: []tls.Certificate{*serverCerts}, + Certificates: []tls.Certificate{serverCerts}, }, } return s, nil } + +func HelloWorld(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte("Hello World")) + if err != nil { + return + } +} diff --git a/api/server_test.go b/api/server_test.go index 4b2075fa..97afeefb 100644 --- a/api/server_test.go +++ b/api/server_test.go @@ -1,13 +1,15 @@ package server_test import ( + "log" + "os" "testing" server "github.com/canonical/gocert/api" ) const ( - ValidCert = `-----BEGIN CERTIFICATE----- + validCert = `-----BEGIN CERTIFICATE----- MIIELjCCAxagAwIBAgICBnowDQYJKoZIhvcNAQELBQAwJzELMAkGA1UEBhMCVVMx GDAWBgNVBAoTD0Nhbm9uaWNhbCwgSU5DLjAeFw0yNDA0MDUxMDAzMjhaFw0zNDA0 MDUxMDAzMjhaMCcxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9DYW5vbmljYWwsIElO @@ -32,7 +34,7 @@ IaRnPI0zEGbg2v340jMbB26FiyaFKyHEc24nnq3suZFmbslXzRE2Ebut+Qtft8he W7GwJ5qPjnm6EMe8da55m8Q0hZchwGZreXNG7iCaw98pACBNgOOxh4LOhEZy25Bv ayrvWnmPfg1u47sduuhHeUid -----END CERTIFICATE-----` - ValidPK = `-----BEGIN RSA PRIVATE KEY----- + validPK = `-----BEGIN RSA PRIVATE KEY----- MIIJKQIBAAKCAgEAwD/fI3HzcONB20tcUelKUkE6eABpXRVkGQThWlcw/noe7GDP wRXU9HWURxo18S++3sA28HcABSec7saPrvP1YChkaSuigpymopZla3UQ4QOOWoqz EUDekF7qzeDWYF3AhyVhgECgPVyc3MBiRZeS8aBWOfMAr9HdST/H4Jm3ICEUEwiL @@ -83,71 +85,100 @@ Q53tuiWQeoxNOjHiWstBPELxGbW6447JyVVbNYGUk+VFU7okzA6sRTJ/5Ysda4Sf auNQc2hruhr/2plhFUYoZHPzGz7d5zUGKymhCoS8BsFVtD0WDL4srdtY/W2Us7TD D7DC34n8CH9+avz9sCRwxpjxKnYW/BeyK0c4n9uZpjI8N4sOVqy6yWBUseww -----END RSA PRIVATE KEY-----` + validConfig = `keypath: "./key_test.pem" +certpath: "./cert_test.pem" +dbpath: "./certs.db"` + invalidYAMLConfig = `wrong: fields +every: where` + invalidFileConfig = `keypath: "./nokeyfile.pem" +certpath: "./nocertfile.pem" +dbpath: "./certs.db"` ) +func TestMain(m *testing.M) { + testfolder, err := os.MkdirTemp("./", "configtest-") + if err != nil { + log.Fatalf("couldn't create temp directory") + } + writeConfigErr := os.WriteFile(testfolder+"/config.yaml", []byte(validConfig), 0644) + writeCertErr := os.WriteFile(testfolder+"/cert_test.pem", []byte(validCert), 0644) + writeKeyErr := os.WriteFile(testfolder+"/key_test.pem", []byte(validPK), 0644) + if writeConfigErr != nil || writeCertErr != nil || writeKeyErr != nil { + log.Fatalf("couldn't create temp testing file") + } + if err := os.Chdir(testfolder); err != nil { + log.Fatalf("couldn't enter testing directory") + } + + exitval := m.Run() + + if err := os.Chdir("../"); err != nil { + log.Fatalf("couldn't change back to parent directory") + } + if err := os.RemoveAll(testfolder); err != nil { + log.Fatalf("couldn't remove temp testing directory") + } + os.Exit(exitval) +} + func TestNewServerSuccess(t *testing.T) { + s, err := server.NewServer([]byte(validCert), []byte(validPK)) + if err != nil { + t.Errorf("Error occured: %s", err) + } + if s.TLSConfig.Certificates == nil { + t.Errorf("No certificates were configured for server") + } +} + +func TestNewServerFail(t *testing.T) { testCases := []struct { desc string cert string key string }{ { - desc: "correct certificate and key", - cert: ValidCert, - key: ValidPK, - }, - { - desc: "empty certificate", - cert: "", - key: ValidPK, - }, - { - desc: "empty key", - cert: ValidCert, - key: "", + desc: "wrong certificate", + cert: "some cert", + key: validPK, }, { - desc: "empty certificate and key", - cert: "", - key: "", + desc: "wrong key", + cert: validCert, + key: "some pk", }, } for _, tC := range testCases { t.Run(tC.desc, func(t *testing.T) { - s, err := server.NewServer(tC.cert, tC.key) - if err != nil { - t.Errorf("Error occured: %s", err) - } - if s.TLSConfig.Certificates == nil { - t.Errorf("No certificates were configured for server") + _, err := server.NewServer([]byte(tC.cert), []byte(tC.key)) + if err == nil { + t.Errorf("Expected error") } }) } } -func TestNewServerFail(t *testing.T) { +func TestConfigFileSuccess(t *testing.T) { + config, err := server.ValidateConfigFile("./config.yaml") + if err != nil { + t.Errorf("Error occured: %s", err) + } + if config.Cert == nil || config.Key == nil || config.DBPath == "" { + t.Errorf("Expected values were not read: %s", err) + } +} + +func TestConfigFileFail(t *testing.T) { testCases := []struct { desc string - cert string - key string }{ { - desc: "wrong certificate", - cert: "some cert", - key: ValidPK, - }, - { - desc: "wrong key", - cert: ValidCert, - key: "some pk", + desc: "", }, } for _, tC := range testCases { t.Run(tC.desc, func(t *testing.T) { - _, err := server.NewServer(tC.cert, tC.key) - if err == nil { - t.Errorf("Expected error") - } + }) } } diff --git a/cmd/gocert/main.go b/cmd/gocert/main.go index debff8d6..452b4990 100644 --- a/cmd/gocert/main.go +++ b/cmd/gocert/main.go @@ -10,20 +10,22 @@ import ( ) func main() { - certPathPtr := flag.String("cert", "", "A path for a certificate file to be used by the webserver") - keyPathPtr := flag.String("key", "", "The path for a private key for the given certificate") - dbPathPtr := flag.String("db", "./certs.db", "The path of the SQLite database for the repository") + configFilePtr := flag.String("config", "", "The config file to be provided to the server") flag.Parse() - if *certPathPtr == "" || *keyPathPtr == "" { - fmt.Fprintf(os.Stderr, "Providing a certificate and matching private key is extremely highly recommended. To do so, run: --cert --key . Using self signed certificates.") + if *configFilePtr == "" { + fmt.Fprintf(os.Stderr, "Providing a valid config file is required.") } - _, err := certdb.NewCertificateRequestsRepository(*dbPathPtr, "CertificateRequests") + config, err := server.ValidateConfigFile(*configFilePtr) + if err != nil { + fmt.Fprintf(os.Stderr, "Config file validation failed: %s.", err) + } + _, err = certdb.NewCertificateRequestsRepository(config.DBPath, "CertificateRequests") if err != nil { fmt.Fprintf(os.Stderr, "Couldn't connect to database: %s", err) os.Exit(1) } - srv, err := server.NewServer(*certPathPtr, *keyPathPtr) + srv, err := server.NewServer(config.Cert, config.Key) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't create server: %s", err) os.Exit(1) diff --git a/go.mod b/go.mod index 103d8a47..52e4d74e 100644 --- a/go.mod +++ b/go.mod @@ -3,3 +3,5 @@ module github.com/canonical/gocert go 1.22.1 require github.com/mattn/go-sqlite3 v1.14.22 + +require gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index e8d092a9..7016d6d4 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,5 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/certificates/certificates.go b/internal/certificates/certificates.go deleted file mode 100644 index 9e7f51dc..00000000 --- a/internal/certificates/certificates.go +++ /dev/null @@ -1,146 +0,0 @@ -package certificates - -import ( - "bytes" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "errors" - "math/big" - "net" - "time" -) - -// GenerateCACertificate generates a one time discardable root CA for the running GoCert webserver -func GenerateCACertificate() (string, string, error) { - caCert := &x509.Certificate{ - SerialNumber: big.NewInt(2019), - Subject: pkix.Name{ - Organization: []string{"Canonical, INC."}, - Country: []string{"US"}, - }, - NotBefore: time.Now(), - NotAfter: time.Now().AddDate(10, 0, 0), - IsCA: true, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, - BasicConstraintsValid: true, - } - caPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - return "", "", err - } - - caBytes, err := x509.CreateCertificate(rand.Reader, caCert, caCert, &caPrivateKey.PublicKey, caPrivateKey) - if err != nil { - return "", "", err - } - - caCertPEM := new(bytes.Buffer) - err = pem.Encode(caCertPEM, &pem.Block{ - Type: "CERTIFICATE", - Bytes: caBytes, - }) - if err != nil { - return "", "", err - } - - caPrivateKeyPEM := new(bytes.Buffer) - err = pem.Encode(caPrivateKeyPEM, &pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(caPrivateKey), - }) - if err != nil { - return "", "", err - } - return caCertPEM.String(), caPrivateKeyPEM.String(), nil -} - -// GenerateSelfSignedCertificate will create a certificate and pk for GoCert coming from a rootCA -// This certificate and PK is not saved anywhere. -// This certificate and PK should either be saved somewhere, or a real certificate should be provided to GoCert -func GenerateSelfSignedCertificate(caCertPEM, caPrivateKeyPEM string) (string, string, error) { - caCert, err := ParseCertificate(caCertPEM) - if err != nil { - return "", "", nil - } - caPrivateKey, err := ParsePKCS1PrivateKey(caPrivateKeyPEM) - if err != nil { - return "", "", nil - } - - certTemplate := &x509.Certificate{ - SerialNumber: big.NewInt(1658), - Subject: pkix.Name{ - Organization: []string{"Canonical, INC."}, - Country: []string{"US"}, - }, - IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback}, // TODO - NotBefore: time.Now(), - NotAfter: time.Now().AddDate(10, 0, 0), - SubjectKeyId: []byte{1, 2, 3, 4, 6}, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - KeyUsage: x509.KeyUsageDigitalSignature, - } - certPrivateKey, err := rsa.GenerateKey(rand.Reader, 4096) - if err != nil { - return "", "", err - } - certBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, &caCert, &certPrivateKey.PublicKey, &caPrivateKey) - if err != nil { - return "", "", err - } - certPEM := new(bytes.Buffer) - err = pem.Encode(certPEM, &pem.Block{ - Type: "CERTIFICATE", - Bytes: certBytes, - }) - if err != nil { - return "", "", err - } - certPrivateKeyPEM := new(bytes.Buffer) - err = pem.Encode(certPrivateKeyPEM, &pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(certPrivateKey), - }) - if err != nil { - return "", "", err - } - return certPEM.String(), certPrivateKeyPEM.String(), nil -} - -// ParseCertificate parses a PEM string into a native x509.Certificate object -func ParseCertificate(certPEM string) (x509.Certificate, error) { - cert := &x509.Certificate{} - block, _ := pem.Decode([]byte(certPEM)) - if block == nil { - return *cert, errors.New("PEM Certificate string not found or malformed") - } - if block.Type != "CERTIFICATE" { - return *cert, errors.New("given PEM string not a certificate") - } - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return *cert, err - } - return *cert, nil -} - -// ParsePrivateKey parses a PEM private key string into a native rsa.PrivateKey object -func ParsePKCS1PrivateKey(pkPEM string) (rsa.PrivateKey, error) { - pk := &rsa.PrivateKey{} - block, _ := pem.Decode([]byte(pkPEM)) - if block == nil { - return *pk, errors.New("PEM private key string not found or malformed") - } - if block.Type != "RSA PRIVATE KEY" { - return *pk, errors.New("given PEM string not an rsa private key") - } - pk, err := x509.ParsePKCS1PrivateKey(block.Bytes) - if err != nil { - return *pk, err - } - return *pk, nil -} diff --git a/internal/certificates/certificates_test.go b/internal/certificates/certificates_test.go deleted file mode 100644 index 085a8f75..00000000 --- a/internal/certificates/certificates_test.go +++ /dev/null @@ -1,279 +0,0 @@ -package certificates_test - -import ( - "fmt" - "strings" - "testing" - - "github.com/canonical/gocert/internal/certificates" -) - -const ( - ValidCert1 = `-----BEGIN CERTIFICATE----- -MIIDKzCCAhOgAwIBAgICB+MwDQYJKoZIhvcNAQELBQAwJzELMAkGA1UEBhMCVVMx -GDAWBgNVBAoTD0Nhbm9uaWNhbCwgSU5DLjAeFw0yNDA0MDUxMDAzMjhaFw0zNDA0 -MDUxMDAzMjhaMCcxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9DYW5vbmljYWwsIElO -Qy4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGiemPRVW7GhnSmbXh -PJz90+mvByoyYIaw+ILlcCvkJolYP4DzO5upME8mBQY8F+YDh0XfC7bGIMyK3XHL -wivglUXU4i3VzpOrwZ4THomCKroTE9o5X0rpS4xjILXRNLOE3Je2GqGEpQYS8ad0 -wHsmdgBACYw2skdgXjqbDkr6y2J1QHG8TCQfwijLpZQJhBFflAT1wm4gl+AKSpTj -isrmsxFJpvlJI9RnhhEY8pDsrUruiwZ6pJ36p79e0r8m+6Rs36fYx7FsTRI5VBCR -H9WvyA2W1xukEh1T0KB3Sy8Omb8ySNp7H5/9/wH4HZQy4tfpQt3MA6kretL2Xp0G -AodZAgMBAAGjYTBfMA4GA1UdDwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcD -AgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUYEQDVgZidxvS -pph/+NWsxWRVA20wDQYJKoZIhvcNAQELBQADggEBAIJb2BeiquR05bDh2t831BCq -EteqhgQoKclZXjCYYyWNGaClXngIHOJhx4WVsNIhRJmscdbJFWbBm0z6UQe0kLU3 -6cAQJlOy/7u5I6jIjyETLqLP8SgVaPuCEWyX82LVRm87OjzTZSQqhTh/gMpU/OdK -BwFENLc7LcWQ1eqBeeBjP5HhaC2BtSENVHBh81mKpR4mQBOoKq71XszSuqTQPkE5 -x/OFl3VkxM8x1aAdSK/V5UR8IlIbDW/dDQwsPoPBr5K/YuxUnm0GD6Ycqz7k/4F/ -R6grKY9bPkxtGvwIw1BqM7sqJ77knkLXRj4mH3J6znjY9dVLSKRjcZLMBtKhlr0= ------END CERTIFICATE-----` - ValidCert2 = `-----BEGIN CERTIFICATE----- -MIIELjCCAxagAwIBAgICBnowDQYJKoZIhvcNAQELBQAwJzELMAkGA1UEBhMCVVMx -GDAWBgNVBAoTD0Nhbm9uaWNhbCwgSU5DLjAeFw0yNDA0MDUxMDAzMjhaFw0zNDA0 -MDUxMDAzMjhaMCcxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9DYW5vbmljYWwsIElO -Qy4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDAP98jcfNw40HbS1xR -6UpSQTp4AGldFWQZBOFaVzD+eh7sYM/BFdT0dZRHGjXxL77ewDbwdwAFJ5zuxo+u -8/VgKGRpK6KCnKailmVrdRDhA45airMRQN6QXurN4NZgXcCHJWGAQKA9XJzcwGJF -l5LxoFY58wCv0d1JP8fgmbcgIRQTCIvhrlgrJ5Acz9QP6BuaxEHKbYYvWyTWtAhi -HS/w51yEbh6959ceJGBDZPyEVd9sfGipvHrA73+33+XBluRcUuWV4dCecyP/m+8C -jTBmW5s8gS6JUDE8yl99qm7CnXTkNDqPXThrorcKRwcHrw3ZEOm5rUPLuyzGBx/C -DZUbY9bsvHJMHOHlbwiY+M2MFIO+3H6qyfPfcHs8NFkrZh/as+9hrEzSYcz+tGBi -NynkSmNPQi4yzT00ilKYgcBhPdDDlBbdhcmdeFA3XE880VkQdJgefsYpCgYRdILm -DDd6ZMfZsQOJjuRC8rQKLO+z1X5JhiOlkNxZaOkq9b9eu7230rxTFCGocn0l9oKw -0q8OIDOTb7UKdIaGq/y++uRxe0hhNoijN1OJvh+R3/KGuztu5Y8ejksIxKBrUqCg -bUDXmQ82xbdJ36qF+NHBqFqFaKhH1XuK6eAIfqgQam/u9HNZZw3mOdm9rvIZfwIT -F9gvSwm1bxzyIHL/zWOgyfzckQIDAQABo2QwYjAOBgNVHQ8BAf8EBAMCB4AwHQYD -VR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA4GA1UdDgQHBAUBAgMEBjAhBgNV -HREEGjAYhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IB -AQB4UEu1/vTpEuuwoqgFpp8tEMewwBQ/CXPBN5seDnd/SUMXFrxk58f498qI3FQy -q98a+89jPWRGA5LY+DfIS82NYCwbKuvTzuJRoUpMPbebrhu7OQl7qQT6n8VOCy6x -IaRnPI0zEGbg2v340jMbB26FiyaFKyHEc24nnq3suZFmbslXzRE2Ebut+Qtft8he -0pSNQXtz5ULt0c8DTje7j+mRABzus45cj3HMDO4vcVRrHegdTE8YcZjwAFTKxqpg -W7GwJ5qPjnm6EMe8da55m8Q0hZchwGZreXNG7iCaw98pACBNgOOxh4LOhEZy25Bv -ayrvWnmPfg1u47sduuhHeUid ------END CERTIFICATE-----` - ValidPK1 = `-----BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAxonpj0VVuxoZ0pm14Tyc/dPprwcqMmCGsPiC5XAr5CaJWD+A -8zubqTBPJgUGPBfmA4dF3wu2xiDMit1xy8Ir4JVF1OIt1c6Tq8GeEx6Jgiq6ExPa -OV9K6UuMYyC10TSzhNyXthqhhKUGEvGndMB7JnYAQAmMNrJHYF46mw5K+stidUBx -vEwkH8Ioy6WUCYQRX5QE9cJuIJfgCkqU44rK5rMRSab5SSPUZ4YRGPKQ7K1K7osG -eqSd+qe/XtK/JvukbN+n2MexbE0SOVQQkR/Vr8gNltcbpBIdU9Cgd0svDpm/Mkja -ex+f/f8B+B2UMuLX6ULdzAOpK3rS9l6dBgKHWQIDAQABAoIBAQCJcpLSUCLw/Dni -Ve3Xt/nLtDi0ppYs+CxnOjSMmOKZ+Z/eC2C/g4XZVIuG+7V8RuNDkBPsPZTUh4Jz -pKkSciOkQFNu9QLcYT5Uix9fhyWVivT1HipWy8T1zAqt2chlxEF/+qPBO6CUTxbs -aIYQyuy3DLxRmQqMF1JYwgN6syNvicQ0rbpsX0/svmpaKEd+YLUy8l06MtSPnj0N -RWoUm2pWbCAFzHfeMUjTbyjeIQgbok1HGFtxuaO25TjPFB+MtZKwIe90NYFasCDO -GCR0wPsgFCzygi/68CycCfwSfQUW/xIQznpa69ifwA0Zwpf1GWeAClmN+/3vP3sI -dMXk9GPNAoGBAN1+MqbPmRNwMsEBZrzprsYF78lUB28iPn9HgbeXir5NpelzJxjh -oL0BRl9TmTgSFPS4baKMrLJPjPP0xk330E195/14lEf29i3Zis5W0d5UN9J3nta3 -nx1iJ8RZmyyPRHb0KEjftSp3nR68OTAQa9oCxKVsf8IsxhxfZM3bQiFrAoGBAOV4 -Ow4hXHhUhjUz86QKTnA8fWgkKL9HAc9r/EOzPGC0Ng39mS867NoV2cqfuHni3r+X -edlNO9dI/WZF52nQ9q1WNUEcZBuqeI07bVGddgtwQ3jUJbVuifG35qSmF2FfryXb -2GhXmVxsnwMkgwuAF57+QQC1kF9W95ioRsN4zndLAoGAZs5Tjk+fWoFiXWlcGWVR -xQIuaUFCbhfz8DntgJyrPmdmEfRr+kWHyRKValuwK3FhHrGX2bH32o/H+dfsT3Yt -zjoZevIDyV9cpq1pmxp7MPngKyVwqXLzPL05fg6lUspw2dG7/Q8w1LROTlzJIoEM -vlU4lxvENQl5LuQsMsyJZl8CgYA/8S7TkyxHQ5ZaQO3FajHNSK3RVTIditXQTNDw -tINAlzbw0xfad1sEsk4MDlHDdRqI3NbRXJouNWKXGyeO7vGt/3W7fQPQScJp6INf -2LbKHTBP/R283t6Fgq88diPRuG3/6LD28mIDxSjSIVx62ei7HzJR1kYyqvM3kLyX -P5SY3QKBgQC0oL1GX4iteb9uzfGsknrlYf9Eid9zLbc7MFEZ+JdQECNUrqAg6vt3 -0X3rDNjY2R0UZEC14WSx+Ax7xuSrozEtnuU6dBMv9wqCpJ36go+1TPwoe29uxJa5 -AqfiZgKPTXDLu0RPIeHUwyygzauy9FfeMGP+YjmGpJdONIDXf1npqg== ------END RSA PRIVATE KEY-----` - ValidPK2 = `-----BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY-----` -) - -func TestParseCertificateSuccess(t *testing.T) { - cert, err := certificates.ParseCertificate(ValidCert1) - if err != nil { - t.Fatalf("couldn't parse certificate: %s", err) - } - if cert.Issuer.Organization[0] != "Canonical, INC." { - t.Fatalf("certificate was parsed incorrectly") - } -} - -func TestParseCertificateFail(t *testing.T) { - var wrongString = "this is a real cert!!!" - var wrongStringErr = "PEM Certificate string not found or malformed" - var ValidCertWithoutWhitespace = strings.ReplaceAll(ValidCert1, "\n", "") - var ValidCertWithoutWhitespaceErr = "PEM Certificate string not found or malformed" - var wrongPemType = strings.ReplaceAll(ValidCert1, "CERTIFICATE", "SOME RANDOM PEM TYPE") - var wrongPemTypeErr = "given PEM string not a certificate" - - cases := []struct { - input string - expectedErr string - }{ - { - input: wrongString, - expectedErr: wrongStringErr, - }, - { - input: ValidCertWithoutWhitespace, - expectedErr: ValidCertWithoutWhitespaceErr, - }, - { - input: wrongPemType, - expectedErr: wrongPemTypeErr, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("InvalidCert%d", i), func(t *testing.T) { - _, err := certificates.ParseCertificate(c.input) - if err.Error() != c.expectedErr { - t.Errorf("Expected error not found:\nReceived: %s\nExpected: %s", err, c.expectedErr) - } - }) - } -} - -func TestParsePKCS1PrivateKeySuccess(t *testing.T) { - pk, err := certificates.ParsePKCS1PrivateKey(ValidPK1) - if err != nil { - t.Fatalf("couldn't parse private key: %s", err) - } - if pk.E != 65537 { - t.Fatalf("private key was parsed incorrectly") - } -} - -func TestParsePKCS1PrivateKeyFail(t *testing.T) { - var wrongString = "this is a real pk!!!" - var wrongStringErr = "PEM private key string not found or malformed" - var ValidCertWithoutWhitespace = strings.ReplaceAll(ValidCert1, "\n", "") - var ValidCertWithoutWhitespaceErr = "PEM private key string not found or malformed" - var wrongPemType = strings.ReplaceAll(ValidCert1, "RSA PRIVATE KEY", "SOME RANDOM PEM TYPE") - var wrongPemTypeErr = "given PEM string not an rsa private key" - - cases := []struct { - input string - expectedErr string - }{ - { - input: wrongString, - expectedErr: wrongStringErr, - }, - { - input: ValidCertWithoutWhitespace, - expectedErr: ValidCertWithoutWhitespaceErr, - }, - { - input: wrongPemType, - expectedErr: wrongPemTypeErr, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("InvalidPK%d", i), func(t *testing.T) { - _, err := certificates.ParsePKCS1PrivateKey(c.input) - if err.Error() != c.expectedErr { - t.Errorf("Expected error not found:\nReceived: %s\nExpected: %s", err, c.expectedErr) - } - }) - } -} -func TestGenerateCACertificateSuccess(t *testing.T) { - caCertPEM, caPKPEM, err := certificates.GenerateCACertificate() - if err != nil { - t.Fatalf("could not generate CA cert and PK") - } - if _, err := certificates.ParseCertificate(caCertPEM); err != nil { - t.Fatalf("generate CA cert cannot be parsed") - } - if _, err := certificates.ParsePKCS1PrivateKey(caPKPEM); err != nil { - t.Fatalf("generate CA private key cannot be parsed") - } -} - -func TestGenerateSelfSignedCertificateSuccess(t *testing.T) { - caCertPEM, caPKPEM, _ := certificates.GenerateCACertificate() - cert, pk, genErr := certificates.GenerateSelfSignedCertificate(caCertPEM, caPKPEM) - _, certErr := certificates.ParseCertificate(cert) - _, keyErr := certificates.ParsePKCS1PrivateKey(pk) - if genErr != nil || certErr != nil || keyErr != nil { - t.Fatalf("couldn't generate self signed certificate") - } -} - -func TestGenerateSelfSignedCertificateFail(t *testing.T) { - var nonMatchingCertPKErr = "x509: provided PrivateKey doesn't match parent's PublicKey" - - cases := []struct { - caCert string - caPK string - expectedErr string - }{ - { - caCert: ValidCert1, - caPK: ValidPK2, - expectedErr: nonMatchingCertPKErr, - }, - // Help: Why is this passing? - // { - // caCert: ValidCert2, - // caPK: ValidPK2, - // expectedErr: notCAErr, - // }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("SelfSignedCertificateFails%d", i), func(t *testing.T) { - _, _, err := certificates.GenerateSelfSignedCertificate(c.caCert, c.caPK) - if err.Error() != c.expectedErr { - t.Errorf("Expected error not found:\nReceived: %s\nExpected: %s", err, c.expectedErr) - } - }) - } -} From 8db136ca13866493a81902905309977799f99411 Mon Sep 17 00:00:00 2001 From: Kayra Date: Mon, 15 Apr 2024 14:56:33 +0300 Subject: [PATCH 11/19] dynamic port and log --- api/server.go | 10 +++++++--- api/server_test.go | 10 ++++++---- cmd/gocert/main.go | 20 +++++++++----------- cmd/gocert/main_test.go | 19 ++++++------------- 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/api/server.go b/api/server.go index ee0ec606..672d9cd4 100644 --- a/api/server.go +++ b/api/server.go @@ -4,6 +4,7 @@ package server import ( "crypto/tls" "errors" + "fmt" "net/http" "os" "time" @@ -15,12 +16,14 @@ type ConfigYAML struct { KeyPath string CertPath string DBPath string + Port int } type Config struct { Key []byte Cert []byte DBPath string + Port int } func ValidateConfigFile(filePath string) (Config, error) { @@ -48,23 +51,24 @@ func ValidateConfigFile(filePath string) (Config, error) { config.Cert = cert config.Key = key config.DBPath = c.DBPath + config.Port = c.Port return config, nil } // NewServer creates a new http server with handlers that Go can start listening to -func NewServer(certificate, key []byte) (*http.Server, error) { +func NewServer(certificate, key []byte, port int) (*http.Server, error) { serverCerts, err := tls.X509KeyPair(certificate, key) if err != nil { return nil, err } router := http.NewServeMux() - router.HandleFunc("/", HelloWorld) + router.HandleFunc("GET /", HelloWorld) v1 := http.NewServeMux() v1.Handle("/v1/", http.StripPrefix("/v1", router)) s := &http.Server{ - Addr: ":8080", + Addr: fmt.Sprintf(":%d", port), ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, diff --git a/api/server_test.go b/api/server_test.go index 97afeefb..336c0559 100644 --- a/api/server_test.go +++ b/api/server_test.go @@ -87,12 +87,14 @@ D7DC34n8CH9+avz9sCRwxpjxKnYW/BeyK0c4n9uZpjI8N4sOVqy6yWBUseww -----END RSA PRIVATE KEY-----` validConfig = `keypath: "./key_test.pem" certpath: "./cert_test.pem" -dbpath: "./certs.db"` +dbpath: "./certs.db" +port: 8000` invalidYAMLConfig = `wrong: fields every: where` invalidFileConfig = `keypath: "./nokeyfile.pem" certpath: "./nocertfile.pem" -dbpath: "./certs.db"` +dbpath: "./certs.db" +port: 8000` ) func TestMain(m *testing.M) { @@ -122,7 +124,7 @@ func TestMain(m *testing.M) { } func TestNewServerSuccess(t *testing.T) { - s, err := server.NewServer([]byte(validCert), []byte(validPK)) + s, err := server.NewServer([]byte(validCert), []byte(validPK), 8000) if err != nil { t.Errorf("Error occured: %s", err) } @@ -150,7 +152,7 @@ func TestNewServerFail(t *testing.T) { } for _, tC := range testCases { t.Run(tC.desc, func(t *testing.T) { - _, err := server.NewServer([]byte(tC.cert), []byte(tC.key)) + _, err := server.NewServer([]byte(tC.cert), []byte(tC.key), 8000) if err == nil { t.Errorf("Expected error") } diff --git a/cmd/gocert/main.go b/cmd/gocert/main.go index 452b4990..a3d179ac 100644 --- a/cmd/gocert/main.go +++ b/cmd/gocert/main.go @@ -2,7 +2,7 @@ package main import ( "flag" - "fmt" + "log" "os" server "github.com/canonical/gocert/api" @@ -10,29 +10,27 @@ import ( ) func main() { + log.SetOutput(os.Stderr) configFilePtr := flag.String("config", "", "The config file to be provided to the server") flag.Parse() if *configFilePtr == "" { - fmt.Fprintf(os.Stderr, "Providing a valid config file is required.") + log.Fatalf("Providing a valid config file is required.") } config, err := server.ValidateConfigFile(*configFilePtr) if err != nil { - fmt.Fprintf(os.Stderr, "Config file validation failed: %s.", err) + log.Fatalf("Config file validation failed: %s.", err) } _, err = certdb.NewCertificateRequestsRepository(config.DBPath, "CertificateRequests") if err != nil { - fmt.Fprintf(os.Stderr, "Couldn't connect to database: %s", err) - os.Exit(1) + log.Fatalf("Couldn't connect to database: %s", err) } - srv, err := server.NewServer(config.Cert, config.Key) + srv, err := server.NewServer(config.Cert, config.Key, config.Port) if err != nil { - fmt.Fprintf(os.Stderr, "Couldn't create server: %s", err) - os.Exit(1) + log.Fatalf("Couldn't create server: %s", err) } - fmt.Fprintf(os.Stdout, "Starting server at %s", srv.Addr) + log.Printf("Starting server at %s", srv.Addr) if err := srv.ListenAndServeTLS("", ""); err != nil { - fmt.Fprintf(os.Stderr, "Server ran into error: %s", err) - os.Exit(1) + log.Fatalf("Server ran into error: %s", err) } } diff --git a/cmd/gocert/main_test.go b/cmd/gocert/main_test.go index 5552a216..82d4b4b2 100644 --- a/cmd/gocert/main_test.go +++ b/cmd/gocert/main_test.go @@ -4,17 +4,10 @@ import ( "testing" ) -func TestGoCertCLI(t *testing.T) { - // os.Args = []string{"gocert"} - // go main() - // run gocert - // run gocert --cert openable path - // run gocert --key openable path - // run gocert --cert openable path --key openable path --db unopenable path - // run gocert --cert openable path --key openable path --db openable path - // parametrize below - // run gocert --cert unopenable path --key unopenable path - // run gocert --cert unopenable path --key openable path - // run gocert --cert openable path --key unopenable path - // run gocert --cert openable path --key openable path +func TestGoCertSuccess(t *testing.T) { + +} + +func TestGoCertFail(t *testing.T) { + } From 0254e24d905a2da96efb183671f084262e588f5a Mon Sep 17 00:00:00 2001 From: Kayra Date: Mon, 15 Apr 2024 15:53:55 +0300 Subject: [PATCH 12/19] main function test skelly --- cmd/gocert/main_test.go | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/cmd/gocert/main_test.go b/cmd/gocert/main_test.go index 82d4b4b2..5f8be718 100644 --- a/cmd/gocert/main_test.go +++ b/cmd/gocert/main_test.go @@ -1,13 +1,41 @@ package main import ( + "flag" + "os" "testing" ) -func TestGoCertSuccess(t *testing.T) { - -} +const ( + validConfig = `keypath: "./key_test.pem" +certpath: "./cert_test.pem" +dbpath: "./certs.db" +port: 8000` + invalidYAMLConfig = `wrong: fields +every: where` + invalidFileConfig = `keypath: "./nokeyfile.pem" +certpath: "./nocertfile.pem" +dbpath: "./certs.db" +port: 8000` +) func TestGoCertFail(t *testing.T) { - + oldArgs := os.Args + defer func() { os.Args = oldArgs }() + cases := []struct { + Name string + Args []string + ExpectedExit int + ExpectedOutput string + }{ + {"flags not set", []string{}, 1, "Providing a valid config file is required."}, + {"config file not valid", []string{"-config", "config_invalid.yaml"}, 1, "Config file validation failed:"}, + {"database not connectable", []string{"-config", "config_invalid_db.yaml"}, 1, "Couldn't connect to database:"}, + {"server couldn't be created", []string{"-config", "config_invalid.yaml"}, 1, "Couldn't create server:"}, + } + for _, tc := range cases { + flag.CommandLine = flag.NewFlagSet(tc.Name, flag.ExitOnError) + os.Args = append([]string{tc.Name}, tc.Args...) + // main() + } } From a694213ef20a6b31746f415cf1739b44669f5331 Mon Sep 17 00:00:00 2001 From: Kayra Date: Mon, 15 Apr 2024 16:39:23 +0300 Subject: [PATCH 13/19] use consts --- cmd/gocert/main_test.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/cmd/gocert/main_test.go b/cmd/gocert/main_test.go index 5f8be718..d7998e44 100644 --- a/cmd/gocert/main_test.go +++ b/cmd/gocert/main_test.go @@ -11,8 +11,15 @@ const ( certpath: "./cert_test.pem" dbpath: "./certs.db" port: 8000` - invalidYAMLConfig = `wrong: fields -every: where` + invalidCertConfig = `keypath: "./key_test.pem" +certpath: "./wrong_cert_test.pem" +dbpath: "./certs.db" +port: 8000` + + invalidDBConfig = `keypath: "./nokeyfile.pem" +certpath: "./nocertfile.pem" +dbpath: "/etc/hosts" +port: 8000` invalidFileConfig = `keypath: "./nokeyfile.pem" certpath: "./nocertfile.pem" dbpath: "./certs.db" @@ -25,13 +32,14 @@ func TestGoCertFail(t *testing.T) { cases := []struct { Name string Args []string + ConfigYAML string ExpectedExit int ExpectedOutput string }{ - {"flags not set", []string{}, 1, "Providing a valid config file is required."}, - {"config file not valid", []string{"-config", "config_invalid.yaml"}, 1, "Config file validation failed:"}, - {"database not connectable", []string{"-config", "config_invalid_db.yaml"}, 1, "Couldn't connect to database:"}, - {"server couldn't be created", []string{"-config", "config_invalid.yaml"}, 1, "Couldn't create server:"}, + {"flags not set", []string{}, validConfig, 1, "Providing a valid config file is required."}, + {"config file not valid", []string{"-config", "config_invalid.yaml"}, invalidFileConfig, 1, "Config file validation failed:"}, + {"database not connectable", []string{"-config", "config_invalid_db.yaml"}, invalidDBConfig, 1, "Couldn't connect to database:"}, + {"server couldn't be created", []string{"-config", "config_invalid.yaml"}, invalidCertConfig, 1, "Couldn't create server:"}, } for _, tc := range cases { flag.CommandLine = flag.NewFlagSet(tc.Name, flag.ExitOnError) From 42b7a4eb780e8fb3af47185565810a4f26d468ce Mon Sep 17 00:00:00 2001 From: Kayra Date: Mon, 15 Apr 2024 17:44:03 +0300 Subject: [PATCH 14/19] main test --- cmd/gocert/main_test.go | 145 +++++++++++++++++++++++++++++++++++----- 1 file changed, 127 insertions(+), 18 deletions(-) diff --git a/cmd/gocert/main_test.go b/cmd/gocert/main_test.go index d7998e44..ce8581a7 100644 --- a/cmd/gocert/main_test.go +++ b/cmd/gocert/main_test.go @@ -2,30 +2,128 @@ package main import ( "flag" + "io" + "log" "os" + "os/exec" + "strings" "testing" ) const ( + validCert = `-----BEGIN CERTIFICATE----- +MIIELjCCAxagAwIBAgICBnowDQYJKoZIhvcNAQELBQAwJzELMAkGA1UEBhMCVVMx +GDAWBgNVBAoTD0Nhbm9uaWNhbCwgSU5DLjAeFw0yNDA0MDUxMDAzMjhaFw0zNDA0 +MDUxMDAzMjhaMCcxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9DYW5vbmljYWwsIElO +Qy4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDAP98jcfNw40HbS1xR +6UpSQTp4AGldFWQZBOFaVzD+eh7sYM/BFdT0dZRHGjXxL77ewDbwdwAFJ5zuxo+u +8/VgKGRpK6KCnKailmVrdRDhA45airMRQN6QXurN4NZgXcCHJWGAQKA9XJzcwGJF +l5LxoFY58wCv0d1JP8fgmbcgIRQTCIvhrlgrJ5Acz9QP6BuaxEHKbYYvWyTWtAhi +HS/w51yEbh6959ceJGBDZPyEVd9sfGipvHrA73+33+XBluRcUuWV4dCecyP/m+8C +jTBmW5s8gS6JUDE8yl99qm7CnXTkNDqPXThrorcKRwcHrw3ZEOm5rUPLuyzGBx/C +DZUbY9bsvHJMHOHlbwiY+M2MFIO+3H6qyfPfcHs8NFkrZh/as+9hrEzSYcz+tGBi +NynkSmNPQi4yzT00ilKYgcBhPdDDlBbdhcmdeFA3XE880VkQdJgefsYpCgYRdILm +DDd6ZMfZsQOJjuRC8rQKLO+z1X5JhiOlkNxZaOkq9b9eu7230rxTFCGocn0l9oKw +0q8OIDOTb7UKdIaGq/y++uRxe0hhNoijN1OJvh+R3/KGuztu5Y8ejksIxKBrUqCg +bUDXmQ82xbdJ36qF+NHBqFqFaKhH1XuK6eAIfqgQam/u9HNZZw3mOdm9rvIZfwIT +F9gvSwm1bxzyIHL/zWOgyfzckQIDAQABo2QwYjAOBgNVHQ8BAf8EBAMCB4AwHQYD +VR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA4GA1UdDgQHBAUBAgMEBjAhBgNV +HREEGjAYhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IB +AQB4UEu1/vTpEuuwoqgFpp8tEMewwBQ/CXPBN5seDnd/SUMXFrxk58f498qI3FQy +q98a+89jPWRGA5LY+DfIS82NYCwbKuvTzuJRoUpMPbebrhu7OQl7qQT6n8VOCy6x +IaRnPI0zEGbg2v340jMbB26FiyaFKyHEc24nnq3suZFmbslXzRE2Ebut+Qtft8he +0pSNQXtz5ULt0c8DTje7j+mRABzus45cj3HMDO4vcVRrHegdTE8YcZjwAFTKxqpg +W7GwJ5qPjnm6EMe8da55m8Q0hZchwGZreXNG7iCaw98pACBNgOOxh4LOhEZy25Bv +ayrvWnmPfg1u47sduuhHeUid +-----END CERTIFICATE-----` + validPK = `-----BEGIN RSA PRIVATE KEY----- +MIIJKQIBAAKCAgEAwD/fI3HzcONB20tcUelKUkE6eABpXRVkGQThWlcw/noe7GDP +wRXU9HWURxo18S++3sA28HcABSec7saPrvP1YChkaSuigpymopZla3UQ4QOOWoqz +EUDekF7qzeDWYF3AhyVhgECgPVyc3MBiRZeS8aBWOfMAr9HdST/H4Jm3ICEUEwiL +4a5YKyeQHM/UD+gbmsRBym2GL1sk1rQIYh0v8OdchG4evefXHiRgQ2T8hFXfbHxo +qbx6wO9/t9/lwZbkXFLlleHQnnMj/5vvAo0wZlubPIEuiVAxPMpffapuwp105DQ6 +j104a6K3CkcHB68N2RDpua1Dy7ssxgcfwg2VG2PW7LxyTBzh5W8ImPjNjBSDvtx+ +qsnz33B7PDRZK2Yf2rPvYaxM0mHM/rRgYjcp5EpjT0IuMs09NIpSmIHAYT3Qw5QW +3YXJnXhQN1xPPNFZEHSYHn7GKQoGEXSC5gw3emTH2bEDiY7kQvK0Cizvs9V+SYYj +pZDcWWjpKvW/Xru9t9K8UxQhqHJ9JfaCsNKvDiAzk2+1CnSGhqv8vvrkcXtIYTaI +ozdTib4fkd/yhrs7buWPHo5LCMSga1KgoG1A15kPNsW3Sd+qhfjRwahahWioR9V7 +iungCH6oEGpv7vRzWWcN5jnZva7yGX8CExfYL0sJtW8c8iBy/81joMn83JECAwEA +AQKCAgEAmtqX7SAbXCHh6TchrOUCNZFO/Fwwgob5cuGod7FlyIUrpXExxzDDsQmI +n2EwdA7matxfJIBmJsDKutZ75Auj6Yl/n+tC4nw2CR6loNHR/71yi+HO7SXYYGfk +MGNbqpG5w+JLUBg+Ok8AFxxry+yUs0ZYTiM7uWONIDRc1sBabmnWlqI6slVRtakP +fvW0tf9bROWyrNBd1oVO/hZT7lveQujJb+6XmpZFg4T/eSm98QaOif8H+zjTk9cW +hFC366CUXv1y6rDS7t6F7511/xMlGj3NpAXWK0rJ7lKAamO/Bcn43txnExWenaya +TY/6zKinueHSsforcs5Y+UXBwfhY0in4lbOmAauF10eTufpnxR3G5+dNOBrq9oXu +zSk2R7RmbitIY49xAcuYKDhLkr9C0jexh433piHgRlBAcWqbjCc8GyK8hdiI+tGA +mt66jSRTSe70EfPj8xH6EUOLjcKNER4iVUAt4kdYWcvwgamW5CWtRB1bql8YYbiw +9xYtE2QsYbCk8pZ2yIK8R2ejRxoAZzHSjGi9c7qoCMeSNWpv2dso+hOtXlLnFdX7 +aQ11I1vqhzn2Ls2aTgKFUcb0q3JkCQr19lkGy0qoSwjw+ZtlA4qpIcQ8aO6c4FqK +QkKZ/pfmuP8CafaNH6sbNoGAS8nEwnnQo5C8iMMsR8o4WblllkECggEBAO1xZznn +ubIPYxyL+NCIm1lDsNsT508gZWGXhQf1qqvOdY7zsPQeI9/5v1OpkMFe0Di8Zwr/ +wiQcqP5hyXv7c1wJJxsOWhaI5QpiJDkbM89NPR0nJGF1k/d71fQ6z08yNrqeAruy +jOhXjOhkUAIBmSgZeUzp5f2we1n/35GdVcGy9g7V/4dMfrV9z/qRhD8mIeeZlvU3 +icinpqWtcWY4jn5rwyM7Jpau2m2wu1m3G/vQiKAcJQrIirSdOyJ8a82f7mKv9LsI +rMJGPJ4Q3TTkhcx9U0utQw8wPFJC94Z4RWriM+VYSjUKoHYOHCwmRqJrTXMPaSR8 +fnnLb2PynfViQfkCggEBAM9GRKMY7WVl6RJAGKvlQJ/NTXrFLPSlI0HvCKZSfv5E +tzu3AzSRs84BkiMXtMB9/Q47+/XVXnGC2mgVrRhgf1HCFzgYZwLruLuLSepxVpm7 +QTmgaQ59hxKBXwkE0yj+02cbdsLdzKsnU60zHL4v6wEH8lE7TS5qIsU4Szm/YQhb +3Eq2bAOKqku+SfZwf7b2e0jzTZl0dzqXpz5rImXQdwm1exy6Wmc/XtTmjC/kCOnr +SghgoBSSeTCNDFlUtBKlhBJDQqXhOfM8sl6DBRYZrJGgZzAzaAkO+o/JhYPYJ3W5 +5bZ+gnZNJYh8ZYG63Ae1KudDRXinIIlzX7/nBNlelVkCggEAPbB/9EBrM4Lh6jHH +lE5Zpih7E4ApUZqGHIPkUTwXeomqa1iO+e22vmNBvTfJ3yOGD6eLUgU+6Gj10xmO +4oJi51+NZG8nIsGwWDFFXfzeSha0MRXRUuzcY6kt3kVFRTszkuqopSFvkJHmjx44 +1zyZER0FMeF3GqE2exyKdmedNzUKzrH0sK9EIF0uotgZttpuZqC14sHqL1K3bkYQ +t1EsXFYdHdMpZG7LW0JWeqmjQJpeVNLbIOEXgHN1QLF4xLSvl75FZC6Ny++5oguZ +nTteM9G/yWKbkJ+knG6/ppUq2+knOIfmx78aD3H9Cc9r/JjKR4GSfKNHrNcY+qu3 +NGCx6QKCAQAZDhNp6692nFUKIblZvgKLzpNZDdCbWgLjC3PuNvam4cOMclju19X2 +RvZVS55Lzm7yc4nHc51Q91JTVptv4OpDBcUswLZjAf94nCO5NS4Usy/1OVC5sa7M +K9tDCdREllkTk5xNfeYpoj1ZKF6HFt+/ZiiCbTqtK6M8V8uwFVQzYHdGiLqRywc+ +1Ke4JG0rvqu0a8Srkgp/iKlswCKOUB6zi75wAI7BAEYEUkIL3/K74/c1AAkZs4L2 +vXYKrlR+FIfcdUjvKESLBIFDL29D9qKHj+4pQ22F+suK6f87qrtKXchIwQ4gIr8w +umjCv8WtINco0VbqeLlUJCAk4FYTuH0xAoIBAQCA+A2l7DCMCb7MjkjdyNFqkzpg +2ou3WkCf3j7txqg8oGxQ5eCg45BU1zTOW35YVCtP/PMU0tLo7iPudL79jArv+GfS +6SbLz3OEzQb6HU9/4JA5fldHv+6XJLZA27b8LnfhL1Iz6dS+MgH53+OJdkQBc+Dm +Q53tuiWQeoxNOjHiWstBPELxGbW6447JyVVbNYGUk+VFU7okzA6sRTJ/5Ysda4Sf +auNQc2hruhr/2plhFUYoZHPzGz7d5zUGKymhCoS8BsFVtD0WDL4srdtY/W2Us7TD +D7DC34n8CH9+avz9sCRwxpjxKnYW/BeyK0c4n9uZpjI8N4sOVqy6yWBUseww +-----END RSA PRIVATE KEY-----` validConfig = `keypath: "./key_test.pem" certpath: "./cert_test.pem" dbpath: "./certs.db" port: 8000` - invalidCertConfig = `keypath: "./key_test.pem" -certpath: "./wrong_cert_test.pem" -dbpath: "./certs.db" -port: 8000` - - invalidDBConfig = `keypath: "./nokeyfile.pem" -certpath: "./nocertfile.pem" + invalidConfig = `hello: "world" +goodbye: "world"` + invalidDBConfig = `keypath: "./key_test.pem" +certpath: "./cert_test.pem" dbpath: "/etc/hosts" -port: 8000` - invalidFileConfig = `keypath: "./nokeyfile.pem" -certpath: "./nocertfile.pem" -dbpath: "./certs.db" port: 8000` ) +func TestMain(m *testing.M) { + testfolder, err := os.MkdirTemp("./", "configtest-") + if err != nil { + log.Fatalf("couldn't create temp directory") + } + writeCertErr := os.WriteFile(testfolder+"/cert_test.pem", []byte(validCert), 0644) + writeKeyErr := os.WriteFile(testfolder+"/key_test.pem", []byte(validPK), 0644) + if writeCertErr != nil || writeKeyErr != nil { + log.Fatalf("couldn't create temp testing file") + } + if err := os.Chdir(testfolder); err != nil { + log.Fatalf("couldn't enter testing directory") + } + + exitval := m.Run() + + if err := os.Chdir("../"); err != nil { + log.Fatalf("couldn't change back to parent directory") + } + if err := os.RemoveAll(testfolder); err != nil { + log.Fatalf("couldn't remove temp testing directory") + } + os.Exit(exitval) +} + func TestGoCertFail(t *testing.T) { oldArgs := os.Args defer func() { os.Args = oldArgs }() @@ -33,17 +131,28 @@ func TestGoCertFail(t *testing.T) { Name string Args []string ConfigYAML string - ExpectedExit int ExpectedOutput string }{ - {"flags not set", []string{}, validConfig, 1, "Providing a valid config file is required."}, - {"config file not valid", []string{"-config", "config_invalid.yaml"}, invalidFileConfig, 1, "Config file validation failed:"}, - {"database not connectable", []string{"-config", "config_invalid_db.yaml"}, invalidDBConfig, 1, "Couldn't connect to database:"}, - {"server couldn't be created", []string{"-config", "config_invalid.yaml"}, invalidCertConfig, 1, "Couldn't create server:"}, + {"flags not set", []string{}, validConfig, "Providing a valid config file is required."}, + {"config file not valid", []string{"-config", "config.yaml"}, invalidConfig, "Config file validation failed:"}, + {"database not connectable", []string{"-config", "config.yaml"}, invalidDBConfig, "Couldn't connect to database:"}, } for _, tc := range cases { + writeConfigErr := os.WriteFile("config.yaml", []byte(tc.ConfigYAML), 0644) + if writeConfigErr != nil { + t.Errorf("Failed writing config file") + } flag.CommandLine = flag.NewFlagSet(tc.Name, flag.ExitOnError) - os.Args = append([]string{tc.Name}, tc.Args...) - // main() + cmd := exec.Command("gocert", tc.Args...) + stderr, _ := cmd.StderrPipe() + + cmd.Start() + + slurp, _ := io.ReadAll(stderr) + + cmd.Wait() + if !strings.HasPrefix(string(slurp), tc.ExpectedOutput) { + t.Errorf("%s: Expected error not found: %s", tc.Name, slurp) + } } } From 26ec535673e5dd90b1505cd9605119975659972d Mon Sep 17 00:00:00 2001 From: Kayra Date: Mon, 15 Apr 2024 17:51:16 +0300 Subject: [PATCH 15/19] lint and test --- cmd/gocert/main_test.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cmd/gocert/main_test.go b/cmd/gocert/main_test.go index ce8581a7..1891a659 100644 --- a/cmd/gocert/main_test.go +++ b/cmd/gocert/main_test.go @@ -146,12 +146,16 @@ func TestGoCertFail(t *testing.T) { cmd := exec.Command("gocert", tc.Args...) stderr, _ := cmd.StderrPipe() - cmd.Start() + if err := cmd.Start(); err != nil { + t.Errorf("Failed running command") + } slurp, _ := io.ReadAll(stderr) - cmd.Wait() - if !strings.HasPrefix(string(slurp), tc.ExpectedOutput) { + if err := cmd.Wait(); err == nil { + t.Errorf("Command did not fail") + } + if !strings.Contains(string(slurp), tc.ExpectedOutput) { t.Errorf("%s: Expected error not found: %s", tc.Name, slurp) } } From de87fcbf1118221798d80fd58541cb1c47e2b649 Mon Sep 17 00:00:00 2001 From: Kayra Date: Mon, 15 Apr 2024 17:55:28 +0300 Subject: [PATCH 16/19] unit testing the cli is hard --- cmd/gocert/main_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/gocert/main_test.go b/cmd/gocert/main_test.go index 1891a659..3a784a98 100644 --- a/cmd/gocert/main_test.go +++ b/cmd/gocert/main_test.go @@ -100,6 +100,11 @@ port: 8000` ) func TestMain(m *testing.M) { + cmd := exec.Command("go", "install", "./...") + if err := cmd.Run(); err != nil { + log.Fatalf("couldn't install the gocert CLI") + } + testfolder, err := os.MkdirTemp("./", "configtest-") if err != nil { log.Fatalf("couldn't create temp directory") From 7dced05c71aec37656e06ae70703c3f1737d39c5 Mon Sep 17 00:00:00 2001 From: Kayra Date: Mon, 15 Apr 2024 18:21:36 +0300 Subject: [PATCH 17/19] update hello example to be healthcheck instead --- api/server.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/server.go b/api/server.go index 672d9cd4..649076f3 100644 --- a/api/server.go +++ b/api/server.go @@ -62,7 +62,7 @@ func NewServer(certificate, key []byte, port int) (*http.Server, error) { return nil, err } router := http.NewServeMux() - router.HandleFunc("GET /", HelloWorld) + router.HandleFunc("GET /", HealthCheck) v1 := http.NewServeMux() v1.Handle("/v1/", http.StripPrefix("/v1", router)) @@ -82,8 +82,8 @@ func NewServer(certificate, key []byte, port int) (*http.Server, error) { return s, nil } -func HelloWorld(w http.ResponseWriter, r *http.Request) { - _, err := w.Write([]byte("Hello World")) +func HealthCheck(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte("Server Alive")) if err != nil { return } From 2054d08d7ce45df0b6ac483a44274a36599e3786 Mon Sep 17 00:00:00 2001 From: Kayra Date: Mon, 15 Apr 2024 18:32:29 +0300 Subject: [PATCH 18/19] remove unneeded error check --- api/server.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/api/server.go b/api/server.go index 649076f3..d0c783b6 100644 --- a/api/server.go +++ b/api/server.go @@ -83,8 +83,5 @@ func NewServer(certificate, key []byte, port int) (*http.Server, error) { } func HealthCheck(w http.ResponseWriter, r *http.Request) { - _, err := w.Write([]byte("Server Alive")) - if err != nil { - return - } + w.Write([]byte("Server Alive")) //nolint:errcheck } From 5c9827926b22c1fa00b5c654b50979c594d63c9d Mon Sep 17 00:00:00 2001 From: Kayra Date: Wed, 17 Apr 2024 20:40:21 +0300 Subject: [PATCH 19/19] close file after we're done --- api/server.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/api/server.go b/api/server.go index d0c783b6..386b7d7c 100644 --- a/api/server.go +++ b/api/server.go @@ -45,9 +45,15 @@ func ValidateConfigFile(filePath string) (Config, error) { if err != nil { return config, errors.Join(validationErr, err) } - if _, err := os.OpenFile(c.DBPath, os.O_CREATE|os.O_RDONLY, 0644); err != nil { + dbfile, err := os.OpenFile(c.DBPath, os.O_CREATE|os.O_RDONLY, 0644) + if err != nil { return config, errors.Join(validationErr, err) } + err = dbfile.Close() + if err != nil { + return config, errors.Join(validationErr, err) + } + config.Cert = cert config.Key = key config.DBPath = c.DBPath