Skip to content

Commit

Permalink
fix: add key missing in config validation (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
gruyaume authored Jun 14, 2024
1 parent 160db0d commit e6db72b
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 116 deletions.
10 changes: 7 additions & 3 deletions cmd/gocert/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@ import (
"os"

server "github.com/canonical/gocert/internal/api"
"github.com/canonical/gocert/internal/config"
)

func main() {
log.SetOutput(os.Stderr)
configFilePtr := flag.String("config", "", "The config file to be provided to the server")
flag.Parse()

if *configFilePtr == "" {
log.Fatalf("Providing a valid config file is required.")
log.Fatalf("Providing a config file is required.")
}
conf, err := config.Validate(*configFilePtr)
if err != nil {
log.Fatalf("Couldn't validate config file: %s", err)
}
srv, err := server.NewServer(*configFilePtr)
srv, err := server.NewServer(conf.Port, conf.Cert, conf.Key, conf.DBPath, conf.PebbleNotificationsEnabled)
if err != nil {
log.Fatalf("Couldn't create server: %s", err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/gocert/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func TestGoCertFail(t *testing.T) {
ConfigYAML string
ExpectedOutput string
}{
{"flags not set", []string{}, validConfig, "Providing a valid config file is required."},
{"flags not set", []string{}, validConfig, "Providing a 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:"},
}
Expand Down
69 changes: 5 additions & 64 deletions internal/api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,72 +7,17 @@ import (
"fmt"
"log"
"net/http"
"os"
"os/exec"
"time"

"github.com/canonical/gocert/internal/certdb"
"gopkg.in/yaml.v3"
)

type ConfigYAML struct {
KeyPath string `yaml:"key_path"`
CertPath string `yaml:"cert_path"`
DBPath string `yaml:"db_path"`
Port int `yaml:"port"`
Pebblenotificationsenabled bool `yaml:"pebble_notifications"`
}

type Config struct {
Key []byte
Cert []byte
DBPath string
Port int
PebbleNotificationsEnabled bool
}

type Environment struct {
DB *certdb.CertificateRequestsRepository
SendPebbleNotifications bool
}

// validateConfigFile opens and processes the given yaml file, and catches errors in the process
func validateConfigFile(filePath string) (Config, error) {
validationErr := errors.New("config file validation failed: ")
config := Config{}
configYaml, err := os.ReadFile(filePath)
if err != nil {
return config, errors.Join(validationErr, err)
}
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)
}
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
config.Port = c.Port
config.PebbleNotificationsEnabled = c.Pebblenotificationsenabled
return config, nil
}

func SendPebbleNotification(key, request_id string) error {
cmd := exec.Command("pebble", "notify", key, fmt.Sprintf("request_id=%s", request_id))
if err := cmd.Run(); err != nil {
Expand All @@ -82,27 +27,23 @@ func SendPebbleNotification(key, request_id string) error {
}

// NewServer creates an environment and an http server with handlers that Go can start listening to
func NewServer(configFile string) (*http.Server, error) {
config, err := validateConfigFile(configFile)
if err != nil {
return nil, err
}
serverCerts, err := tls.X509KeyPair(config.Cert, config.Key)
func NewServer(port int, cert []byte, key []byte, dbPath string, pebbleNotificationsEnabled bool) (*http.Server, error) {
serverCerts, err := tls.X509KeyPair(cert, key)
if err != nil {
return nil, err
}
db, err := certdb.NewCertificateRequestsRepository(config.DBPath, "CertificateRequests")
db, err := certdb.NewCertificateRequestsRepository(dbPath, "CertificateRequests")
if err != nil {
log.Fatalf("Couldn't connect to database: %s", err)
}

env := &Environment{}
env.DB = db
env.SendPebbleNotifications = config.PebbleNotificationsEnabled
env.SendPebbleNotifications = pebbleNotificationsEnabled
router := NewGoCertRouter(env)

s := &http.Server{
Addr: fmt.Sprintf(":%d", config.Port),
Addr: fmt.Sprintf(":%d", port),

ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
Expand Down
53 changes: 5 additions & 48 deletions internal/api/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,24 +85,6 @@ Q53tuiWQeoxNOjHiWstBPELxGbW6447JyVVbNYGUk+VFU7okzA6sRTJ/5Ysda4Sf
auNQc2hruhr/2plhFUYoZHPzGz7d5zUGKymhCoS8BsFVtD0WDL4srdtY/W2Us7TD
D7DC34n8CH9+avz9sCRwxpjxKnYW/BeyK0c4n9uZpjI8N4sOVqy6yWBUseww
-----END RSA PRIVATE KEY-----`
validConfig = `key_path: "./key_test.pem"
cert_path: "./cert_test.pem"
db_path: "./certs.db"
port: 8000`
wrongCertConfig = `key_path: "./key_test.pem"
cert_path: "./cert_test_wrong.pem"
db_path: "./certs.db"
port: 8000`
wrongKeyConfig = `key_path: "./key_test_wrong.pem"
cert_path: "./cert_test.pem"
db_path: "./certs.db"
port: 8000`
invalidYAMLConfig = `wrong: fields
every: where`
invalidFileConfig = `key_path: "./nokeyfile.pem"
cert_path: "./nocertfile.pem"
db_path: "./certs.db"
port: 8000`
)

func TestMain(m *testing.M) {
Expand Down Expand Up @@ -131,11 +113,7 @@ func TestMain(m *testing.M) {
}

func TestNewServerSuccess(t *testing.T) {
writeConfigErr := os.WriteFile("config.yaml", []byte(validConfig), 0644)
if writeConfigErr != nil {
log.Fatalf("Error writing config file")
}
s, err := server.NewServer("config.yaml")
s, err := server.NewServer(8000, []byte(validCert), []byte(validPK), "certs.db", false)
if err != nil {
t.Errorf("Error occured: %s", err)
}
Expand All @@ -144,30 +122,9 @@ func TestNewServerSuccess(t *testing.T) {
}
}

func TestNewServerFail(t *testing.T) {
testCases := []struct {
desc string
config string
}{
{
desc: "wrong certificate",
config: wrongCertConfig,
},
{
desc: "wrong key",
config: wrongKeyConfig,
},
}
for _, tC := range testCases {
writeConfigErr := os.WriteFile("config.yaml", []byte(tC.config), 0644)
if writeConfigErr != nil {
log.Fatalf("Error writing config file")
}
t.Run(tC.desc, func(t *testing.T) {
_, err := server.NewServer("config.yaml")
if err == nil {
t.Errorf("Expected error")
}
})
func TestInvalidKeyFailure(t *testing.T) {
_, err := server.NewServer(8000, []byte(validCert), []byte{}, "certs.db", false)
if err == nil {
t.Errorf("No error was thrown for invalid key")
}
}
73 changes: 73 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package config

import (
"errors"
"os"

"gopkg.in/yaml.v3"
)

type ConfigYAML struct {
KeyPath string `yaml:"key_path"`
CertPath string `yaml:"cert_path"`
DBPath string `yaml:"db_path"`
Port int `yaml:"port"`
Pebblenotificationsenabled bool `yaml:"pebble_notifications"`
}

type Config struct {
Key []byte
Cert []byte
DBPath string
Port int
PebbleNotificationsEnabled bool
}

// Validate opens and processes the given yaml file, and catches errors in the process
func Validate(filePath string) (Config, error) {
validationErr := errors.New("config file validation failed: ")
config := Config{}
configYaml, err := os.ReadFile(filePath)
if err != nil {
return config, errors.Join(validationErr, err)
}
c := ConfigYAML{}
if err := yaml.Unmarshal(configYaml, &c); err != nil {
return config, errors.Join(validationErr, err)
}
if c.CertPath == "" {
return config, errors.Join(validationErr, errors.New("`cert_path` is empty"))
}
cert, err := os.ReadFile(c.CertPath)
if err != nil {
return config, errors.Join(validationErr, err)
}
if c.KeyPath == "" {
return config, errors.Join(validationErr, errors.New("`key_path` is empty"))
}
key, err := os.ReadFile(c.KeyPath)
if err != nil {
return config, errors.Join(validationErr, err)
}
if c.DBPath == "" {
return config, errors.Join(validationErr, errors.New("`db_path` is empty"))
}
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)
}
if c.Port == 0 {
return config, errors.Join(validationErr, errors.New("`port` is empty"))
}

config.Cert = cert
config.Key = key
config.DBPath = c.DBPath
config.Port = c.Port
config.PebbleNotificationsEnabled = c.Pebblenotificationsenabled
return config, nil
}
Loading

0 comments on commit e6db72b

Please sign in to comment.