139 lines
3.4 KiB
Go
139 lines
3.4 KiB
Go
|
package db
|
||
|
|
||
|
import (
|
||
|
"crypto/x509"
|
||
|
"encoding/json"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/pkg/errors"
|
||
|
"github.com/smallstep/nosql"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
revokedCertsTable = []byte("revoked_x509_certs")
|
||
|
certsTable = []byte("x509_certs")
|
||
|
)
|
||
|
|
||
|
// ErrAlreadyExists can be returned if the DB attempts to set a key that has
|
||
|
// been previously set.
|
||
|
var ErrAlreadyExists = errors.New("already exists")
|
||
|
|
||
|
// Config represents the JSON attributes used for configuring a step-ca DB.
|
||
|
type Config struct {
|
||
|
Type string `json:"type"`
|
||
|
Path string `json:"path"`
|
||
|
}
|
||
|
|
||
|
// AuthDB is an interface over an Authority DB client that implements a nosql.DB interface.
|
||
|
type AuthDB interface {
|
||
|
IsRevoked(sn string) (bool, error)
|
||
|
Revoke(rci *RevokedCertificateInfo) error
|
||
|
StoreCertificate(crt *x509.Certificate) error
|
||
|
Shutdown() error
|
||
|
}
|
||
|
|
||
|
// DB is a wrapper over the nosql.DB interface.
|
||
|
type DB struct {
|
||
|
nosql.DB
|
||
|
}
|
||
|
|
||
|
// New returns a new database client that implements the AuthDB interface.
|
||
|
func New(c *Config) (AuthDB, error) {
|
||
|
if c == nil {
|
||
|
return new(NoopDB), nil
|
||
|
}
|
||
|
|
||
|
var db nosql.DB
|
||
|
switch strings.ToLower(c.Type) {
|
||
|
case "bbolt":
|
||
|
db = &nosql.BoltDB{}
|
||
|
if err := db.Open(c.Path); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
default:
|
||
|
return nil, errors.Errorf("unsupported db.type '%s'", c.Type)
|
||
|
}
|
||
|
|
||
|
tables := [][]byte{revokedCertsTable, certsTable}
|
||
|
for _, b := range tables {
|
||
|
if err := db.CreateTable(b); err != nil {
|
||
|
return nil, errors.Wrapf(err, "error creating table %s",
|
||
|
string(b))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return &DB{db}, nil
|
||
|
}
|
||
|
|
||
|
// RevokedCertificateInfo contains information regarding the certificate
|
||
|
// revocation action.
|
||
|
type RevokedCertificateInfo struct {
|
||
|
Serial string
|
||
|
ProvisionerID string
|
||
|
ReasonCode int
|
||
|
Reason string
|
||
|
RevokedAt time.Time
|
||
|
TokenID string
|
||
|
MTLS bool
|
||
|
}
|
||
|
|
||
|
// IsRevoked returns whether or not a certificate with the given identifier
|
||
|
// has been revoked.
|
||
|
// In the case of an X509 Certificate the `id` should be the Serial Number of
|
||
|
// the Certificate.
|
||
|
func (db *DB) IsRevoked(sn string) (bool, error) {
|
||
|
// If the DB is nil then act as pass through.
|
||
|
if db == nil {
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
|
// If the error is `Not Found` then the certificate has not been revoked.
|
||
|
// Any other error should be propagated to the caller.
|
||
|
if _, err := db.Get(revokedCertsTable, []byte(sn)); err != nil {
|
||
|
if nosql.IsErrNotFound(err) {
|
||
|
return false, nil
|
||
|
}
|
||
|
return false, errors.Wrap(err, "error checking revocation bucket")
|
||
|
}
|
||
|
|
||
|
// This certificate has been revoked.
|
||
|
return true, nil
|
||
|
}
|
||
|
|
||
|
// Revoke adds a certificate to the revocation table.
|
||
|
func (db *DB) Revoke(rci *RevokedCertificateInfo) error {
|
||
|
isRvkd, err := db.IsRevoked(rci.Serial)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if isRvkd {
|
||
|
return ErrAlreadyExists
|
||
|
}
|
||
|
rcib, err := json.Marshal(rci)
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "error marshaling revoked certificate info")
|
||
|
}
|
||
|
|
||
|
if err = db.Set(revokedCertsTable, []byte(rci.Serial), rcib); err != nil {
|
||
|
return errors.Wrap(err, "database Set error")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// StoreCertificate stores a certificate PEM.
|
||
|
func (db *DB) StoreCertificate(crt *x509.Certificate) error {
|
||
|
if err := db.Set(certsTable, []byte(crt.SerialNumber.String()), crt.Raw); err != nil {
|
||
|
return errors.Wrap(err, "database Set error")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Shutdown sends a shutdown message to the database.
|
||
|
func (db *DB) Shutdown() error {
|
||
|
if err := db.Close(); err != nil {
|
||
|
return errors.Wrap(err, "database shutdown error")
|
||
|
}
|
||
|
return nil
|
||
|
}
|