2016-12-28 00:39:43 +00:00
|
|
|
package services
|
|
|
|
|
|
|
|
import (
|
2016-12-28 01:28:46 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
|
2016-12-28 23:50:32 +00:00
|
|
|
"github.com/gravitational/teleport/lib/defaults"
|
2016-12-28 01:28:46 +00:00
|
|
|
"github.com/gravitational/teleport/lib/utils"
|
2016-12-28 00:39:43 +00:00
|
|
|
|
|
|
|
"github.com/gravitational/trace"
|
2016-12-28 01:28:46 +00:00
|
|
|
"golang.org/x/crypto/ssh"
|
2016-12-28 00:39:43 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// CertAuthority is a host or user certificate authority that
|
|
|
|
// can check and if it has private key stored as well, sign it too
|
|
|
|
type CertAuthority interface {
|
2016-12-29 02:47:33 +00:00
|
|
|
// GetID returns certificate authority ID -
|
|
|
|
// combined type and name
|
|
|
|
GetID() CertAuthID
|
2016-12-28 00:39:43 +00:00
|
|
|
// GetName returns cert authority name
|
|
|
|
GetName() string
|
|
|
|
// GetType returns user or host certificate authority
|
2016-12-28 22:07:03 +00:00
|
|
|
GetType() CertAuthType
|
2016-12-28 00:39:43 +00:00
|
|
|
// GetClusterName returns cluster name this cert authority
|
|
|
|
// is associated with
|
|
|
|
GetClusterName() string
|
|
|
|
// GetCheckingKeys returns public keys to check signature
|
|
|
|
GetCheckingKeys() [][]byte
|
|
|
|
// GetRoles returns a list of roles assumed by users signed by this CA
|
2016-12-28 22:07:03 +00:00
|
|
|
GetRoles() []string
|
2016-12-28 00:39:43 +00:00
|
|
|
// FirstSigningKey returns first signing key or returns error if it's not here
|
|
|
|
FirstSigningKey() ([]byte, error)
|
2016-12-28 01:28:46 +00:00
|
|
|
// GetRawObject returns raw object data, used for migrations
|
|
|
|
GetRawObject() interface{}
|
2016-12-29 02:47:33 +00:00
|
|
|
// Check checks object for errors
|
|
|
|
Check() error
|
|
|
|
// SetSigningKeys sets signing keys
|
|
|
|
SetSigningKeys([][]byte) error
|
2016-12-28 00:39:43 +00:00
|
|
|
}
|
|
|
|
|
2016-12-29 20:23:58 +00:00
|
|
|
// CertAuthorityV2 is version 1 resource spec for Cert Authority
|
|
|
|
type CertAuthorityV2 struct {
|
2016-12-28 00:39:43 +00:00
|
|
|
// Kind is a resource kind
|
|
|
|
Kind string `json:"kind"`
|
|
|
|
// Version is version
|
|
|
|
Version string `json:"version"`
|
|
|
|
// Metadata is connector metadata
|
|
|
|
Metadata Metadata `json:"metadata"`
|
|
|
|
// Spec contains cert authority specification
|
2016-12-29 20:23:58 +00:00
|
|
|
Spec CertAuthoritySpecV2 `json:"spec"`
|
2016-12-28 01:28:46 +00:00
|
|
|
// rawObject is object that is raw object stored in DB
|
2016-12-28 22:07:03 +00:00
|
|
|
// without any conversions applied, used in migrations
|
2016-12-28 01:28:46 +00:00
|
|
|
rawObject interface{}
|
2016-12-28 00:39:43 +00:00
|
|
|
}
|
|
|
|
|
2016-12-29 02:47:33 +00:00
|
|
|
// SetSigningKeys sets signing keys
|
2016-12-29 20:23:58 +00:00
|
|
|
func (ca *CertAuthorityV2) SetSigningKeys(keys [][]byte) error {
|
2016-12-29 02:47:33 +00:00
|
|
|
ca.Spec.SigningKeys = keys
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetID returns certificate authority ID -
|
|
|
|
// combined type and name
|
2016-12-29 20:23:58 +00:00
|
|
|
func (ca *CertAuthorityV2) GetID() CertAuthID {
|
2016-12-29 02:47:33 +00:00
|
|
|
return CertAuthID{Type: ca.Spec.Type, DomainName: ca.Metadata.Name}
|
|
|
|
}
|
|
|
|
|
2016-12-28 22:07:03 +00:00
|
|
|
// GetName returns cert authority name
|
2016-12-29 20:23:58 +00:00
|
|
|
func (ca *CertAuthorityV2) GetName() string {
|
2016-12-28 22:07:03 +00:00
|
|
|
return ca.Metadata.Name
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetType returns user or host certificate authority
|
2016-12-29 20:23:58 +00:00
|
|
|
func (ca *CertAuthorityV2) GetType() CertAuthType {
|
2016-12-28 22:07:03 +00:00
|
|
|
return ca.Spec.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetClusterName returns cluster name this cert authority
|
|
|
|
// is associated with
|
2016-12-29 20:23:58 +00:00
|
|
|
func (ca *CertAuthorityV2) GetClusterName() string {
|
2016-12-28 22:07:03 +00:00
|
|
|
return ca.Spec.ClusterName
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetCheckingKeys returns public keys to check signature
|
2016-12-29 20:23:58 +00:00
|
|
|
func (ca *CertAuthorityV2) GetCheckingKeys() [][]byte {
|
2016-12-28 22:07:03 +00:00
|
|
|
return ca.Spec.CheckingKeys
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetRoles returns a list of roles assumed by users signed by this CA
|
2016-12-29 20:23:58 +00:00
|
|
|
func (ca *CertAuthorityV2) GetRoles() []string {
|
2016-12-28 22:07:03 +00:00
|
|
|
return ca.Spec.Roles
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetRawObject returns raw object data, used for migrations
|
2016-12-29 20:23:58 +00:00
|
|
|
func (ca *CertAuthorityV2) GetRawObject() interface{} {
|
2016-12-28 22:07:03 +00:00
|
|
|
return ca.rawObject
|
|
|
|
}
|
|
|
|
|
2016-12-28 00:39:43 +00:00
|
|
|
// FirstSigningKey returns first signing key or returns error if it's not here
|
2016-12-29 20:23:58 +00:00
|
|
|
func (ca *CertAuthorityV2) FirstSigningKey() ([]byte, error) {
|
2016-12-28 00:39:43 +00:00
|
|
|
if len(ca.Spec.SigningKeys) == 0 {
|
|
|
|
return nil, trace.NotFound("%v has no signing keys", ca.Metadata.Name)
|
|
|
|
}
|
|
|
|
return ca.Spec.SigningKeys[0], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ID returns id (consisting of domain name and type) that
|
|
|
|
// identifies the authority this key belongs to
|
2016-12-29 20:23:58 +00:00
|
|
|
func (ca *CertAuthorityV2) ID() *CertAuthID {
|
2016-12-28 00:39:43 +00:00
|
|
|
return &CertAuthID{DomainName: ca.Spec.ClusterName, Type: ca.Spec.Type}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checkers returns public keys that can be used to check cert authorities
|
2016-12-29 20:23:58 +00:00
|
|
|
func (ca *CertAuthorityV2) Checkers() ([]ssh.PublicKey, error) {
|
2016-12-28 00:39:43 +00:00
|
|
|
out := make([]ssh.PublicKey, 0, len(ca.Spec.CheckingKeys))
|
|
|
|
for _, keyBytes := range ca.Spec.CheckingKeys {
|
|
|
|
key, _, _, _, err := ssh.ParseAuthorizedKey(keyBytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.BadParameter("invalid authority public key (len=%d): %v", len(keyBytes), err)
|
|
|
|
}
|
|
|
|
out = append(out, key)
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Signers returns a list of signers that could be used to sign keys
|
2016-12-29 20:23:58 +00:00
|
|
|
func (ca *CertAuthorityV2) Signers() ([]ssh.Signer, error) {
|
2016-12-28 00:39:43 +00:00
|
|
|
out := make([]ssh.Signer, 0, len(ca.Spec.SigningKeys))
|
|
|
|
for _, keyBytes := range ca.Spec.SigningKeys {
|
|
|
|
signer, err := ssh.ParsePrivateKey(keyBytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
out = append(out, signer)
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check checks if all passed parameters are valid
|
2016-12-29 20:23:58 +00:00
|
|
|
func (ca *CertAuthorityV2) Check() error {
|
2016-12-28 00:39:43 +00:00
|
|
|
err := ca.ID().Check()
|
|
|
|
if err != nil {
|
|
|
|
return trace.Wrap(err)
|
|
|
|
}
|
|
|
|
_, err = ca.Checkers()
|
|
|
|
if err != nil {
|
|
|
|
return trace.Wrap(err)
|
|
|
|
}
|
|
|
|
_, err = ca.Signers()
|
|
|
|
if err != nil {
|
|
|
|
return trace.Wrap(err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-12-29 20:23:58 +00:00
|
|
|
// CertAuthoritySpecV2 is a host or user certificate authority that
|
2016-12-28 00:39:43 +00:00
|
|
|
// can check and if it has private key stored as well, sign it too
|
2016-12-29 20:23:58 +00:00
|
|
|
type CertAuthoritySpecV2 struct {
|
2016-12-28 00:39:43 +00:00
|
|
|
// Type is either user or host certificate authority
|
|
|
|
Type CertAuthType `json:"type"`
|
|
|
|
// ClusterName identifies cluster name this authority serves,
|
|
|
|
// for host authorities that means base hostname of all servers,
|
|
|
|
// for user authorities that means organization name
|
|
|
|
ClusterName string `json:"cluster_name"`
|
|
|
|
// Checkers is a list of SSH public keys that can be used to check
|
|
|
|
// certificate signatures
|
|
|
|
CheckingKeys [][]byte `json:"checking_keys"`
|
|
|
|
// SigningKeys is a list of private keys used for signing
|
|
|
|
SigningKeys [][]byte `json:"signing_keys"`
|
|
|
|
// Roles is a list of roles assumed by users signed by this CA
|
2016-12-29 02:47:33 +00:00
|
|
|
Roles []string `json:"roles,omitempty"`
|
2016-12-28 00:39:43 +00:00
|
|
|
}
|
|
|
|
|
2016-12-29 20:23:58 +00:00
|
|
|
// CertAuthoritySpecV2Schema is JSON schema for cert authority V2
|
|
|
|
const CertAuthoritySpecV2Schema = `{
|
2016-12-28 00:39:43 +00:00
|
|
|
"type": "object",
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": ["type", "cluster_name", "checking_keys", "signing_keys"],
|
|
|
|
"properties": {
|
|
|
|
"type": {"type": "string"},
|
|
|
|
"cluster_name": {"type": "string"},
|
|
|
|
"checking_keys": {
|
|
|
|
"type": "array",
|
|
|
|
"items": {
|
2016-12-29 02:47:33 +00:00
|
|
|
"type": "string"
|
2016-12-28 00:39:43 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
"signing_keys": {
|
|
|
|
"type": "array",
|
|
|
|
"items": {
|
2016-12-29 02:47:33 +00:00
|
|
|
"type": "string"
|
2016-12-28 00:39:43 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
"roles": {
|
|
|
|
"type": "array",
|
|
|
|
"items": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
|
|
|
// CertAuthorityV0 is a host or user certificate authority that
|
|
|
|
// can check and if it has private key stored as well, sign it too
|
|
|
|
type CertAuthorityV0 struct {
|
|
|
|
// Type is either user or host certificate authority
|
|
|
|
Type CertAuthType `json:"type"`
|
|
|
|
// DomainName identifies domain name this authority serves,
|
|
|
|
// for host authorities that means base hostname of all servers,
|
|
|
|
// for user authorities that means organization name
|
|
|
|
DomainName string `json:"domain_name"`
|
|
|
|
// Checkers is a list of SSH public keys that can be used to check
|
|
|
|
// certificate signatures
|
|
|
|
CheckingKeys [][]byte `json:"checking_keys"`
|
|
|
|
// SigningKeys is a list of private keys used for signing
|
|
|
|
SigningKeys [][]byte `json:"signing_keys"`
|
|
|
|
// AllowedLogins is a list of allowed logins for users within
|
|
|
|
// this certificate authority
|
|
|
|
AllowedLogins []string `json:"allowed_logins"`
|
|
|
|
}
|
2016-12-28 01:28:46 +00:00
|
|
|
|
2016-12-29 20:23:58 +00:00
|
|
|
func (c *CertAuthorityV0) V2() *CertAuthorityV2 {
|
|
|
|
return &CertAuthorityV2{
|
2016-12-28 01:28:46 +00:00
|
|
|
Kind: KindCertAuthority,
|
2016-12-29 20:23:58 +00:00
|
|
|
Version: V2,
|
2016-12-28 01:28:46 +00:00
|
|
|
Metadata: Metadata{
|
2016-12-28 23:50:32 +00:00
|
|
|
Name: c.DomainName,
|
|
|
|
Namespace: defaults.Namespace,
|
2016-12-28 01:28:46 +00:00
|
|
|
},
|
2016-12-29 20:23:58 +00:00
|
|
|
Spec: CertAuthoritySpecV2{
|
2016-12-28 01:28:46 +00:00
|
|
|
Type: c.Type,
|
|
|
|
ClusterName: c.DomainName,
|
|
|
|
CheckingKeys: c.CheckingKeys,
|
|
|
|
SigningKeys: c.SigningKeys,
|
|
|
|
},
|
|
|
|
rawObject: *c,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var certAuthorityMarshaler CertAuthorityMarshaler = &TeleportCertAuthorityMarshaler{}
|
|
|
|
|
|
|
|
// SetCertAuthorityMarshaler sets global user marshaler
|
|
|
|
func SetCertAuthorityMarshaler(u CertAuthorityMarshaler) {
|
|
|
|
marshalerMutex.Lock()
|
|
|
|
defer marshalerMutex.Unlock()
|
|
|
|
certAuthorityMarshaler = u
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetCertAuthorityMarshaler returns currently set user marshaler
|
|
|
|
func GetCertAuthorityMarshaler() CertAuthorityMarshaler {
|
|
|
|
marshalerMutex.RLock()
|
|
|
|
defer marshalerMutex.RUnlock()
|
|
|
|
return certAuthorityMarshaler
|
|
|
|
}
|
|
|
|
|
|
|
|
// CertAuthorityMarshaler implements marshal/unmarshal of User implementations
|
|
|
|
// mostly adds support for extended versions
|
|
|
|
type CertAuthorityMarshaler interface {
|
|
|
|
// UnmarshalCertAuthority unmarhsals cert authority from binary representation
|
2016-12-28 22:07:03 +00:00
|
|
|
UnmarshalCertAuthority(bytes []byte) (CertAuthority, error)
|
2016-12-28 01:28:46 +00:00
|
|
|
// MarshalCertAuthority to binary representation
|
|
|
|
MarshalCertAuthority(c CertAuthority) ([]byte, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetCertAuthoritySchema returns JSON Schema for cert authorities
|
|
|
|
func GetCertAuthoritySchema() string {
|
2016-12-29 20:23:58 +00:00
|
|
|
return fmt.Sprintf(V2SchemaTemplate, MetadataSchema, CertAuthoritySpecV2Schema)
|
2016-12-28 01:28:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type TeleportCertAuthorityMarshaler struct{}
|
|
|
|
|
|
|
|
// UnmarshalUser unmarshals user from JSON
|
2016-12-28 22:07:03 +00:00
|
|
|
func (*TeleportCertAuthorityMarshaler) UnmarshalCertAuthority(bytes []byte) (CertAuthority, error) {
|
2016-12-28 01:28:46 +00:00
|
|
|
var h ResourceHeader
|
|
|
|
err := json.Unmarshal(bytes, &h)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
switch h.Version {
|
|
|
|
case "":
|
|
|
|
var ca CertAuthorityV0
|
|
|
|
err := json.Unmarshal(bytes, &ca)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2016-12-29 20:23:58 +00:00
|
|
|
return ca.V2(), nil
|
|
|
|
case V2:
|
|
|
|
var ca CertAuthorityV2
|
2016-12-28 01:28:46 +00:00
|
|
|
if err := utils.UnmarshalWithSchema(GetCertAuthoritySchema(), &ca, bytes); err != nil {
|
|
|
|
return nil, trace.BadParameter(err.Error())
|
|
|
|
}
|
2016-12-29 02:54:10 +00:00
|
|
|
return &ca, nil
|
2016-12-28 01:28:46 +00:00
|
|
|
}
|
|
|
|
|
2016-12-29 02:54:10 +00:00
|
|
|
return nil, trace.BadParameter("cert authority resource version %v is not supported", h.Version)
|
2016-12-28 01:28:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// MarshalUser marshalls cert authority into JSON
|
|
|
|
func (*TeleportCertAuthorityMarshaler) MarshalCertAuthority(ca CertAuthority) ([]byte, error) {
|
2016-12-28 22:07:03 +00:00
|
|
|
return json.Marshal(ca)
|
2016-12-28 01:28:46 +00:00
|
|
|
}
|