diff --git a/Makefile.am b/Makefile.am index 820bcd8..eab40eb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,12 +12,12 @@ GOFLAGS = -v -ldflags='-s -w -X helmet/certmanager/internal/config.buildVersion= certmanagerd_SOURCES = cmd/certmanagerd/main.go certmanagerd$(EXEEXT): $(certmanagerd_SOURCES) $(EXTRA_certmanagerd_SOURCES) - env CGO_ENABLED=0 $(GO) build $(GOFLAGS) -o certmanagerd$(EXEEXT) $(certmanagerd_SOURCES) + env CGO_ENABLED=1 $(GO) build $(GOFLAGS) -o certmanagerd$(EXEEXT) $(certmanagerd_SOURCES) certmanagerctl_SOURCES = cmd/certmanagerctl/main.go certmanagerctl$(EXEEXT): $(certmanagerctl_SOURCES) $(EXTRA_certmanagerd_SOURCES) - env CGO_ENABLED=0 $(GO) build $(GOFLAGS) -o certmanagerctl$(EXEEXT) $(certmanagerctl_SOURCES) + env CGO_ENABLED=1 $(GO) build $(GOFLAGS) -o certmanagerctl$(EXEEXT) $(certmanagerctl_SOURCES) EXTRA_certmanagerd_SOURCES = @@ -29,8 +29,6 @@ grpc: mkdir -p $(GENDIR) protoc --proto_path=proto --go_out=$(GENDIR) --go-grpc_out=$(GENDIR) proto/certmanagercontrol.proto - - SYSTEMD_LIBDIR = /lib/systemd/system install-data-local: @@ -49,7 +47,10 @@ gformat: done run: - env CGO_ENABLED=0 $(GO) run $(GOFLAGS) ./cmd/certmanagerd/... --daemon=false + test -z $(DESTDIR)$(SRV_LOGDIR) || $(MKDIR_P) $(DESTDIR)$(SRV_LOGDIR) + test -z $(DESTDIR)$(SRV_RUNDIR) || $(MKDIR_P) $(DESTDIR)$(SRV_RUNDIR) + test -z $(DESTDIR)$(SRV_DATADIR) || $(MKDIR_P) $(DESTDIR)$(SRV_DATADIR) + env CGO_ENABLED=1 $(GO) run $(GOFLAGS) ./cmd/certmanagerd/... --daemon=false distclean-local: clean rm -rf autom4te.cache @@ -58,4 +59,5 @@ clean-local: rm -rf autom4te.cache rm -f cmd/certmanagerd/certmanagerd rm -f cmd/certmanagerctl/certmanagerctl - rm -rf tmp/ + rm -rf tmp.*/ + diff --git a/Makefile.in b/Makefile.in index 3c2430b..1f3bd89 100644 --- a/Makefile.in +++ b/Makefile.in @@ -795,9 +795,9 @@ uninstall-am: uninstall-binPROGRAMS uninstall-sbinPROGRAMS .PRECIOUS: Makefile certmanagerd$(EXEEXT): $(certmanagerd_SOURCES) $(EXTRA_certmanagerd_SOURCES) - env CGO_ENABLED=0 $(GO) build $(GOFLAGS) -o certmanagerd$(EXEEXT) $(certmanagerd_SOURCES) + env CGO_ENABLED=1 $(GO) build $(GOFLAGS) -o certmanagerd$(EXEEXT) $(certmanagerd_SOURCES) certmanagerctl$(EXEEXT): $(certmanagerctl_SOURCES) $(EXTRA_certmanagerd_SOURCES) - env CGO_ENABLED=0 $(GO) build $(GOFLAGS) -o certmanagerctl$(EXEEXT) $(certmanagerctl_SOURCES) + env CGO_ENABLED=1 $(GO) build $(GOFLAGS) -o certmanagerctl$(EXEEXT) $(certmanagerctl_SOURCES) grpc: mkdir -p $(GENDIR) protoc --proto_path=proto --go_out=$(GENDIR) --go-grpc_out=$(GENDIR) proto/certmanagercontrol.proto @@ -817,7 +817,10 @@ gformat: done run: - env CGO_ENABLED=0 $(GO) run $(GOFLAGS) ./cmd/certmanagerd/... --daemon=false + test -z $(DESTDIR)$(SRV_LOGDIR) || $(MKDIR_P) $(DESTDIR)$(SRV_LOGDIR) + test -z $(DESTDIR)$(SRV_RUNDIR) || $(MKDIR_P) $(DESTDIR)$(SRV_RUNDIR) + test -z $(DESTDIR)$(SRV_DATADIR) || $(MKDIR_P) $(DESTDIR)$(SRV_DATADIR) + env CGO_ENABLED=1 $(GO) run $(GOFLAGS) ./cmd/certmanagerd/... --daemon=false distclean-local: clean rm -rf autom4te.cache @@ -826,7 +829,7 @@ clean-local: rm -rf autom4te.cache rm -f cmd/certmanagerd/certmanagerd rm -f cmd/certmanagerctl/certmanagerctl - rm -rf tmp/ + rm -rf tmp.*/ # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/go.mod b/go.mod index 4ab0f49..a6dcb69 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/jmoiron/sqlx v1.4.0 github.com/mattn/go-sqlite3 v1.14.22 github.com/sirupsen/logrus v1.9.3 + github.com/stretchr/testify v1.9.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 @@ -18,6 +19,7 @@ require ( github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -31,6 +33,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect golang.org/x/arch v0.8.0 // indirect diff --git a/internal/config/path.go b/internal/config/path.go index ee8ff56..f75c664 100644 --- a/internal/config/path.go +++ b/internal/config/path.go @@ -1,9 +1,8 @@ package config const ( - confdirPath = "/home/ziggi/projects/certman/etc/certmanager" - rundirPath = "/home/ziggi/projects/certman/tmp.run" - logdirPath = "/home/ziggi/projects/certman/tmp.log" - datadirPath = "/home/ziggi/projects/certman/tmp.data" + confdirPath = "/home/ziggi/projects/certman/etc/certmanager" + rundirPath = "/home/ziggi/projects/certman/tmp.run" + logdirPath = "/home/ziggi/projects/certman/tmp.log" + datadirPath = "/home/ziggi/projects/certman/tmp.data" ) - diff --git a/internal/database/database.go b/internal/database/database.go index 1f0faa2..62be318 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -10,49 +10,51 @@ import ( ) const schema = ` - --- DROP TABLE IF EXISTS manifests; - CREATE TABLE IF NOT EXISTS manifests ( - id VARCHAR(255) NOT NULL, - name VARCHAR(255) NOT NULL, - reference VARCHAR(255) NOT NULL, - contentType VARCHAR(255) NOT NULL, - payload VARCHAR(4096) NOT NULL + DROP TABLE IF EXISTS issuer; + CREATE TABLE IF NOT EXISTS issuer ( + id INT NOT NULL, + name TEXT NOT NULL, + cert TEXT NOT NULL, + key TEXT NOT NULL, + revoked BOOL ); - CREATE INDEX IF NOT EXISTS manifest_index - ON manifests(name, reference); + CREATE INDEX IF NOT EXISTS issuer_index + ON issuer(id, name); - CREATE TABLE IF NOT EXISTS blobs ( - id VARCHAR(255) NOT NULL, - name VARCHAR(255) NOT NULL, - digest VARCHAR(255) NOT NULL, - used INTEGER + DROP TABLE IF EXISTS service; + CREATE TABLE IF NOT EXISTS service ( + id INT NOT NULL, + issuer_id INT NOT NULL, + name TEXT NOT NULL, + cert TEXT NOT NULL, + key TEXT NOT NULL, + revoked BOOL ); - - CREATE INDEX IF NOT EXISTS blobs_index - ON blobs(name, digest); - - + CREATE INDEX IF NOT EXISTS service_index + ON issuer(id, name); ` type Database struct { datapath string db *sqlx.DB - log *logger.Logger + log *logger.Logger } func NewDatabase(datapath string) (*Database, error) { - var err error + var err error db := &Database{ datapath: datapath, } - db.log = logger.NewLogger("database") - return db, err + db.log = logger.NewLogger("database") + return db, err } func (db *Database) InitDatabase() error { var err error dbPath := filepath.Join(db.datapath, "certmanager.db") + db.log.Infof("Initialize database %s", dbPath) + db.db, err = sqlx.Open("sqlite3", dbPath) if err != nil { return err diff --git a/internal/database/issuer.go b/internal/database/issuer.go new file mode 100644 index 0000000..4e37e3e --- /dev/null +++ b/internal/database/issuer.go @@ -0,0 +1,101 @@ +package database + +import ( + "context" + + "certmanager/internal/descriptor" + "certmanager/pkg/auxid" + + _ "github.com/mattn/go-sqlite3" +) + +func (db *Database) InsertIssuer(ctx context.Context, issuer *descriptor.Issuer) (int64, error) { + var err error + var res int64 + issuer.ID = auxid.GenID() + request := `INSERT INTO issuer(id, name, cert, key, revoked) + VALUES ($1, $2, $3, $4, $5)` + _, err = db.db.Exec(request, issuer.ID, issuer.Name, issuer.Cert, issuer.Key, issuer.Revoked) + if err != nil { + return res, err + } + res = issuer.ID + return res, err +} + +func (db *Database) UpdateIssuerByID(ctx context.Context, issuerID int64, issuer *descriptor.Issuer) error { + var err error + request := `UPDATE issuer SET revoked = $1 WHERE id = $2` + _, err = db.db.Exec(request, issuer.Revoked, issuerID) + if err != nil { + return err + } + return err +} + +func (db *Database) ListIssuers(ctx context.Context) ([]descriptor.Issuer, error) { + var err error + request := `SELECT id, name, revoked FROM issuer` + res := make([]descriptor.Issuer, 0) + err = db.db.Select(&res, request) + if err != nil { + return res, err + } + return res, err +} + +func (db *Database) GetIssuerByID(ctx context.Context, issuerID int64) (bool, *descriptor.Issuer, error) { + var err error + var res *descriptor.Issuer + var exists bool + request := `SELECT id, name, cert, key, revoked FROM issuer WHERE id = $1 LiMIT 1` + dbRes := make([]descriptor.Issuer, 0) + err = db.db.Select(&dbRes, request, issuerID) + if err != nil { + return exists, res, err + } + if len(dbRes) == 0 { + return exists, res, err + } + exists = true + res = &dbRes[0] + return exists, res, err +} + +func (db *Database) GetIssuerByName(ctx context.Context, issuerName string) (bool, *descriptor.Issuer, error) { + var err error + var res *descriptor.Issuer + var exists bool + request := `SELECT id, name, cert, key, revoked FROM issuer WHERE name = $1 LIMIT 1` + dbRes := make([]descriptor.Issuer, 0) + err = db.db.Select(&dbRes, request, issuerName) + if err != nil { + return exists, res, err + } + if len(dbRes) == 0 { + return exists, res, err + } + exists = true + res = &dbRes[0] + return exists, res, err +} + +func (db *Database) DeleteIssuerByID(ctx context.Context, issuerID int64) error { + var err error + request := `DELETE FROM issuer WHERE id = $1` + _, err = db.db.Exec(request, issuerID) + if err != nil { + return err + } + return err +} + +func (db *Database) DeleteIssuerByName(ctx context.Context, issuerName string) error { + var err error + request := `DELETE FROM issuer WHERE name = $1` + _, err = db.db.Exec(request, issuerName) + if err != nil { + return err + } + return err +} diff --git a/internal/database/service.go b/internal/database/service.go new file mode 100644 index 0000000..b8f805b --- /dev/null +++ b/internal/database/service.go @@ -0,0 +1,102 @@ +package database + +import ( + "context" + + "certmanager/internal/descriptor" + "certmanager/pkg/auxid" + + _ "github.com/mattn/go-sqlite3" +) + +func (db *Database) InsertService(ctx context.Context, service *descriptor.Service) (int64, error) { + var err error + var res int64 + service.ID = auxid.GenID() + request := `INSERT INTO service(id, issuer_id, name, cert, key, revoked) + VALUES ($1, $2, $3, $4, $5, $6)` + _, err = db.db.Exec(request, service.ID, service.IssuerID, service.Name, service.Cert, + service.Key, service.Revoked) + if err != nil { + return res, err + } + res = service.ID + return res, err +} + +func (db *Database) UpdateServiceByID(ctx context.Context, serviceID int64, service *descriptor.Service) error { + var err error + request := `UPDATE service SET revoked = $1 WHERE id = $2` + _, err = db.db.Exec(request, service.Revoked, serviceID) + if err != nil { + return err + } + return err +} + +func (db *Database) ListServices(ctx context.Context) ([]descriptor.Service, error) { + var err error + request := `SELECT id, name, revoked FROM service` + res := make([]descriptor.Service, 0) + err = db.db.Select(&res, request) + if err != nil { + return res, err + } + return res, err +} + +func (db *Database) GetServiceByID(ctx context.Context, serviceID int64) (bool, *descriptor.Service, error) { + var err error + var res *descriptor.Service + var exists bool + request := `SELECT id, name, cert, key, revoked FROM service WHERE id = $1 LiMIT 1` + dbRes := make([]descriptor.Service, 0) + err = db.db.Select(&dbRes, request, serviceID) + if err != nil { + return exists, res, err + } + if len(dbRes) == 0 { + return exists, res, err + } + exists = true + res = &dbRes[0] + return exists, res, err +} + +func (db *Database) GetServiceByName(ctx context.Context, serviceName string) (bool, *descriptor.Service, error) { + var err error + var res *descriptor.Service + var exists bool + request := `SELECT id, name, cert, key, revoked FROM service WHERE name = $1 LIMIT 1` + dbRes := make([]descriptor.Service, 0) + err = db.db.Select(&dbRes, request, serviceName) + if err != nil { + return exists, res, err + } + if len(dbRes) == 0 { + return exists, res, err + } + exists = true + res = &dbRes[0] + return exists, res, err +} + +func (db *Database) DeleteServiceByID(ctx context.Context, serviceID int64) error { + var err error + request := `DELETE FROM service WHERE id = $1` + _, err = db.db.Exec(request, serviceID) + if err != nil { + return err + } + return err +} + +func (db *Database) DeleteServiceByName(ctx context.Context, serviceName string) error { + var err error + request := `DELETE FROM service WHERE name = $1` + _, err = db.db.Exec(request, serviceName) + if err != nil { + return err + } + return err +} diff --git a/internal/descriptor/descriptor.go b/internal/descriptor/descriptor.go index 475e26a..053ae9d 100644 --- a/internal/descriptor/descriptor.go +++ b/internal/descriptor/descriptor.go @@ -1,3 +1,18 @@ package descriptor +type Issuer struct { + ID int64 `json:"id" yaml:"id" db:"id"` + Name string `json:"name" yaml:"name" db:"name"` + Cert string `json:"cert" yaml:"cert" db:"cert"` + Key string `json:"key" yaml:"key" db:"key"` + Revoked bool `json:"revoked" yaml:"revoked" db:"revoked"` +} +type Service struct { + ID int64 `json:"id" yaml:"id" db:"id"` + IssuerID int64 `json:"issuerId" yaml:"issuerId" db:"issuer_id"` + Name string `json:"name" yaml:"name" db:"name"` + Cert string `json:"cert" yaml:"cert" db:"cert"` + Key string `json:"key" yaml:"key" db:"key"` + Revoked bool `json:"revoked" yaml:"revoked" db:"revoked"` +} diff --git a/internal/grpc/handler/certman.go b/internal/grpc/handler/certman.go index 8e44bdf..0fbd7a9 100644 --- a/internal/grpc/handler/certman.go +++ b/internal/grpc/handler/certman.go @@ -6,7 +6,6 @@ import ( "certmanager/api/certmanagercontrol" ) - func (hand *Handler) CreateIssuerPair(ctx context.Context, req *certmanagercontrol.CreateIssuerPairParams) (*certmanagercontrol.CreateIssuerPairResult, error) { var err error hand.log.Debugf("Handle CreateIssuerPair request") diff --git a/internal/logic/certman.go b/internal/logic/certman.go index 39d5c74..452073d 100644 --- a/internal/logic/certman.go +++ b/internal/logic/certman.go @@ -2,66 +2,90 @@ package logic import ( "context" + "encoding/base64" + "encoding/json" - "certmanager/api/certmanagercontrol" + cmapi "certmanager/api/certmanagercontrol" + "certmanager/internal/descriptor" + //yaml "gopkg.in/yaml.v3" ) -func (lg *Logic) CreateIssuerPair(ctx context.Context, params *certmanagercontrol.CreateIssuerPairParams) (*certmanagercontrol.CreateIssuerPairResult, error) { +func (lg *Logic) CreateIssuerPair(ctx context.Context, params *cmapi.CreateIssuerPairParams) (*cmapi.CreateIssuerPairResult, error) { var err error - res := &certmanagercontrol.CreateIssuerPairResult{} + res := &cmapi.CreateIssuerPairResult{} + + paramsJson, err := json.Marshal(params) + if err != nil { + return res, err + } + lg.log.Debugf("params: \n%s\n", string(paramsJson)) + + certBytes, keyBytes, err := CreateX509SelfSignedCert(params.IssuerName) + certString := base64.StdEncoding.EncodeToString(certBytes) + keyString := base64.StdEncoding.EncodeToString(keyBytes) + issuer := &descriptor.Issuer{ + Name: params.IssuerName, + Cert: certString, + Key: keyString, + } + issuerID, err := lg.db.InsertIssuer(ctx, issuer) + if err != nil { + return res, err + } + res.IssuerID = issuerID return res, err } -func (lg *Logic) ImportIssuerPair(ctx context.Context, params *certmanagercontrol.ImportIssuerPairParams) (*certmanagercontrol.ImportIssuerPairResult, error) { +func (lg *Logic) ImportIssuerPair(ctx context.Context, params *cmapi.ImportIssuerPairParams) (*cmapi.ImportIssuerPairResult, error) { var err error - res := &certmanagercontrol.ImportIssuerPairResult{} + res := &cmapi.ImportIssuerPairResult{} return res, err } -func (lg *Logic) RevokeIssuerPair(ctx context.Context, params *certmanagercontrol.RevokeIssuerPairParams) (*certmanagercontrol.RevokeIssuerPairResult, error) { +func (lg *Logic) RevokeIssuerPair(ctx context.Context, params *cmapi.RevokeIssuerPairParams) (*cmapi.RevokeIssuerPairResult, error) { var err error - res := &certmanagercontrol.RevokeIssuerPairResult{} + res := &cmapi.RevokeIssuerPairResult{} return res, err } -func (lg *Logic) UnrevokeIssuerPair(ctx context.Context, params *certmanagercontrol.UnrevokeIssuerPairParams) (*certmanagercontrol.UnrevokeIssuerPairResult, error) { +func (lg *Logic) UnrevokeIssuerPair(ctx context.Context, params *cmapi.UnrevokeIssuerPairParams) (*cmapi.UnrevokeIssuerPairResult, error) { var err error - res := &certmanagercontrol.UnrevokeIssuerPairResult{} + res := &cmapi.UnrevokeIssuerPairResult{} return res, err } -func (lg *Logic) ListIssuerPairs(ctx context.Context, params *certmanagercontrol.ListIssuerPairsParams) (*certmanagercontrol.ListIssuerPairsResult, error) { +func (lg *Logic) ListIssuerPairs(ctx context.Context, params *cmapi.ListIssuerPairsParams) (*cmapi.ListIssuerPairsResult, error) { var err error - res := &certmanagercontrol.ListIssuerPairsResult{} + res := &cmapi.ListIssuerPairsResult{} return res, err } -func (lg *Logic) GetIssuerCertificate(ctx context.Context, params *certmanagercontrol.GetIssuerCertificateParams) (*certmanagercontrol.GetIssuerCertificateResult, error) { +func (lg *Logic) GetIssuerCertificate(ctx context.Context, params *cmapi.GetIssuerCertificateParams) (*cmapi.GetIssuerCertificateResult, error) { var err error - res := &certmanagercontrol.GetIssuerCertificateResult{} + res := &cmapi.GetIssuerCertificateResult{} return res, err } -func (lg *Logic) CreateServicePair(ctx context.Context, params *certmanagercontrol.CreateServicePairParams) (*certmanagercontrol.CreateServicePairResult, error) { +func (lg *Logic) CreateServicePair(ctx context.Context, params *cmapi.CreateServicePairParams) (*cmapi.CreateServicePairResult, error) { var err error - res := &certmanagercontrol.CreateServicePairResult{} + res := &cmapi.CreateServicePairResult{} return res, err } -func (lg *Logic) RevokeServicePair(ctx context.Context, params *certmanagercontrol.RevokeServicePairParams) (*certmanagercontrol.RevokeServicePairResult, error) { +func (lg *Logic) RevokeServicePair(ctx context.Context, params *cmapi.RevokeServicePairParams) (*cmapi.RevokeServicePairResult, error) { var err error - res := &certmanagercontrol.RevokeServicePairResult{} + res := &cmapi.RevokeServicePairResult{} return res, err } -func (lg *Logic) ListServicePairs(ctx context.Context, params *certmanagercontrol.ListServicePairsParams) (*certmanagercontrol.ListServicePairsResult, error) { +func (lg *Logic) ListServicePairs(ctx context.Context, params *cmapi.ListServicePairsParams) (*cmapi.ListServicePairsResult, error) { var err error - res := &certmanagercontrol.ListServicePairsResult{} + res := &cmapi.ListServicePairsResult{} return res, err } -func (lg *Logic) GetServicePair(ctx context.Context, params *certmanagercontrol.GetServicePairParams) (*certmanagercontrol.GetServicePairResult, error) { +func (lg *Logic) GetServicePair(ctx context.Context, params *cmapi.GetServicePairParams) (*cmapi.GetServicePairResult, error) { var err error - res := &certmanagercontrol.GetServicePairResult{} + res := &cmapi.GetServicePairResult{} return res, err } diff --git a/internal/logic/logic.go b/internal/logic/logic.go index 01bef90..089b7c2 100644 --- a/internal/logic/logic.go +++ b/internal/logic/logic.go @@ -7,21 +7,21 @@ import ( ) type LogicConfig struct { - Database *database.Database - Auths []config.AuthConfig + Database *database.Database + Auths []config.AuthConfig } type Logic struct { - auths []config.AuthConfig - log *logger.Logger - db *database.Database + auths []config.AuthConfig + log *logger.Logger + db *database.Database } func NewLogic(conf *LogicConfig) (*Logic, error) { var err error lg := &Logic{ - db: conf.Database, - auths: conf.Auths, + db: conf.Database, + auths: conf.Auths, } lg.log = logger.NewLogger("logic") return lg, err diff --git a/internal/logic/x509.go b/internal/logic/x509.go new file mode 100644 index 0000000..dfe1b1f --- /dev/null +++ b/internal/logic/x509.go @@ -0,0 +1,176 @@ +package logic + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "net" + "time" +) + +func CreateX509SelfSignedCert(subject string, commonNames ...string) ([]byte, []byte, error) { + var err error + certPem := make([]byte, 0) + keyPem := make([]byte, 0) + + now := time.Now() + + const yearsAfter int = 10 + const keySize int = 2048 + + key, err := rsa.GenerateKey(rand.Reader, keySize) + if err != nil { + err := fmt.Errorf("Can't create a private key: %v", err) + return certPem, keyPem, err + + } + keyPemBlock := pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(key), + } + keyPem = pem.EncodeToMemory(&keyPemBlock) + + dnsNames := make([]string, 0) + dnsNames = append(dnsNames, subject) + dnsNames = append(dnsNames, commonNames...) + tml := x509.Certificate{ + SerialNumber: big.NewInt(now.Unix()), + NotBefore: now, + NotAfter: now.AddDate(yearsAfter, 0, 0), + Subject: pkix.Name{ + CommonName: subject, + }, + DNSNames: dnsNames, + IPAddresses: []net.IP{net.ParseIP("192.168.57.1")}, + BasicConstraintsValid: true, + } + certBytes, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key) + if err != nil { + return certPem, keyPem, fmt.Errorf("Can't create a certificate: %v", err) + + } + certPemBlock := pem.Block{ + Type: "CERTIFICATE", + Bytes: certBytes, + } + certPem = pem.EncodeToMemory(&certPemBlock) + if err != nil { + return certPem, keyPem, err + } + return certPem, keyPem, err +} + +func CreateX509CACert(commonName string) ([]byte, []byte, error) { + var err error + certPem := make([]byte, 0) + keyPem := make([]byte, 0) + + now := time.Now() + + const yearsAfter int = 10 + const keySize int = 2048 + + key, err := rsa.GenerateKey(rand.Reader, keySize) + if err != nil { + err := fmt.Errorf("Can't create a private key: %v", err) + return certPem, keyPem, err + + } + keyPemBlock := pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(key), + } + keyPem = pem.EncodeToMemory(&keyPemBlock) + + tml := x509.Certificate{ + SerialNumber: big.NewInt(now.Unix()), + NotBefore: now, + NotAfter: now.AddDate(yearsAfter, 0, 0), + Subject: pkix.Name{ + CommonName: commonName, + }, + IsCA: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + } + certBytes, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key) + if err != nil { + return certPem, keyPem, fmt.Errorf("Can't create a certificate: %v", err) + + } + certPemBlock := pem.Block{ + Type: "CERTIFICATE", + Bytes: certBytes, + } + certPem = pem.EncodeToMemory(&certPemBlock) + + if err != nil { + return certPem, keyPem, err + } + return certPem, keyPem, err +} + +func CreateX509Cert(commonName string, caKeyPem []byte, dnsNames ...string) ([]byte, []byte, error) { + var err error + certPem := make([]byte, 0) + keyPem := make([]byte, 0) + now := time.Now() + + const yearsAfter int = 10 + const keySize int = 2048 + + key, err := rsa.GenerateKey(rand.Reader, keySize) + if err != nil { + err := fmt.Errorf("Can't create a private key: %v", err) + return certPem, keyPem, err + } + keyPemBlock := pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(key), + } + keyPem = pem.EncodeToMemory(&keyPemBlock) + + pemBlock, _ := pem.Decode(caKeyPem) + if pemBlock == nil { + err := fmt.Errorf("Can't parse a CA private key block") + return certPem, keyPem, err + } + caKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes) + if err != nil { + err := fmt.Errorf("Can't parse a CA private key") + return certPem, keyPem, err + } + + tml := x509.Certificate{ + SerialNumber: big.NewInt(now.Unix()), + NotBefore: now, + NotAfter: now.AddDate(yearsAfter, 0, 0), + Subject: pkix.Name{ + CommonName: commonName, + }, + DNSNames: append([]string{commonName}, dnsNames...), + IsCA: false, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature, + BasicConstraintsValid: true, + } + certBytes, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, caKey) + if err != nil { + return certPem, keyPem, fmt.Errorf("Can't create a certificate: %v", err) + + } + certPemBlock := pem.Block{ + Type: "CERTIFICATE", + Bytes: certBytes, + } + certPem = pem.EncodeToMemory(&certPemBlock) + if err != nil { + return certPem, keyPem, err + } + return certPem, keyPem, err +} diff --git a/internal/server/server.go b/internal/server/server.go index aaf975e..20be66f 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -85,8 +85,8 @@ func (srv *Server) Build() error { // Create logic logicConfig := &logic.LogicConfig{ - Auths: srv.conf.Auths, - Database: srv.db, + Auths: srv.conf.Auths, + Database: srv.db, } srv.lg, err = logic.NewLogic(logicConfig) if err != nil { @@ -149,13 +149,19 @@ func (srv *Server) Run() error { } srv.log.Debugf("Server configuration:\n%s\n", yamlConfig) - // Show current user + // Show current user currUser, err := user.Current() if err != nil { return err } srv.log.Infof("Running server as user %s", currUser.Username) + // Show current user + err = srv.db.InitDatabase() + if err != nil { + return err + } + sigs := make(chan os.Signal, 1) gdone := make(chan error, 1) wdone := make(chan error, 1) diff --git a/internal/test/database_test.go b/internal/test/database_test.go new file mode 100644 index 0000000..7907c60 --- /dev/null +++ b/internal/test/database_test.go @@ -0,0 +1,118 @@ +package test + +import ( + "context" + "fmt" + "testing" + "time" + + "certmanager/internal/config" + "certmanager/internal/database" + "certmanager/internal/descriptor" + + "github.com/stretchr/testify/require" + yaml "gopkg.in/yaml.v3" +) + +func TestDatabaseIssuer(t *testing.T) { + var err error + conf := config.NewConfig() + err = conf.ReadFile() + require.NoError(t, err) + + db, err := database.NewDatabase(conf.DataDir) + require.NoError(t, err) + require.NotNil(t, db) + + err = db.InitDatabase() + require.NoError(t, err) + + ctx, _ := context.WithTimeout(context.Background(), 1*time.Second) + + issuer := &descriptor.Issuer{ + Name: "foo.bar", + Cert: "ASDFF", + Key: "QWERT", + Revoked: true, + } + issuerID, err := db.InsertIssuer(ctx, issuer) + require.NoError(t, err) + fmt.Printf("issuerId: %d\n", issuerID) + + issuer.Revoked = true + err = db.UpdateIssuerByID(ctx, issuerID, issuer) + require.NoError(t, err) + + issuerExists, issuer, err := db.GetIssuerByID(ctx, issuerID) + require.NoError(t, err) + require.True(t, issuerExists) + require.NotNil(t, issuer) + require.True(t, issuer.Revoked) + + issuerYAML, err := yaml.Marshal(issuer) + require.NoError(t, err) + fmt.Printf("Issuer: \n%s\n---\n", string(issuerYAML)) + + listIssuers, err := db.ListIssuers(ctx) + require.NoError(t, err) + require.NotNil(t, listIssuers) + + listIssuersYAML, err := yaml.Marshal(listIssuers) + require.NoError(t, err) + fmt.Printf("listIssuers: \n%s\n", string(listIssuersYAML)) + + err = db.DeleteIssuerByID(ctx, issuerID) + require.NoError(t, err) +} + +func TestDatabaseService(t *testing.T) { + var err error + conf := config.NewConfig() + err = conf.ReadFile() + require.NoError(t, err) + + db, err := database.NewDatabase(conf.DataDir) + require.NoError(t, err) + require.NotNil(t, db) + + err = db.InitDatabase() + require.NoError(t, err) + + ctx, _ := context.WithTimeout(context.Background(), 1*time.Second) + + service := &descriptor.Service{ + Name: "foo.bar", + IssuerID: 123456, + Cert: "ASDFF", + Key: "QWERT", + Revoked: true, + } + serviceID, err := db.InsertService(ctx, service) + require.NoError(t, err) + fmt.Printf("serviceId: %d\n", serviceID) + + service.Revoked = true + err = db.UpdateServiceByID(ctx, serviceID, service) + require.NoError(t, err) + + serviceExists, service, err := db.GetServiceByID(ctx, serviceID) + require.NoError(t, err) + require.True(t, serviceExists) + require.NotNil(t, service) + require.True(t, service.Revoked) + + serviceYAML, err := yaml.Marshal(service) + require.NoError(t, err) + fmt.Printf("Service: \n%s\n---\n", string(serviceYAML)) + + listServices, err := db.ListServices(ctx) + require.NoError(t, err) + require.NotNil(t, listServices) + + listServicesYAML, err := yaml.Marshal(listServices) + require.NoError(t, err) + fmt.Printf("listServices: \n%s\n", string(listServicesYAML)) + + err = db.DeleteServiceByID(ctx, serviceID) + require.NoError(t, err) +} diff --git a/internal/test/logic_test.go b/internal/test/logic_test.go new file mode 100644 index 0000000..813dd53 --- /dev/null +++ b/internal/test/logic_test.go @@ -0,0 +1,50 @@ +package test + +import ( + "context" + "fmt" + "testing" + "time" + + cmapi "certmanager/api/certmanagercontrol" + "certmanager/internal/config" + "certmanager/internal/database" + "certmanager/internal/logic" + + "github.com/stretchr/testify/require" +) + +func TestLogicIssuer(t *testing.T) { + var err error + conf := config.NewConfig() + err = conf.ReadFile() + require.NoError(t, err) + + db, err := database.NewDatabase(conf.DataDir) + require.NoError(t, err) + require.NotNil(t, db) + + err = db.InitDatabase() + require.NoError(t, err) + + logicConfig := &logic.LogicConfig{ + Auths: conf.Auths, + Database: db, + } + lg, err := logic.NewLogic(logicConfig) + require.NoError(t, err) + require.NotNil(t, lg) + + ctx, _ := context.WithTimeout(context.Background(), 1*time.Second) + + createIssuerPairParams := &cmapi.CreateIssuerPairParams{ + SelfSigned: true, + IssuerName: "foo.bar", + } + createIssuerPairRes, err := lg.CreateIssuerPair(ctx, createIssuerPairParams) + require.NoError(t, err) + require.NotNil(t, createIssuerPairRes) + + fmt.Printf("issuerId: %d\n", createIssuerPairRes.IssuerID) + +} diff --git a/pkg/auxid/genid.go b/pkg/auxid/genid.go new file mode 100644 index 0000000..2e427ef --- /dev/null +++ b/pkg/auxid/genid.go @@ -0,0 +1,32 @@ +package auxid + +import ( + "math/rand" + "sync" + "time" +) + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +var ( + idMtx sync.Mutex + lastID int64 +) + +func GenID() int64 { + // 53 bit limit for js + // See https://stackoverflow.com/questions/1379934/large-numbers-erroneously-rounded-in-javascript + idMtx.Lock() + defer idMtx.Unlock() + for { + id := (time.Now().UnixNano() / 1000) // - 10000000000000 + if id != lastID { + lastID = id + return id + } + time.Sleep(1 * time.Microsecond) + } + //10467328383814 +} diff --git a/pkg/client/control.go b/pkg/client/control.go index 4f6b83b..160166f 100644 --- a/pkg/client/control.go +++ b/pkg/client/control.go @@ -24,7 +24,6 @@ type Access struct { Password string } - type Control struct { conn *grpc.ClientConn client cmapi.ControlClient diff --git a/pkg/common/common.go b/pkg/common/common.go index 4f5f1d1..805d0c7 100644 --- a/pkg/common/common.go +++ b/pkg/common/common.go @@ -1,2 +1 @@ package common -