339 lines
9 KiB
Go
339 lines
9 KiB
Go
package nosql
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"go.step.sm/linkedca"
|
|
|
|
"github.com/smallstep/certificates/authority/admin"
|
|
"github.com/smallstep/nosql"
|
|
)
|
|
|
|
type dbX509Policy struct {
|
|
Allow *dbX509Names `json:"allow,omitempty"`
|
|
Deny *dbX509Names `json:"deny,omitempty"`
|
|
AllowWildcardNames bool `json:"allow_wildcard_names,omitempty"`
|
|
}
|
|
|
|
type dbX509Names struct {
|
|
CommonNames []string `json:"cn,omitempty"`
|
|
DNSDomains []string `json:"dns,omitempty"`
|
|
IPRanges []string `json:"ip,omitempty"`
|
|
EmailAddresses []string `json:"email,omitempty"`
|
|
URIDomains []string `json:"uri,omitempty"`
|
|
}
|
|
|
|
type dbSSHPolicy struct {
|
|
// User contains SSH user certificate options.
|
|
User *dbSSHUserPolicy `json:"user,omitempty"`
|
|
// Host contains SSH host certificate options.
|
|
Host *dbSSHHostPolicy `json:"host,omitempty"`
|
|
}
|
|
|
|
type dbSSHHostPolicy struct {
|
|
Allow *dbSSHHostNames `json:"allow,omitempty"`
|
|
Deny *dbSSHHostNames `json:"deny,omitempty"`
|
|
}
|
|
|
|
type dbSSHHostNames struct {
|
|
DNSDomains []string `json:"dns,omitempty"`
|
|
IPRanges []string `json:"ip,omitempty"`
|
|
Principals []string `json:"principal,omitempty"`
|
|
}
|
|
|
|
type dbSSHUserPolicy struct {
|
|
Allow *dbSSHUserNames `json:"allow,omitempty"`
|
|
Deny *dbSSHUserNames `json:"deny,omitempty"`
|
|
}
|
|
|
|
type dbSSHUserNames struct {
|
|
EmailAddresses []string `json:"email,omitempty"`
|
|
Principals []string `json:"principal,omitempty"`
|
|
}
|
|
|
|
type dbPolicy struct {
|
|
X509 *dbX509Policy `json:"x509,omitempty"`
|
|
SSH *dbSSHPolicy `json:"ssh,omitempty"`
|
|
}
|
|
|
|
type dbAuthorityPolicy struct {
|
|
ID string `json:"id"`
|
|
AuthorityID string `json:"authorityID"`
|
|
Policy *dbPolicy `json:"policy,omitempty"`
|
|
}
|
|
|
|
func (dbap *dbAuthorityPolicy) convert() *linkedca.Policy {
|
|
if dbap == nil {
|
|
return nil
|
|
}
|
|
return dbToLinked(dbap.Policy)
|
|
}
|
|
|
|
func (db *DB) getDBAuthorityPolicyBytes(ctx context.Context, authorityID string) ([]byte, error) {
|
|
data, err := db.db.Get(authorityPoliciesTable, []byte(authorityID))
|
|
if nosql.IsErrNotFound(err) {
|
|
return nil, admin.NewError(admin.ErrorNotFoundType, "authority policy not found")
|
|
} else if err != nil {
|
|
return nil, fmt.Errorf("error loading authority policy: %w", err)
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
func (db *DB) unmarshalDBAuthorityPolicy(data []byte) (*dbAuthorityPolicy, error) {
|
|
if len(data) == 0 {
|
|
return nil, nil
|
|
}
|
|
var dba = new(dbAuthorityPolicy)
|
|
if err := json.Unmarshal(data, dba); err != nil {
|
|
return nil, fmt.Errorf("error unmarshaling policy bytes into dbAuthorityPolicy: %w", err)
|
|
}
|
|
return dba, nil
|
|
}
|
|
|
|
func (db *DB) getDBAuthorityPolicy(ctx context.Context, authorityID string) (*dbAuthorityPolicy, error) {
|
|
data, err := db.getDBAuthorityPolicyBytes(ctx, authorityID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dbap, err := db.unmarshalDBAuthorityPolicy(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if dbap == nil {
|
|
return nil, nil
|
|
}
|
|
if dbap.AuthorityID != authorityID {
|
|
return nil, admin.NewError(admin.ErrorAuthorityMismatchType,
|
|
"authority policy is not owned by authority %s", authorityID)
|
|
}
|
|
return dbap, nil
|
|
}
|
|
|
|
func (db *DB) CreateAuthorityPolicy(ctx context.Context, policy *linkedca.Policy) error {
|
|
|
|
dbap := &dbAuthorityPolicy{
|
|
ID: db.authorityID,
|
|
AuthorityID: db.authorityID,
|
|
Policy: linkedToDB(policy),
|
|
}
|
|
|
|
if err := db.save(ctx, dbap.ID, dbap, nil, "authority_policy", authorityPoliciesTable); err != nil {
|
|
return admin.WrapErrorISE(err, "error creating authority policy")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (db *DB) GetAuthorityPolicy(ctx context.Context) (*linkedca.Policy, error) {
|
|
dbap, err := db.getDBAuthorityPolicy(ctx, db.authorityID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return dbap.convert(), nil
|
|
}
|
|
|
|
func (db *DB) UpdateAuthorityPolicy(ctx context.Context, policy *linkedca.Policy) error {
|
|
old, err := db.getDBAuthorityPolicy(ctx, db.authorityID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dbap := &dbAuthorityPolicy{
|
|
ID: db.authorityID,
|
|
AuthorityID: db.authorityID,
|
|
Policy: linkedToDB(policy),
|
|
}
|
|
|
|
if err := db.save(ctx, dbap.ID, dbap, old, "authority_policy", authorityPoliciesTable); err != nil {
|
|
return admin.WrapErrorISE(err, "error updating authority policy")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (db *DB) DeleteAuthorityPolicy(ctx context.Context) error {
|
|
old, err := db.getDBAuthorityPolicy(ctx, db.authorityID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := db.save(ctx, old.ID, nil, old, "authority_policy", authorityPoliciesTable); err != nil {
|
|
return admin.WrapErrorISE(err, "error deleting authority policy")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func dbToLinked(p *dbPolicy) *linkedca.Policy {
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
r := &linkedca.Policy{}
|
|
if x509 := p.X509; x509 != nil {
|
|
r.X509 = &linkedca.X509Policy{}
|
|
if allow := x509.Allow; allow != nil {
|
|
r.X509.Allow = &linkedca.X509Names{}
|
|
r.X509.Allow.Dns = allow.DNSDomains
|
|
r.X509.Allow.Emails = allow.EmailAddresses
|
|
r.X509.Allow.Ips = allow.IPRanges
|
|
r.X509.Allow.Uris = allow.URIDomains
|
|
r.X509.Allow.CommonNames = allow.CommonNames
|
|
}
|
|
if deny := x509.Deny; deny != nil {
|
|
r.X509.Deny = &linkedca.X509Names{}
|
|
r.X509.Deny.Dns = deny.DNSDomains
|
|
r.X509.Deny.Emails = deny.EmailAddresses
|
|
r.X509.Deny.Ips = deny.IPRanges
|
|
r.X509.Deny.Uris = deny.URIDomains
|
|
r.X509.Deny.CommonNames = deny.CommonNames
|
|
}
|
|
r.X509.AllowWildcardNames = x509.AllowWildcardNames
|
|
}
|
|
if ssh := p.SSH; ssh != nil {
|
|
r.Ssh = &linkedca.SSHPolicy{}
|
|
if host := ssh.Host; host != nil {
|
|
r.Ssh.Host = &linkedca.SSHHostPolicy{}
|
|
if allow := host.Allow; allow != nil {
|
|
r.Ssh.Host.Allow = &linkedca.SSHHostNames{}
|
|
r.Ssh.Host.Allow.Dns = allow.DNSDomains
|
|
r.Ssh.Host.Allow.Ips = allow.IPRanges
|
|
r.Ssh.Host.Allow.Principals = allow.Principals
|
|
}
|
|
if deny := host.Deny; deny != nil {
|
|
r.Ssh.Host.Deny = &linkedca.SSHHostNames{}
|
|
r.Ssh.Host.Deny.Dns = deny.DNSDomains
|
|
r.Ssh.Host.Deny.Ips = deny.IPRanges
|
|
r.Ssh.Host.Deny.Principals = deny.Principals
|
|
}
|
|
}
|
|
if user := ssh.User; user != nil {
|
|
r.Ssh.User = &linkedca.SSHUserPolicy{}
|
|
if allow := user.Allow; allow != nil {
|
|
r.Ssh.User.Allow = &linkedca.SSHUserNames{}
|
|
r.Ssh.User.Allow.Emails = allow.EmailAddresses
|
|
r.Ssh.User.Allow.Principals = allow.Principals
|
|
}
|
|
if deny := user.Deny; deny != nil {
|
|
r.Ssh.User.Deny = &linkedca.SSHUserNames{}
|
|
r.Ssh.User.Deny.Emails = deny.EmailAddresses
|
|
r.Ssh.User.Deny.Principals = deny.Principals
|
|
}
|
|
}
|
|
}
|
|
|
|
return r
|
|
}
|
|
|
|
func linkedToDB(p *linkedca.Policy) *dbPolicy {
|
|
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
|
|
// return early if x509 nor SSH is set
|
|
if p.GetX509() == nil && p.GetSsh() == nil {
|
|
return nil
|
|
}
|
|
|
|
r := &dbPolicy{}
|
|
// fill x509 policy configuration
|
|
if x509 := p.GetX509(); x509 != nil {
|
|
r.X509 = &dbX509Policy{}
|
|
if allow := x509.GetAllow(); allow != nil {
|
|
r.X509.Allow = &dbX509Names{}
|
|
if allow.Dns != nil {
|
|
r.X509.Allow.DNSDomains = allow.Dns
|
|
}
|
|
if allow.Ips != nil {
|
|
r.X509.Allow.IPRanges = allow.Ips
|
|
}
|
|
if allow.Emails != nil {
|
|
r.X509.Allow.EmailAddresses = allow.Emails
|
|
}
|
|
if allow.Uris != nil {
|
|
r.X509.Allow.URIDomains = allow.Uris
|
|
}
|
|
if allow.CommonNames != nil {
|
|
r.X509.Allow.CommonNames = allow.CommonNames
|
|
}
|
|
}
|
|
if deny := x509.GetDeny(); deny != nil {
|
|
r.X509.Deny = &dbX509Names{}
|
|
if deny.Dns != nil {
|
|
r.X509.Deny.DNSDomains = deny.Dns
|
|
}
|
|
if deny.Ips != nil {
|
|
r.X509.Deny.IPRanges = deny.Ips
|
|
}
|
|
if deny.Emails != nil {
|
|
r.X509.Deny.EmailAddresses = deny.Emails
|
|
}
|
|
if deny.Uris != nil {
|
|
r.X509.Deny.URIDomains = deny.Uris
|
|
}
|
|
if deny.CommonNames != nil {
|
|
r.X509.Deny.CommonNames = deny.CommonNames
|
|
}
|
|
}
|
|
|
|
r.X509.AllowWildcardNames = x509.GetAllowWildcardNames()
|
|
}
|
|
|
|
// fill ssh policy configuration
|
|
if ssh := p.GetSsh(); ssh != nil {
|
|
r.SSH = &dbSSHPolicy{}
|
|
if host := ssh.GetHost(); host != nil {
|
|
r.SSH.Host = &dbSSHHostPolicy{}
|
|
if allow := host.GetAllow(); allow != nil {
|
|
r.SSH.Host.Allow = &dbSSHHostNames{}
|
|
if allow.Dns != nil {
|
|
r.SSH.Host.Allow.DNSDomains = allow.Dns
|
|
}
|
|
if allow.Ips != nil {
|
|
r.SSH.Host.Allow.IPRanges = allow.Ips
|
|
}
|
|
if allow.Principals != nil {
|
|
r.SSH.Host.Allow.Principals = allow.Principals
|
|
}
|
|
}
|
|
if deny := host.GetDeny(); deny != nil {
|
|
r.SSH.Host.Deny = &dbSSHHostNames{}
|
|
if deny.Dns != nil {
|
|
r.SSH.Host.Deny.DNSDomains = deny.Dns
|
|
}
|
|
if deny.Ips != nil {
|
|
r.SSH.Host.Deny.IPRanges = deny.Ips
|
|
}
|
|
if deny.Principals != nil {
|
|
r.SSH.Host.Deny.Principals = deny.Principals
|
|
}
|
|
}
|
|
}
|
|
if user := ssh.GetUser(); user != nil {
|
|
r.SSH.User = &dbSSHUserPolicy{}
|
|
if allow := user.GetAllow(); allow != nil {
|
|
r.SSH.User.Allow = &dbSSHUserNames{}
|
|
if allow.Emails != nil {
|
|
r.SSH.User.Allow.EmailAddresses = allow.Emails
|
|
}
|
|
if allow.Principals != nil {
|
|
r.SSH.User.Allow.Principals = allow.Principals
|
|
}
|
|
}
|
|
if deny := user.GetDeny(); deny != nil {
|
|
r.SSH.User.Deny = &dbSSHUserNames{}
|
|
if deny.Emails != nil {
|
|
r.SSH.User.Deny.EmailAddresses = deny.Emails
|
|
}
|
|
if deny.Principals != nil {
|
|
r.SSH.User.Deny.Principals = deny.Principals
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return r
|
|
}
|