From 9b7b7e9f076224bd83a515c074cea645af343911 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 14 Oct 2024 11:57:05 +0300 Subject: [PATCH 1/9] Add SAN option Signed-off-by: nyagamunene --- service.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/service.go b/service.go index dc6d839..388bf57 100644 --- a/service.go +++ b/service.go @@ -12,6 +12,7 @@ import ( "encoding/asn1" "encoding/pem" "math/big" + "net" "time" "github.com/absmach/certs/errors" @@ -38,6 +39,15 @@ const ( downloadTokenExpiry = time.Minute * 5 ) +// SAN configuration variables. +var ( + sanDNSNames = []string{"localhost"} + sanIPAddresses = []net.IP{ + net.ParseIP("192.168.100.4"), + net.ParseIP("164.90.178.85"), + } +) + type CertType int const ( @@ -490,9 +500,12 @@ func (s *service) generateRootCA(ctx context.Context) (*CA, error) { }, NotBefore: time.Now(), NotAfter: time.Now().Add(RootCAValidityPeriod), - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCRLSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, BasicConstraintsValid: true, IsCA: true, + DNSNames: sanDNSNames, + IPAddresses: sanIPAddresses, } certBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, certTemplate, &rootKey.PublicKey, rootKey) @@ -505,7 +518,7 @@ func (s *service) generateRootCA(ctx context.Context) (*CA, error) { return nil, err } - if err != s.saveCA(ctx, cert, rootKey, RootCA) { + if err := s.saveCA(ctx, cert, rootKey, RootCA); err != nil { return nil, err } @@ -563,10 +576,12 @@ func (s *service) createIntermediateCA(ctx context.Context, rootCA *CA) (*CA, er }, NotBefore: time.Now(), NotAfter: time.Now().Add(IntermediateCAVAlidityPeriod), - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCRLSign, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, BasicConstraintsValid: true, IsCA: true, + DNSNames: sanDNSNames, + IPAddresses: sanIPAddresses, } certBytes, err := x509.CreateCertificate(rand.Reader, &template, rootCA.Certificate, &intermediateKey.PublicKey, rootCA.PrivateKey) From e5afbba971eab6de5b3976be1ee56ba7b0d5071c Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 14 Oct 2024 14:10:31 +0300 Subject: [PATCH 2/9] Make SAN dynamic Signed-off-by: nyagamunene --- docker/config.yml | 25 ++++++++ docker/docker-compose.yml | 2 + go.mod | 2 +- service.go | 124 ++++++++++++++++++++++++-------------- 4 files changed, 108 insertions(+), 45 deletions(-) create mode 100644 docker/config.yml diff --git a/docker/config.yml b/docker/config.yml new file mode 100644 index 0000000..3cb7965 --- /dev/null +++ b/docker/config.yml @@ -0,0 +1,25 @@ +# Copyright (c) Abstract Machines +# SPDX-License-Identifier: Apache-2.0 + +ca: + common_name: "AbstractMachines_Selfsigned_ca" + organization: + - "AbstractMacines" + organizational_unit: + - "AbstractMachines_ca" + country: + - "Sirbea" + province: + - "Sirbea" + locality: + - "Sirbea" + street_address: + - "Sirbea" + postal_code: + - "Sirbea" + dns_names: + - "localhost" + ip_addresses: + - "192.168.100.4" + - "164.90.178.85" + validity_period: "8760h" diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 71b7a57..f22ee7e 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -34,6 +34,8 @@ services: ports: - ${AM_CERTS_HTTP_PORT}:${AM_CERTS_HTTP_PORT} - ${AM_CERTS_GRPC_PORT}:${AM_CERTS_GRPC_PORT} + volumes: + - ./config.yml:/config/config.yml certs-db: image: postgres:16.2-alpine diff --git a/go.mod b/go.mod index d193323..9c7a447 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,7 @@ require ( golang.org/x/sync v0.8.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 + gopkg.in/yaml.v2 v2.4.0 moul.io/http2curl v1.0.0 ) @@ -87,6 +88,5 @@ require ( golang.org/x/text v0.17.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/service.go b/service.go index 388bf57..8a9184b 100644 --- a/service.go +++ b/service.go @@ -13,22 +13,17 @@ import ( "encoding/pem" "math/big" "net" + "os" "time" "github.com/absmach/certs/errors" "github.com/golang-jwt/jwt" "golang.org/x/crypto/ocsp" + "gopkg.in/yaml.v2" ) const ( - CommonName = "AbstractMachines_Selfsigned_ca" Organization = "AbstractMacines" - OrganizationalUnit = "AbstractMachines_ca" - Country = "Sirbea" - Province = "Sirbea" - Locality = "Sirbea" - StreetAddress = "Sirbea" - PostalCode = "Sirbea" emailAddress = "info@abstractmachines.rs" PrivateKeyBytes = 2048 RootCAValidityPeriod = time.Hour * 24 * 365 // 365 days @@ -37,15 +32,7 @@ const ( rCertExpiryThreshold = time.Hour * 24 * 30 // 30 days iCertExpiryThreshold = time.Hour * 24 * 10 // 10 days downloadTokenExpiry = time.Minute * 5 -) - -// SAN configuration variables. -var ( - sanDNSNames = []string{"localhost"} - sanIPAddresses = []net.IP{ - net.ParseIP("192.168.100.4"), - net.ParseIP("164.90.178.85"), - } + configFile = "/config/config.yml" ) type CertType int @@ -96,6 +83,24 @@ type CA struct { SerialNumber string } +type CAConfig struct { + CommonName string `yaml:"common_name"` + Organization []string `yaml:"organization"` + OrganizationalUnit []string `yaml:"organizational_unit"` + Country []string `yaml:"country"` + Province []string `yaml:"province"` + Locality []string `yaml:"locality"` + StreetAddress []string `yaml:"street_address"` + PostalCode []string `yaml:"postal_code"` + DNSNames []string `yaml:"dns_names"` + IPAddresses []string `yaml:"ip_addresses"` + ValidityPeriod string `yaml:"validity_period"` +} + +type Config struct { + CA CAConfig `yaml:"ca"` +} + var ( serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128) ErrNotFound = errors.New("entity not found") @@ -134,6 +139,12 @@ var _ Service = (*service)(nil) func NewService(ctx context.Context, repo Repository) (Service, error) { var svc service + + config, err := LoadConfig(configFile) + if err != nil { + return &svc, err + } + svc.repo = repo if err := svc.loadCACerts(ctx); err != nil { return &svc, err @@ -142,14 +153,14 @@ func NewService(ctx context.Context, repo Repository) (Service, error) { // check if root ca should be rotated rotateRoot := svc.shouldRotateCA(RootCA) if rotateRoot { - if err := svc.rotateCA(ctx, RootCA); err != nil { + if err := svc.rotateCA(ctx, RootCA, config); err != nil { return &svc, err } } rotateIntermediate := svc.shouldRotateCA(IntermediateCA) if rotateIntermediate { - if err := svc.rotateCA(ctx, IntermediateCA); err != nil { + if err := svc.rotateCA(ctx, IntermediateCA, config); err != nil { return &svc, err } } @@ -468,7 +479,22 @@ func (s *service) GetSigningCA(ctx context.Context, token string) (Certificate, return cert, nil } -func (s *service) generateRootCA(ctx context.Context) (*CA, error) { +func LoadConfig(filename string) (*Config, error) { + file, err := os.Open(filename) + if err != nil { + return nil, err + } + defer file.Close() + + var config Config + decoder := yaml.NewDecoder(file) + if err := decoder.Decode(&config); err != nil { + return nil, err + } + return &config, nil +} + +func (s *service) generateRootCA(ctx context.Context, config CAConfig) (*CA, error) { rootKey, err := rsa.GenerateKey(rand.Reader, PrivateKeyBytes) if err != nil { return nil, err @@ -482,14 +508,14 @@ func (s *service) generateRootCA(ctx context.Context) (*CA, error) { certTemplate := &x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ - Organization: []string{Organization}, - OrganizationalUnit: []string{OrganizationalUnit}, - Country: []string{Country}, - Province: []string{Province}, - Locality: []string{Locality}, - StreetAddress: []string{StreetAddress}, - PostalCode: []string{PostalCode}, - CommonName: CommonName, + CommonName: config.CommonName, + Organization: config.Organization, + OrganizationalUnit: config.OrganizationalUnit, + Country: config.Country, + Province: config.Province, + Locality: config.Locality, + StreetAddress: config.StreetAddress, + PostalCode: config.PostalCode, SerialNumber: serialNumber.String(), ExtraNames: []pkix.AttributeTypeAndValue{ { @@ -504,8 +530,8 @@ func (s *service) generateRootCA(ctx context.Context) (*CA, error) { ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, BasicConstraintsValid: true, IsCA: true, - DNSNames: sanDNSNames, - IPAddresses: sanIPAddresses, + DNSNames: config.DNSNames, + IPAddresses: parseIPs(config.IPAddresses), } certBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, certTemplate, &rootKey.PublicKey, rootKey) @@ -544,7 +570,7 @@ func (s *service) saveCA(ctx context.Context, cert *x509.Certificate, privateKey return nil } -func (s *service) createIntermediateCA(ctx context.Context, rootCA *CA) (*CA, error) { +func (s *service) createIntermediateCA(ctx context.Context, rootCA *CA, config CAConfig) (*CA, error) { intermediateKey, err := rsa.GenerateKey(rand.Reader, PrivateKeyBytes) if err != nil { return nil, err @@ -558,14 +584,14 @@ func (s *service) createIntermediateCA(ctx context.Context, rootCA *CA) (*CA, er template := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ - CommonName: CommonName, - Organization: []string{Organization}, - OrganizationalUnit: []string{OrganizationalUnit}, - Country: []string{Country}, - Province: []string{Province}, - Locality: []string{Locality}, - StreetAddress: []string{StreetAddress}, - PostalCode: []string{PostalCode}, + CommonName: config.CommonName, + Organization: config.Organization, + OrganizationalUnit: config.OrganizationalUnit, + Country: config.Country, + Province: config.Province, + Locality: config.Locality, + StreetAddress: config.StreetAddress, + PostalCode: config.PostalCode, SerialNumber: serialNumber.String(), ExtraNames: []pkix.AttributeTypeAndValue{ { @@ -580,8 +606,8 @@ func (s *service) createIntermediateCA(ctx context.Context, rootCA *CA) (*CA, er ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, BasicConstraintsValid: true, IsCA: true, - DNSNames: sanDNSNames, - IPAddresses: sanIPAddresses, + DNSNames: config.DNSNames, + IPAddresses: parseIPs(config.IPAddresses), } certBytes, err := x509.CreateCertificate(rand.Reader, &template, rootCA.Certificate, &intermediateKey.PublicKey, rootCA.PrivateKey) @@ -638,7 +664,7 @@ func (s *service) getSubject(options SubjectOptions) pkix.Name { return subject } -func (s *service) rotateCA(ctx context.Context, ctype CertType) error { +func (s *service) rotateCA(ctx context.Context, ctype CertType, config *Config) error { switch ctype { case RootCA: certificates, err := s.repo.GetCAs(ctx) @@ -650,12 +676,12 @@ func (s *service) rotateCA(ctx context.Context, ctype CertType) error { return err } } - newRootCA, err := s.generateRootCA(ctx) + newRootCA, err := s.generateRootCA(ctx, config.CA) if err != nil { return err } s.rootCA = newRootCA - newIntermediateCA, err := s.createIntermediateCA(ctx, newRootCA) + newIntermediateCA, err := s.createIntermediateCA(ctx, newRootCA, config.CA) if err != nil { return err } @@ -671,7 +697,7 @@ func (s *service) rotateCA(ctx context.Context, ctype CertType) error { return err } } - newIntermediateCA, err := s.createIntermediateCA(ctx, s.rootCA) + newIntermediateCA, err := s.createIntermediateCA(ctx, s.rootCA, config.CA) if err != nil { return err } @@ -771,3 +797,13 @@ func (s *service) loadCACerts(ctx context.Context) error { } return nil } + +func parseIPs(ipStrings []string) []net.IP { + var ips []net.IP + for _, ipString := range ipStrings { + if ip := net.ParseIP(ipString); ip != nil { + ips = append(ips, ip) + } + } + return ips +} From 29941f71ab278e36d7631a8ff8c43a7719e39d77 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 14 Oct 2024 14:56:24 +0300 Subject: [PATCH 3/9] Load config file in main.go Signed-off-by: nyagamunene --- cmd/certs/main.go | 29 ++++++++++++++++++++++++++--- docker/config.yml | 2 +- service.go | 25 +------------------------ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/cmd/certs/main.go b/cmd/certs/main.go index 0ad9dca..185bd3b 100644 --- a/cmd/certs/main.go +++ b/cmd/certs/main.go @@ -32,6 +32,7 @@ import ( "golang.org/x/sync/errgroup" "google.golang.org/grpc" "google.golang.org/grpc/reflection" + "gopkg.in/yaml.v2" ) const ( @@ -43,6 +44,7 @@ const ( defDB = "certs" defSvcHTTPPort = "9010" defSvcGRPCPort = "7012" + configFile = "/config/config.yml" ) type config struct { @@ -99,7 +101,13 @@ func main() { logger.Error(fmt.Sprintf("failed to load %s gRPC server configuration : %s", svcName, err)) } - svc, err := newService(ctx, db, tracer, logger, dbConfig) + config, err := LoadConfig(configFile) + if err != nil { + logger.Error(fmt.Sprintf("failed to load CA config file : %s", err)) + return + } + + svc, err := newService(ctx, db, tracer, logger, dbConfig, config) if err != nil { logger.Error(fmt.Sprintf("failed to create %s service: %s", svcName, err)) return @@ -136,10 +144,10 @@ func main() { } } -func newService(ctx context.Context, db *sqlx.DB, tracer trace.Tracer, logger *slog.Logger, dbConfig pgClient.Config) (certs.Service, error) { +func newService(ctx context.Context, db *sqlx.DB, tracer trace.Tracer, logger *slog.Logger, dbConfig pgClient.Config, config *certs.Config) (certs.Service, error) { database := postgres.NewDatabase(db, dbConfig, tracer) repo := cpostgres.NewRepository(database) - svc, err := certs.NewService(ctx, repo) + svc, err := certs.NewService(ctx, repo, config) if err != nil { return nil, err } @@ -163,3 +171,18 @@ func initLogger(levelText string) (*slog.Logger, error) { return slog.New(logHandler), nil } + +func LoadConfig(filename string) (*certs.Config, error) { + file, err := os.Open(filename) + if err != nil { + return nil, err + } + defer file.Close() + + var config certs.Config + decoder := yaml.NewDecoder(file) + if err := decoder.Decode(&config); err != nil { + return nil, err + } + return &config, nil +} diff --git a/docker/config.yml b/docker/config.yml index 3cb7965..26ba63d 100644 --- a/docker/config.yml +++ b/docker/config.yml @@ -8,7 +8,7 @@ ca: organizational_unit: - "AbstractMachines_ca" country: - - "Sirbea" + - "France" province: - "Sirbea" locality: diff --git a/service.go b/service.go index 8a9184b..8f4e521 100644 --- a/service.go +++ b/service.go @@ -13,13 +13,11 @@ import ( "encoding/pem" "math/big" "net" - "os" "time" "github.com/absmach/certs/errors" "github.com/golang-jwt/jwt" "golang.org/x/crypto/ocsp" - "gopkg.in/yaml.v2" ) const ( @@ -32,7 +30,6 @@ const ( rCertExpiryThreshold = time.Hour * 24 * 30 // 30 days iCertExpiryThreshold = time.Hour * 24 * 10 // 10 days downloadTokenExpiry = time.Minute * 5 - configFile = "/config/config.yml" ) type CertType int @@ -137,14 +134,9 @@ type service struct { var _ Service = (*service)(nil) -func NewService(ctx context.Context, repo Repository) (Service, error) { +func NewService(ctx context.Context, repo Repository, config *Config) (Service, error) { var svc service - config, err := LoadConfig(configFile) - if err != nil { - return &svc, err - } - svc.repo = repo if err := svc.loadCACerts(ctx); err != nil { return &svc, err @@ -479,21 +471,6 @@ func (s *service) GetSigningCA(ctx context.Context, token string) (Certificate, return cert, nil } -func LoadConfig(filename string) (*Config, error) { - file, err := os.Open(filename) - if err != nil { - return nil, err - } - defer file.Close() - - var config Config - decoder := yaml.NewDecoder(file) - if err := decoder.Decode(&config); err != nil { - return nil, err - } - return &config, nil -} - func (s *service) generateRootCA(ctx context.Context, config CAConfig) (*CA, error) { rootKey, err := rsa.GenerateKey(rand.Reader, PrivateKeyBytes) if err != nil { From 5f8e28c6021cddb8adf48e62f4ccaff1d8ea466f Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 14 Oct 2024 15:04:28 +0300 Subject: [PATCH 4/9] Fix tests Signed-off-by: nyagamunene --- certs_test.go | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/certs_test.go b/certs_test.go index 23cef22..172da24 100644 --- a/certs_test.go +++ b/certs_test.go @@ -25,14 +25,21 @@ import ( const serialNumber = "serial number" -var invalidToken = "123" +var ( + invalidToken = "123" + config = certs.Config{ + CA: certs.CAConfig{ + CommonName: "test", + }, + } +) func TestIssueCert(t *testing.T) { cRepo := new(mocks.MockRepository) repoCall := cRepo.On("GetCAs", mock.Anything).Return([]certs.Certificate{}, nil) repoCall1 := cRepo.On("CreateCert", mock.Anything, mock.Anything).Return(nil) - svc, err := certs.NewService(context.Background(), cRepo) + svc, err := certs.NewService(context.Background(), cRepo, &config) require.NoError(t, err) repoCall.Unset() repoCall1.Unset() @@ -76,7 +83,7 @@ func TestRevokeCert(t *testing.T) { repoCall := cRepo.On("GetCAs", mock.Anything).Return([]certs.Certificate{}, nil) repoCall1 := cRepo.On("CreateCert", mock.Anything, mock.Anything).Return(nil) - svc, err := certs.NewService(context.Background(), cRepo) + svc, err := certs.NewService(context.Background(), cRepo, &config) require.NoError(t, err) repoCall.Unset() repoCall1.Unset() @@ -128,7 +135,7 @@ func TestGetCertDownloadToken(t *testing.T) { repoCall := cRepo.On("GetCAs", mock.Anything).Return([]certs.Certificate{}, nil) repoCall1 := cRepo.On("CreateCert", mock.Anything, mock.Anything).Return(nil) - svc, err := certs.NewService(context.Background(), cRepo) + svc, err := certs.NewService(context.Background(), cRepo, &config) require.NoError(t, err) repoCall.Unset() repoCall1.Unset() @@ -162,7 +169,7 @@ func TestGetCert(t *testing.T) { repoCall := cRepo.On("GetCAs", mock.Anything).Return([]certs.Certificate{}, nil) repoCall1 := cRepo.On("CreateCert", mock.Anything, mock.Anything).Return(nil) - svc, err := certs.NewService(context.Background(), cRepo) + svc, err := certs.NewService(context.Background(), cRepo, &config) require.NoError(t, err) repoCall.Unset() repoCall1.Unset() @@ -257,7 +264,7 @@ func TestRenewCert(t *testing.T) { repoCall := cRepo.On("GetCAs", mock.Anything).Return([]certs.Certificate{}, nil) repoCall1 := cRepo.On("CreateCert", mock.Anything, mock.Anything).Return(nil) - svc, err := certs.NewService(context.Background(), cRepo) + svc, err := certs.NewService(context.Background(), cRepo, &config) require.NoError(t, err) repoCall.Unset() repoCall1.Unset() @@ -349,7 +356,7 @@ func TestGetEntityID(t *testing.T) { repoCall := cRepo.On("GetCAs", mock.Anything).Return([]certs.Certificate{}, nil) repoCall1 := cRepo.On("CreateCert", mock.Anything, mock.Anything).Return(nil) - svc, err := certs.NewService(context.Background(), cRepo) + svc, err := certs.NewService(context.Background(), cRepo, &config) require.NoError(t, err) repoCall.Unset() repoCall1.Unset() @@ -380,7 +387,7 @@ func TestListCerts(t *testing.T) { repoCall := cRepo.On("GetCAs", mock.Anything).Return([]certs.Certificate{}, nil) repoCall1 := cRepo.On("CreateCert", mock.Anything, mock.Anything).Return(nil) - svc, err := certs.NewService(context.Background(), cRepo) + svc, err := certs.NewService(context.Background(), cRepo, &config) require.NoError(t, err) repoCall.Unset() repoCall1.Unset() @@ -435,7 +442,7 @@ func TestGenerateCRL(t *testing.T) { {Type: certs.IntermediateCA, Certificate: pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}), Key: pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)})}, }, nil) repoCall1 := cRepo.On("CreateCert", mock.Anything, mock.Anything).Return(nil) - svc, err := certs.NewService(context.Background(), cRepo) + svc, err := certs.NewService(context.Background(), cRepo, &config) require.NoError(t, err) repoCall.Unset() repoCall1.Unset() From 5ee1a18f3be7a966cee65930eddc8048f4c85e16 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 14 Oct 2024 15:55:35 +0300 Subject: [PATCH 5/9] Address comments Signed-off-by: nyagamunene --- cmd/certs/main.go | 18 +------------- config.go | 61 +++++++++++++++++++++++++++++++++++++++++++++++ docker/config.yml | 43 ++++++++++++++++----------------- service.go | 38 +++++++++-------------------- 4 files changed, 94 insertions(+), 66 deletions(-) create mode 100644 config.go diff --git a/cmd/certs/main.go b/cmd/certs/main.go index 185bd3b..e596483 100644 --- a/cmd/certs/main.go +++ b/cmd/certs/main.go @@ -32,7 +32,6 @@ import ( "golang.org/x/sync/errgroup" "google.golang.org/grpc" "google.golang.org/grpc/reflection" - "gopkg.in/yaml.v2" ) const ( @@ -101,7 +100,7 @@ func main() { logger.Error(fmt.Sprintf("failed to load %s gRPC server configuration : %s", svcName, err)) } - config, err := LoadConfig(configFile) + config, err := certs.LoadConfig(configFile) if err != nil { logger.Error(fmt.Sprintf("failed to load CA config file : %s", err)) return @@ -171,18 +170,3 @@ func initLogger(levelText string) (*slog.Logger, error) { return slog.New(logHandler), nil } - -func LoadConfig(filename string) (*certs.Config, error) { - file, err := os.Open(filename) - if err != nil { - return nil, err - } - defer file.Close() - - var config certs.Config - decoder := yaml.NewDecoder(file) - if err := decoder.Decode(&config); err != nil { - return nil, err - } - return &config, nil -} diff --git a/config.go b/config.go new file mode 100644 index 0000000..e7db21d --- /dev/null +++ b/config.go @@ -0,0 +1,61 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package certs + +import ( + "net" + "os" + + "gopkg.in/yaml.v2" +) + +type CAConfig struct { + CommonName string `yaml:"common_name"` + Organization []string `yaml:"organization"` + OrganizationalUnit []string `yaml:"organizational_unit"` + Country []string `yaml:"country"` + Province []string `yaml:"province"` + Locality []string `yaml:"locality"` + StreetAddress []string `yaml:"street_address"` + PostalCode []string `yaml:"postal_code"` + DNSNames []string `yaml:"dns_names"` + IPAddresses []string `yaml:"ip_addresses"` + ValidityPeriod string `yaml:"validity_period"` +} + +func LoadConfig(filename string) (*Config, error) { + file, err := os.Open(filename) + if err != nil { + return nil, err + } + defer file.Close() + + var config CAConfig + decoder := yaml.NewDecoder(file) + if err := decoder.Decode(&config); err != nil { + return nil, err + } + return &Config{ + CommonName: config.CommonName, + Organization: config.Organization, + OrganizationalUnit: config.OrganizationalUnit, + Country: config.Country, + Province: config.Province, + Locality: config.Locality, + StreetAddress: config.StreetAddress, + PostalCode: config.PostalCode, + DNSNames: config.DNSNames, + IPAddresses: parseIPs(config.IPAddresses), + }, nil +} + +func parseIPs(ipStrings []string) []net.IP { + var ips []net.IP + for _, ipString := range ipStrings { + if ip := net.ParseIP(ipString); ip != nil { + ips = append(ips, ip) + } + } + return ips +} diff --git a/docker/config.yml b/docker/config.yml index 26ba63d..9a851d0 100644 --- a/docker/config.yml +++ b/docker/config.yml @@ -1,25 +1,24 @@ # Copyright (c) Abstract Machines # SPDX-License-Identifier: Apache-2.0 -ca: - common_name: "AbstractMachines_Selfsigned_ca" - organization: - - "AbstractMacines" - organizational_unit: - - "AbstractMachines_ca" - country: - - "France" - province: - - "Sirbea" - locality: - - "Sirbea" - street_address: - - "Sirbea" - postal_code: - - "Sirbea" - dns_names: - - "localhost" - ip_addresses: - - "192.168.100.4" - - "164.90.178.85" - validity_period: "8760h" +common_name: "AbstractMachines_Selfsigned_ca" +organization: + - "AbstractMacines" +organizational_unit: + - "AbstractMachines_ca" +country: + - "France" +province: + - "Sirbea" +locality: + - "Sirbea" +street_address: + - "Sirbea" +postal_code: + - "Sirbea" +dns_names: + - "localhost" +ip_addresses: + - "192.168.100.4" + - "164.90.178.85" +validity_period: "8760h" diff --git a/service.go b/service.go index 8f4e521..048812d 100644 --- a/service.go +++ b/service.go @@ -80,7 +80,7 @@ type CA struct { SerialNumber string } -type CAConfig struct { +type Config struct { CommonName string `yaml:"common_name"` Organization []string `yaml:"organization"` OrganizationalUnit []string `yaml:"organizational_unit"` @@ -90,14 +90,10 @@ type CAConfig struct { StreetAddress []string `yaml:"street_address"` PostalCode []string `yaml:"postal_code"` DNSNames []string `yaml:"dns_names"` - IPAddresses []string `yaml:"ip_addresses"` + IPAddresses []net.IP `yaml:"ip_addresses"` ValidityPeriod string `yaml:"validity_period"` } -type Config struct { - CA CAConfig `yaml:"ca"` -} - var ( serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128) ErrNotFound = errors.New("entity not found") @@ -143,15 +139,13 @@ func NewService(ctx context.Context, repo Repository, config *Config) (Service, } // check if root ca should be rotated - rotateRoot := svc.shouldRotateCA(RootCA) - if rotateRoot { + if svc.shouldRotateCA(RootCA) { if err := svc.rotateCA(ctx, RootCA, config); err != nil { return &svc, err } } - rotateIntermediate := svc.shouldRotateCA(IntermediateCA) - if rotateIntermediate { + if svc.shouldRotateCA(IntermediateCA) { if err := svc.rotateCA(ctx, IntermediateCA, config); err != nil { return &svc, err } @@ -471,7 +465,7 @@ func (s *service) GetSigningCA(ctx context.Context, token string) (Certificate, return cert, nil } -func (s *service) generateRootCA(ctx context.Context, config CAConfig) (*CA, error) { +func (s *service) generateRootCA(ctx context.Context, config Config) (*CA, error) { rootKey, err := rsa.GenerateKey(rand.Reader, PrivateKeyBytes) if err != nil { return nil, err @@ -508,7 +502,7 @@ func (s *service) generateRootCA(ctx context.Context, config CAConfig) (*CA, err BasicConstraintsValid: true, IsCA: true, DNSNames: config.DNSNames, - IPAddresses: parseIPs(config.IPAddresses), + IPAddresses: config.IPAddresses, } certBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, certTemplate, &rootKey.PublicKey, rootKey) @@ -547,7 +541,7 @@ func (s *service) saveCA(ctx context.Context, cert *x509.Certificate, privateKey return nil } -func (s *service) createIntermediateCA(ctx context.Context, rootCA *CA, config CAConfig) (*CA, error) { +func (s *service) createIntermediateCA(ctx context.Context, rootCA *CA, config Config) (*CA, error) { intermediateKey, err := rsa.GenerateKey(rand.Reader, PrivateKeyBytes) if err != nil { return nil, err @@ -584,7 +578,7 @@ func (s *service) createIntermediateCA(ctx context.Context, rootCA *CA, config C BasicConstraintsValid: true, IsCA: true, DNSNames: config.DNSNames, - IPAddresses: parseIPs(config.IPAddresses), + IPAddresses: config.IPAddresses, } certBytes, err := x509.CreateCertificate(rand.Reader, &template, rootCA.Certificate, &intermediateKey.PublicKey, rootCA.PrivateKey) @@ -653,12 +647,12 @@ func (s *service) rotateCA(ctx context.Context, ctype CertType, config *Config) return err } } - newRootCA, err := s.generateRootCA(ctx, config.CA) + newRootCA, err := s.generateRootCA(ctx, *config) if err != nil { return err } s.rootCA = newRootCA - newIntermediateCA, err := s.createIntermediateCA(ctx, newRootCA, config.CA) + newIntermediateCA, err := s.createIntermediateCA(ctx, newRootCA, *config) if err != nil { return err } @@ -674,7 +668,7 @@ func (s *service) rotateCA(ctx context.Context, ctype CertType, config *Config) return err } } - newIntermediateCA, err := s.createIntermediateCA(ctx, s.rootCA, config.CA) + newIntermediateCA, err := s.createIntermediateCA(ctx, s.rootCA, *config) if err != nil { return err } @@ -774,13 +768,3 @@ func (s *service) loadCACerts(ctx context.Context) error { } return nil } - -func parseIPs(ipStrings []string) []net.IP { - var ips []net.IP - for _, ipString := range ipStrings { - if ip := net.ParseIP(ipString); ip != nil { - ips = append(ips, ip) - } - } - return ips -} From c077e990f51a12015405e6bcf891ede2841f6eab Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 14 Oct 2024 16:03:18 +0300 Subject: [PATCH 6/9] Update config file Signed-off-by: nyagamunene --- certs_test.go | 4 +--- docker/config.yml | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/certs_test.go b/certs_test.go index 172da24..28616e2 100644 --- a/certs_test.go +++ b/certs_test.go @@ -28,9 +28,7 @@ const serialNumber = "serial number" var ( invalidToken = "123" config = certs.Config{ - CA: certs.CAConfig{ - CommonName: "test", - }, + CommonName: "test", } ) diff --git a/docker/config.yml b/docker/config.yml index 9a851d0..c3f4c94 100644 --- a/docker/config.yml +++ b/docker/config.yml @@ -19,6 +19,5 @@ postal_code: dns_names: - "localhost" ip_addresses: - - "192.168.100.4" - - "164.90.178.85" + - "localhost" validity_period: "8760h" From ee7aec8e719b791b8198cca40409ddf24de2f427 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 14 Oct 2024 16:37:46 +0300 Subject: [PATCH 7/9] Update config file Signed-off-by: nyagamunene --- docker/config.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/config.yml b/docker/config.yml index c3f4c94..559964c 100644 --- a/docker/config.yml +++ b/docker/config.yml @@ -9,13 +9,13 @@ organizational_unit: country: - "France" province: - - "Sirbea" + - "Paris" locality: - - "Sirbea" + - "Quai de Valmy" street_address: - - "Sirbea" + - "141 Quai de Valmy 10" postal_code: - - "Sirbea" + - "75010 Paris" dns_names: - "localhost" ip_addresses: From cdece0660d565cb7ad0022bf8773ab3b669cccfe Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 14 Oct 2024 16:47:43 +0300 Subject: [PATCH 8/9] remove street address Signed-off-by: nyagamunene --- docker/config.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/docker/config.yml b/docker/config.yml index 559964c..2c2f702 100644 --- a/docker/config.yml +++ b/docker/config.yml @@ -12,8 +12,6 @@ province: - "Paris" locality: - "Quai de Valmy" -street_address: - - "141 Quai de Valmy 10" postal_code: - "75010 Paris" dns_names: From 2e306f914cffe02890c0a3e20547d23f140e55fc Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 14 Oct 2024 16:54:16 +0300 Subject: [PATCH 9/9] Rename should rotate method Signed-off-by: nyagamunene --- service.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/service.go b/service.go index 048812d..d2d1bbc 100644 --- a/service.go +++ b/service.go @@ -139,13 +139,13 @@ func NewService(ctx context.Context, repo Repository, config *Config) (Service, } // check if root ca should be rotated - if svc.shouldRotateCA(RootCA) { + if svc.shouldRotate(RootCA) { if err := svc.rotateCA(ctx, RootCA, config); err != nil { return &svc, err } } - if svc.shouldRotateCA(IntermediateCA) { + if svc.shouldRotate(IntermediateCA) { if err := svc.rotateCA(ctx, IntermediateCA, config); err != nil { return &svc, err } @@ -681,7 +681,7 @@ func (s *service) rotateCA(ctx context.Context, ctype CertType, config *Config) return nil } -func (s *service) shouldRotateCA(ctype CertType) bool { +func (s *service) shouldRotate(ctype CertType) bool { switch ctype { case RootCA: if s.rootCA == nil {