teleport/lib/services/authority.go
Andrew Lytvynov 2172965057 Remove alg argument from CertAuthority.Signers
It's no longer needed, since CertAuthority contains the signing
algorithm internally.
2020-06-24 21:25:33 +00:00

1098 lines
33 KiB
Go

/*
Copyright 2017-2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package services
import (
"crypto/x509"
"encoding/json"
"fmt"
"time"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/sshutils"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/teleport/lib/wrappers"
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
"github.com/tstranex/u2f"
"golang.org/x/crypto/ssh"
)
// HostCertParams defines all parameters needed to generate a host certificate
type HostCertParams struct {
// PrivateCASigningKey is the private key of the CA that will sign the public key of the host
PrivateCASigningKey []byte
// CASigningAlg is the signature algorithm used by the CA private key.
CASigningAlg string
// PublicHostKey is the public key of the host
PublicHostKey []byte
// HostID is used by Teleport to uniquely identify a node within a cluster
HostID string
// Principals is a list of additional principals to add to the certificate.
Principals []string
// NodeName is the DNS name of the node
NodeName string
// ClusterName is the name of the cluster within which a node lives
ClusterName string
// Roles identifies the roles of a Teleport instance
Roles teleport.Roles
// TTL defines how long a certificate is valid for
TTL time.Duration
}
// Check checks parameters for errors
func (c HostCertParams) Check() error {
if len(c.PrivateCASigningKey) == 0 || c.CASigningAlg == "" {
return trace.BadParameter("PrivateCASigningKey and CASigningAlg are required")
}
if c.HostID == "" && len(c.Principals) == 0 {
return trace.BadParameter("HostID [%q] or Principals [%q] are required",
c.HostID, c.Principals)
}
if c.ClusterName == "" {
return trace.BadParameter("ClusterName [%q] is required", c.ClusterName)
}
if err := c.Roles.Check(); err != nil {
return trace.Wrap(err)
}
return nil
}
// ChangePasswordReq defines a request to change user password
type ChangePasswordReq struct {
// User is user ID
User string
// OldPassword is user current password
OldPassword []byte `json:"old_password"`
// NewPassword is user new password
NewPassword []byte `json:"new_password"`
// SecondFactorToken is user 2nd factor token
SecondFactorToken string `json:"second_factor_token"`
// U2FSignResponse is U2F sign response
U2FSignResponse *u2f.SignResponse `json:"u2f_sign_response"`
}
// UserCertParams defines OpenSSH user certificate parameters
type UserCertParams struct {
// PrivateCASigningKey is the private key of the CA that will sign the public key of the user
PrivateCASigningKey []byte
// CASigningAlg is the signature algorithm used by the CA private key.
CASigningAlg string
// PublicUserKey is the public key of the user
PublicUserKey []byte
// TTL defines how long a certificate is valid for
TTL time.Duration
// Username is teleport username
Username string
// AllowedLogins is a list of SSH principals
AllowedLogins []string
// PermitX11Forwarding permits X11 forwarding for this cert
PermitX11Forwarding bool
// PermitAgentForwarding permits agent forwarding for this cert
PermitAgentForwarding bool
// PermitPortForwarding permits port forwarding.
PermitPortForwarding bool
// Roles is a list of roles assigned to this user
Roles []string
// CertificateFormat is the format of the SSH certificate.
CertificateFormat string
// RouteToCluster specifies the target cluster
// if present in the certificate, will be used
// to route the requests to
RouteToCluster string
// Traits hold claim data used to populate a role at runtime.
Traits wrappers.Traits
// ActiveRequests tracks privilege escalation requests applied during
// certificate construction.
ActiveRequests RequestIDs
}
func (c UserCertParams) Check() error {
if len(c.PrivateCASigningKey) == 0 || c.CASigningAlg == "" {
return trace.BadParameter("PrivateCASigningKey and CASigningAlg are required")
}
if c.TTL < defaults.MinCertDuration {
return trace.BadParameter("TTL can't be less than %v", defaults.MinCertDuration)
}
if len(c.AllowedLogins) == 0 {
return trace.BadParameter("AllowedLogins are required")
}
return nil
}
// CertRoles defines certificate roles
type CertRoles struct {
// Version is current version of the roles
Version string `json:"version"`
// Roles is a list of roles
Roles []string `json:"roles"`
}
// CertRolesSchema defines cert roles schema
const CertRolesSchema = `{
"type": "object",
"additionalProperties": false,
"properties": {
"version": {"type": "string"},
"roles": {
"type": "array",
"items": {
"type": "string"
}
}
}
}`
// MarshalCertRoles marshal roles list to OpenSSH
func MarshalCertRoles(roles []string) (string, error) {
out, err := json.Marshal(CertRoles{Version: V1, Roles: roles})
if err != nil {
return "", trace.Wrap(err)
}
return string(out), err
}
// UnmarshalCertRoles marshals roles list to OpenSSH
func UnmarshalCertRoles(data string) ([]string, error) {
var certRoles CertRoles
if err := utils.UnmarshalWithSchema(CertRolesSchema, &certRoles, []byte(data)); err != nil {
return nil, trace.BadParameter(err.Error())
}
return certRoles.Roles, nil
}
// 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 {
// ResourceWithSecrets sets common resource properties
ResourceWithSecrets
// GetID returns certificate authority ID -
// combined type and name
GetID() CertAuthID
// GetType returns user or host certificate authority
GetType() CertAuthType
// GetClusterName returns cluster name this cert authority
// is associated with
GetClusterName() string
// GetCheckingKeys returns public keys to check signature
GetCheckingKeys() [][]byte
// GetSigningKeys returns signing keys
GetSigningKeys() [][]byte
// CombinedMapping is used to specify combined mapping from legacy property Roles
// and new property RoleMap
CombinedMapping() RoleMap
// GetRoleMap returns role map property
GetRoleMap() RoleMap
// SetRoleMap sets role map
SetRoleMap(m RoleMap)
// GetRoles returns a list of roles assumed by users signed by this CA
GetRoles() []string
// SetRoles sets assigned roles for this certificate authority
SetRoles(roles []string)
// FirstSigningKey returns first signing key or returns error if it's not here
// The first key is returned because multiple keys can exist during key rotation.
FirstSigningKey() ([]byte, error)
// Check checks object for errors
Check() error
// CheckAndSetDefaults checks and set default values for any missing fields.
CheckAndSetDefaults() error
// SetSigningKeys sets signing keys
SetSigningKeys([][]byte) error
// SetCheckingKeys sets signing keys
SetCheckingKeys([][]byte) error
// AddRole adds a role to ca role list
AddRole(name string)
// Checkers returns public keys that can be used to check cert authorities
Checkers() ([]ssh.PublicKey, error)
// Signers returns a list of signers that could be used to sign keys
Signers() ([]ssh.Signer, error)
// V1 returns V1 version of the resource
V1() *CertAuthorityV1
// V2 returns V2 version of the resource
V2() *CertAuthorityV2
// String returns human readable version of the CertAuthority
String() string
// TLSCA returns first TLS certificate authority from the list of key pairs
TLSCA() (*tlsca.CertAuthority, error)
// SetTLSKeyPairs sets TLS key pairs
SetTLSKeyPairs(keyPairs []TLSKeyPair)
// GetTLSKeyPairs returns first PEM encoded TLS cert
GetTLSKeyPairs() []TLSKeyPair
// GetRotation returns rotation state.
GetRotation() Rotation
// SetRotation sets rotation state.
SetRotation(Rotation)
// GetSigningAlg returns the signing algorithm used by signing keys.
GetSigningAlg() string
// SetSigningAlg sets the signing algorithm used by signing keys.
SetSigningAlg(string)
// Clone returns a copy of the cert authority object.
Clone() CertAuthority
}
// CertPoolFromCertAuthorities returns certificate pools from TLS certificates
// set up in the certificate authorities list
func CertPoolFromCertAuthorities(cas []CertAuthority) (*x509.CertPool, error) {
certPool := x509.NewCertPool()
for _, ca := range cas {
keyPairs := ca.GetTLSKeyPairs()
if len(keyPairs) == 0 {
continue
}
for _, keyPair := range keyPairs {
cert, err := tlsca.ParseCertificatePEM(keyPair.Cert)
if err != nil {
return nil, trace.Wrap(err)
}
certPool.AddCert(cert)
}
return certPool, nil
}
return certPool, nil
}
// CertPool returns certificate pools from TLS certificates
// set up in the certificate authority
func CertPool(ca CertAuthority) (*x509.CertPool, error) {
keyPairs := ca.GetTLSKeyPairs()
if len(keyPairs) == 0 {
return nil, trace.BadParameter("certificate authority has no TLS certificates")
}
certPool := x509.NewCertPool()
for _, keyPair := range keyPairs {
cert, err := tlsca.ParseCertificatePEM(keyPair.Cert)
if err != nil {
return nil, trace.Wrap(err)
}
certPool.AddCert(cert)
}
return certPool, nil
}
// TLSCerts returns TLS certificates from CA
func TLSCerts(ca CertAuthority) [][]byte {
pairs := ca.GetTLSKeyPairs()
out := make([][]byte, len(pairs))
for i, pair := range pairs {
out[i] = append([]byte{}, pair.Cert...)
}
return out
}
// NewCertAuthority returns new cert authority
func NewCertAuthority(
caType CertAuthType,
clusterName string,
signingKeys [][]byte,
checkingKeys [][]byte,
roles []string,
signingAlg CertAuthoritySpecV2_SigningAlgType,
) CertAuthority {
return &CertAuthorityV2{
Kind: KindCertAuthority,
Version: V2,
SubKind: string(caType),
Metadata: Metadata{
Name: clusterName,
Namespace: defaults.Namespace,
},
Spec: CertAuthoritySpecV2{
Roles: roles,
Type: caType,
ClusterName: clusterName,
CheckingKeys: checkingKeys,
SigningKeys: signingKeys,
SigningAlg: signingAlg,
},
}
}
// CertAuthoritiesToV1 converts list of cert authorities to V1 slice
func CertAuthoritiesToV1(in []CertAuthority) ([]CertAuthorityV1, error) {
out := make([]CertAuthorityV1, len(in))
type cav1 interface {
V1() *CertAuthorityV1
}
for i, ca := range in {
v1, ok := ca.(cav1)
if !ok {
return nil, trace.BadParameter("could not transform object to V1")
}
out[i] = *(v1.V1())
}
return out, nil
}
// GetVersion returns resource version
func (c *CertAuthorityV2) GetVersion() string {
return c.Version
}
// GetKind returns resource kind
func (c *CertAuthorityV2) GetKind() string {
return c.Kind
}
// GetSubKind returns resource sub kind
func (c *CertAuthorityV2) GetSubKind() string {
return c.SubKind
}
// SetSubKind sets resource subkind
func (c *CertAuthorityV2) SetSubKind(s string) {
c.SubKind = s
}
// Clone returns a copy of the cert authority object.
func (c *CertAuthorityV2) Clone() CertAuthority {
out := *c
out.Spec.CheckingKeys = utils.CopyByteSlices(c.Spec.CheckingKeys)
out.Spec.SigningKeys = utils.CopyByteSlices(c.Spec.SigningKeys)
for i, kp := range c.Spec.TLSKeyPairs {
out.Spec.TLSKeyPairs[i] = TLSKeyPair{
Key: utils.CopyByteSlice(kp.Key),
Cert: utils.CopyByteSlice(kp.Cert),
}
}
out.Spec.Roles = utils.CopyStrings(c.Spec.Roles)
return &out
}
// GetRotation returns rotation state.
func (c *CertAuthorityV2) GetRotation() Rotation {
if c.Spec.Rotation == nil {
return Rotation{}
}
return *c.Spec.Rotation
}
// SetRotation sets rotation state.
func (c *CertAuthorityV2) SetRotation(r Rotation) {
c.Spec.Rotation = &r
}
// TLSCA returns TLS certificate authority
func (c *CertAuthorityV2) TLSCA() (*tlsca.CertAuthority, error) {
if len(c.Spec.TLSKeyPairs) == 0 {
return nil, trace.BadParameter("no TLS key pairs found for certificate authority")
}
return tlsca.New(c.Spec.TLSKeyPairs[0].Cert, c.Spec.TLSKeyPairs[0].Key)
}
// SetTLSPrivateKey sets TLS key pairs
func (c *CertAuthorityV2) SetTLSKeyPairs(pairs []TLSKeyPair) {
c.Spec.TLSKeyPairs = pairs
}
// GetTLSPrivateKey returns TLS key pairs
func (c *CertAuthorityV2) GetTLSKeyPairs() []TLSKeyPair {
return c.Spec.TLSKeyPairs
}
// GetMetadata returns object metadata
func (c *CertAuthorityV2) GetMetadata() Metadata {
return c.Metadata
}
// SetExpiry sets expiry time for the object
func (c *CertAuthorityV2) SetExpiry(expires time.Time) {
c.Metadata.SetExpiry(expires)
}
// Expires returns object expiry setting
func (c *CertAuthorityV2) Expiry() time.Time {
return c.Metadata.Expiry()
}
// SetTTL sets Expires header using realtime clock
func (c *CertAuthorityV2) SetTTL(clock clockwork.Clock, ttl time.Duration) {
c.Metadata.SetTTL(clock, ttl)
}
// GetResourceID returns resource ID
func (c *CertAuthorityV2) GetResourceID() int64 {
return c.Metadata.ID
}
// SetResourceID sets resource ID
func (c *CertAuthorityV2) SetResourceID(id int64) {
c.Metadata.ID = id
}
// WithoutSecrets returns an instance of resource without secrets.
func (c *CertAuthorityV2) WithoutSecrets() Resource {
c2 := c.Clone()
RemoveCASecrets(c2)
return c2
}
// V2 returns V2 version of the resouirce - itself
func (c *CertAuthorityV2) V2() *CertAuthorityV2 {
return c
}
// String returns human readable version of the CertAuthorityV2.
func (c *CertAuthorityV2) String() string {
return fmt.Sprintf("CA(name=%v, type=%v)", c.GetClusterName(), c.GetType())
}
// V1 returns V1 version of the object
func (c *CertAuthorityV2) V1() *CertAuthorityV1 {
return &CertAuthorityV1{
Type: c.Spec.Type,
DomainName: c.Spec.ClusterName,
CheckingKeys: c.Spec.CheckingKeys,
SigningKeys: c.Spec.SigningKeys,
}
}
// AddRole adds a role to ca role list
func (ca *CertAuthorityV2) AddRole(name string) {
for _, r := range ca.Spec.Roles {
if r == name {
return
}
}
ca.Spec.Roles = append(ca.Spec.Roles, name)
}
// GetSigning keys returns signing keys
func (ca *CertAuthorityV2) GetSigningKeys() [][]byte {
return ca.Spec.SigningKeys
}
// SetSigningKeys sets signing keys
func (ca *CertAuthorityV2) SetSigningKeys(keys [][]byte) error {
ca.Spec.SigningKeys = keys
return nil
}
// SetCheckingKeys sets SSH public keys
func (ca *CertAuthorityV2) SetCheckingKeys(keys [][]byte) error {
ca.Spec.CheckingKeys = keys
return nil
}
// GetID returns certificate authority ID -
// combined type and name
func (ca *CertAuthorityV2) GetID() CertAuthID {
return CertAuthID{Type: ca.Spec.Type, DomainName: ca.Metadata.Name}
}
// SetName sets cert authority name
func (ca *CertAuthorityV2) SetName(name string) {
ca.Metadata.SetName(name)
}
// GetName returns cert authority name
func (ca *CertAuthorityV2) GetName() string {
return ca.Metadata.Name
}
// GetType returns user or host certificate authority
func (ca *CertAuthorityV2) GetType() CertAuthType {
return ca.Spec.Type
}
// GetClusterName returns cluster name this cert authority
// is associated with.
func (ca *CertAuthorityV2) GetClusterName() string {
return ca.Spec.ClusterName
}
// GetCheckingKeys returns public keys to check signature
func (ca *CertAuthorityV2) GetCheckingKeys() [][]byte {
return ca.Spec.CheckingKeys
}
// GetRoles returns a list of roles assumed by users signed by this CA
func (ca *CertAuthorityV2) GetRoles() []string {
return ca.Spec.Roles
}
// SetRoles sets assigned roles for this certificate authority
func (ca *CertAuthorityV2) SetRoles(roles []string) {
ca.Spec.Roles = roles
}
// CombinedMapping is used to specify combined mapping from legacy property Roles
// and new property RoleMap
func (ca *CertAuthorityV2) CombinedMapping() RoleMap {
if len(ca.Spec.Roles) != 0 {
return RoleMap([]RoleMapping{{Remote: Wildcard, Local: ca.Spec.Roles}})
}
return RoleMap(ca.Spec.RoleMap)
}
// GetRoleMap returns role map property
func (ca *CertAuthorityV2) GetRoleMap() RoleMap {
return RoleMap(ca.Spec.RoleMap)
}
// SetRoleMap sets role map
func (c *CertAuthorityV2) SetRoleMap(m RoleMap) {
c.Spec.RoleMap = []RoleMapping(m)
}
// FirstSigningKey returns first signing key or returns error if it's not here
func (ca *CertAuthorityV2) FirstSigningKey() ([]byte, error) {
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
func (ca *CertAuthorityV2) ID() *CertAuthID {
return &CertAuthID{DomainName: ca.Spec.ClusterName, Type: ca.Spec.Type}
}
// Checkers returns public keys that can be used to check cert authorities
func (ca *CertAuthorityV2) Checkers() ([]ssh.PublicKey, error) {
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.
func (ca *CertAuthorityV2) Signers() ([]ssh.Signer, error) {
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)
}
signer = sshutils.AlgSigner(signer, ca.GetSigningAlg())
out = append(out, signer)
}
return out, nil
}
func (ca *CertAuthorityV2) GetSigningAlg() string {
switch ca.Spec.SigningAlg {
// UNKNOWN algorithm can come from a cluster that existed before SigningAlg
// field was added. Default to RSA-SHA1 to match the implicit algorithm
// used in those clusters.
case CertAuthoritySpecV2_RSA_SHA1, CertAuthoritySpecV2_UNKNOWN:
return ssh.SigAlgoRSA
case CertAuthoritySpecV2_RSA_SHA2_256:
return ssh.SigAlgoRSASHA2256
case CertAuthoritySpecV2_RSA_SHA2_512:
return ssh.SigAlgoRSASHA2512
default:
return ""
}
}
func (ca *CertAuthorityV2) SetSigningAlg(alg string) {
ca.Spec.SigningAlg = ParseSigningAlg(alg)
}
// Check checks if all passed parameters are valid
func (ca *CertAuthorityV2) Check() error {
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)
}
// This is to force users to migrate
if len(ca.Spec.Roles) != 0 && len(ca.Spec.RoleMap) != 0 {
return trace.BadParameter("should set either 'roles' or 'role_map', not both")
}
if err := RoleMap(ca.Spec.RoleMap).Check(); err != nil {
return trace.Wrap(err)
}
return nil
}
// CheckAndSetDefaults checks and set default values for any missing fields.
func (ca *CertAuthorityV2) CheckAndSetDefaults() error {
err := ca.Metadata.CheckAndSetDefaults()
if err != nil {
return trace.Wrap(err)
}
err = ca.Check()
if err != nil {
return trace.Wrap(err)
}
return nil
}
// ParseSigningAlg converts the SSH signature algorithm strings to the
// corresponding proto enum value.
//
// alg should be one of ssh.SigAlgo* constants. If it's not one of those
// constants, CertAuthoritySpecV2_UNKNOWN is returned.
func ParseSigningAlg(alg string) CertAuthoritySpecV2_SigningAlgType {
switch alg {
case ssh.SigAlgoRSA:
return CertAuthoritySpecV2_RSA_SHA1
case ssh.SigAlgoRSASHA2256:
return CertAuthoritySpecV2_RSA_SHA2_256
case ssh.SigAlgoRSASHA2512:
return CertAuthoritySpecV2_RSA_SHA2_512
default:
return CertAuthoritySpecV2_UNKNOWN
}
}
// RemoveCASecrets removes secret values and keys
// from the certificate authority
func RemoveCASecrets(ca CertAuthority) {
ca.SetSigningKeys(nil)
keyPairs := ca.GetTLSKeyPairs()
for i := range keyPairs {
keyPairs[i].Key = nil
}
ca.SetTLSKeyPairs(keyPairs)
}
const (
// RotationStateStandby is initial status of the rotation -
// nothing is being rotated.
RotationStateStandby = "standby"
// RotationStateInProgress - that rotation is in progress.
RotationStateInProgress = "in_progress"
// RotationPhaseStandby is the initial phase of the rotation
// it means no operations have started.
RotationPhaseStandby = "standby"
// RotationPhaseInit = is a phase of the rotation
// when new certificate authoirty is issued, but not used
// It is necessary for remote trusted clusters to fetch the
// new certificate authority, otherwise the new clients
// will reject it
RotationPhaseInit = "init"
// RotationPhaseUpdateClients is a phase of the rotation
// when client credentials will have to be updated and reloaded
// but servers will use and respond with old credentials
// because clients have no idea about new credentials at first.
RotationPhaseUpdateClients = "update_clients"
// RotationPhaseUpdateServers is a phase of the rotation
// when servers will have to reload and should start serving
// TLS and SSH certificates signed by new CA.
RotationPhaseUpdateServers = "update_servers"
// RotationPhaseRollback means that rotation is rolling
// back to the old certificate authority.
RotationPhaseRollback = "rollback"
// RotationModeManual is a manual rotation mode when all phases
// are set by the operator.
RotationModeManual = "manual"
// RotationModeAuto is set to go through all phases by the schedule.
RotationModeAuto = "auto"
)
// RotatePhases lists all supported rotation phases
var RotatePhases = []string{
RotationPhaseInit,
RotationPhaseStandby,
RotationPhaseUpdateClients,
RotationPhaseUpdateServers,
RotationPhaseRollback,
}
// Matches returns true if this state rotation matches
// external rotation state, phase and rotation ID should match,
// notice that matches does not behave like Equals because it does not require
// all fields to be the same.
func (s *Rotation) Matches(rotation Rotation) bool {
return s.CurrentID == rotation.CurrentID && s.State == rotation.State && s.Phase == rotation.Phase
}
// LastRotatedDescription returns human friendly description.
func (r *Rotation) LastRotatedDescription() string {
if r.LastRotated.IsZero() {
return "never updated"
}
return fmt.Sprintf("last rotated %v", r.LastRotated.Format(teleport.HumanDateFormatSeconds))
}
// PhaseDescription returns human friendly description of a current rotation phase.
func (r *Rotation) PhaseDescription() string {
switch r.Phase {
case RotationPhaseInit:
return "initialized"
case RotationPhaseStandby, "":
return "on standby"
case RotationPhaseUpdateClients:
return "rotating clients"
case RotationPhaseUpdateServers:
return "rotating servers"
case RotationPhaseRollback:
return "rolling back"
default:
return fmt.Sprintf("unknown phase: %q", r.Phase)
}
}
// String returns user friendly information about certificate authority.
func (r *Rotation) String() string {
switch r.State {
case "", RotationStateStandby:
if r.LastRotated.IsZero() {
return "never updated"
}
return fmt.Sprintf("rotated %v", r.LastRotated.Format(teleport.HumanDateFormatSeconds))
case RotationStateInProgress:
return fmt.Sprintf("%v (mode: %v, started: %v, ending: %v)",
r.PhaseDescription(),
r.Mode,
r.Started.Format(teleport.HumanDateFormatSeconds),
r.Started.Add(r.GracePeriod.Duration()).Format(teleport.HumanDateFormatSeconds),
)
default:
return "unknown"
}
}
// CheckAndSetDefaults checks and sets default rotation parameters.
func (r *Rotation) CheckAndSetDefaults(clock clockwork.Clock) error {
switch r.Phase {
case "", RotationPhaseRollback, RotationPhaseUpdateClients, RotationPhaseUpdateServers:
default:
return trace.BadParameter("unsupported phase: %q", r.Phase)
}
switch r.Mode {
case "", RotationModeAuto, RotationModeManual:
default:
return trace.BadParameter("unsupported mode: %q", r.Mode)
}
switch r.State {
case "":
r.State = RotationStateStandby
case RotationStateStandby:
case RotationStateInProgress:
if r.CurrentID == "" {
return trace.BadParameter("set 'current_id' parameter for in progress rotation")
}
if r.Started.IsZero() {
return trace.BadParameter("set 'started' parameter for in progress rotation")
}
default:
return trace.BadParameter(
"unsupported rotation 'state': %q, supported states are: %q, %q",
r.State, RotationStateStandby, RotationStateInProgress)
}
return nil
}
// GenerateSchedule generates schedule based on the time period, using
// even time periods between rotation phases.
func GenerateSchedule(clock clockwork.Clock, gracePeriod time.Duration) (*RotationSchedule, error) {
if gracePeriod <= 0 {
return nil, trace.BadParameter("invalid grace period %q, provide value > 0", gracePeriod)
}
return &RotationSchedule{
UpdateClients: clock.Now().UTC().Add(gracePeriod / 3).UTC(),
UpdateServers: clock.Now().UTC().Add((gracePeriod * 2) / 3).UTC(),
Standby: clock.Now().UTC().Add(gracePeriod).UTC(),
}, nil
}
// CheckAndSetDefaults checks and sets default values of the rotation schedule.
func (s *RotationSchedule) CheckAndSetDefaults(clock clockwork.Clock) error {
if s.UpdateServers.IsZero() {
return trace.BadParameter("phase %q has no time switch scheduled", RotationPhaseUpdateServers)
}
if s.Standby.IsZero() {
return trace.BadParameter("phase %q has no time switch scheduled", RotationPhaseStandby)
}
if s.Standby.Before(s.UpdateServers) {
return trace.BadParameter("phase %q can not be scheduled before %q", RotationPhaseStandby, RotationPhaseUpdateServers)
}
if s.UpdateServers.Before(clock.Now()) {
return trace.BadParameter("phase %q can not be scheduled in the past", RotationPhaseUpdateServers)
}
if s.Standby.Before(clock.Now()) {
return trace.BadParameter("phase %q can not be scheduled in the past", RotationPhaseStandby)
}
return nil
}
// CertAuthoritySpecV2Schema is JSON schema for cert authority V2
const CertAuthoritySpecV2Schema = `{
"type": "object",
"additionalProperties": false,
"required": ["type", "cluster_name", "checking_keys"],
"properties": {
"type": {"type": "string"},
"cluster_name": {"type": "string"},
"checking_keys": {
"type": "array",
"items": {
"type": "string"
}
},
"signing_keys": {
"type": "array",
"items": {
"type": "string"
}
},
"roles": {
"type": "array",
"items": {
"type": "string"
}
},
"tls_key_pairs": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"cert": {"type": "string"},
"key": {"type": "string"}
}
}
},
"signing_alg": {"type": "integer"},
"rotation": %v,
"role_map": %v
}
}`
// RotationSchema is a JSON validation schema of the CA rotation state object.
const RotationSchema = `{
"type": "object",
"additionalProperties": false,
"properties": {
"state": {"type": "string"},
"phase": {"type": "string"},
"mode": {"type": "string"},
"current_id": {"type": "string"},
"started": {"type": "string"},
"grace_period": {"type": "string"},
"last_rotated": {"type": "string"},
"schedule": {
"type": "object",
"properties": {
"update_clients": {"type": "string"},
"update_servers": {"type": "string"},
"standby": {"type": "string"}
}
}
}
}`
// CertAuthorityV1 is a host or user certificate authority that
// can check and if it has private key stored as well, sign it too
type CertAuthorityV1 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"`
}
// CombinedMapping is used to specify combined mapping from legacy property Roles
// and new property RoleMap
func (ca *CertAuthorityV1) CombinedMapping() RoleMap {
return []RoleMapping{}
}
// GetRoleMap returns role map property
func (ca *CertAuthorityV1) GetRoleMap() RoleMap {
return nil
}
// SetRoleMap sets role map
func (c *CertAuthorityV1) SetRoleMap(m RoleMap) {
}
// V1 returns V1 version of the resource
func (c *CertAuthorityV1) V1() *CertAuthorityV1 {
return c
}
// V2 returns V2 version of the resource
func (c *CertAuthorityV1) V2() *CertAuthorityV2 {
return &CertAuthorityV2{
Kind: KindCertAuthority,
Version: V2,
Metadata: Metadata{
Name: c.DomainName,
Namespace: defaults.Namespace,
},
Spec: CertAuthoritySpecV2{
Type: c.Type,
ClusterName: c.DomainName,
CheckingKeys: c.CheckingKeys,
SigningKeys: c.SigningKeys,
},
}
}
// String returns human readable version of the CertAuthorityV1.
func (c *CertAuthorityV1) String() string {
return fmt.Sprintf("CA(name=%v, type=%v)", c.DomainName, c.Type)
}
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
UnmarshalCertAuthority(bytes []byte, opts ...MarshalOption) (CertAuthority, error)
// MarshalCertAuthority to binary representation
MarshalCertAuthority(c CertAuthority, opts ...MarshalOption) ([]byte, error)
// GenerateCertAuthority is used to generate new cert authority
// based on standard teleport one and is used to add custom
// parameters and extend it in extensions of teleport
GenerateCertAuthority(CertAuthority) (CertAuthority, error)
}
// GetCertAuthoritySchema returns JSON Schema for cert authorities
func GetCertAuthoritySchema() string {
return fmt.Sprintf(V2SchemaTemplate, MetadataSchema, fmt.Sprintf(CertAuthoritySpecV2Schema, RotationSchema, RoleMapSchema), DefaultDefinitions)
}
type TeleportCertAuthorityMarshaler struct{}
// GenerateCertAuthority is used to generate new cert authority
// based on standard teleport one and is used to add custom
// parameters and extend it in extensions of teleport
func (*TeleportCertAuthorityMarshaler) GenerateCertAuthority(ca CertAuthority) (CertAuthority, error) {
return ca, nil
}
// UnmarshalCertAuthority unmarshals cert authority from JSON
func (*TeleportCertAuthorityMarshaler) UnmarshalCertAuthority(bytes []byte, opts ...MarshalOption) (CertAuthority, error) {
cfg, err := collectOptions(opts)
if err != nil {
return nil, trace.Wrap(err)
}
var h ResourceHeader
err = utils.FastUnmarshal(bytes, &h)
if err != nil {
return nil, trace.Wrap(err)
}
switch h.Version {
case "":
var ca CertAuthorityV1
err := json.Unmarshal(bytes, &ca)
if err != nil {
return nil, trace.Wrap(err)
}
return ca.V2(), nil
case V2:
var ca CertAuthorityV2
if cfg.SkipValidation {
if err := utils.FastUnmarshal(bytes, &ca); err != nil {
return nil, trace.BadParameter(err.Error())
}
} else {
if err := utils.UnmarshalWithSchema(GetCertAuthoritySchema(), &ca, bytes); err != nil {
return nil, trace.BadParameter(err.Error())
}
}
if err := ca.CheckAndSetDefaults(); err != nil {
return nil, trace.Wrap(err)
}
if cfg.ID != 0 {
ca.SetResourceID(cfg.ID)
}
return &ca, nil
}
return nil, trace.BadParameter("cert authority resource version %v is not supported", h.Version)
}
// MarshalCertAuthority marshalls cert authority into JSON
func (*TeleportCertAuthorityMarshaler) MarshalCertAuthority(ca CertAuthority, opts ...MarshalOption) ([]byte, error) {
cfg, err := collectOptions(opts)
if err != nil {
return nil, trace.Wrap(err)
}
type cav1 interface {
V1() *CertAuthorityV1
}
type cav2 interface {
V2() *CertAuthorityV2
}
version := cfg.GetVersion()
switch version {
case V1:
v, ok := ca.(cav1)
if !ok {
return nil, trace.BadParameter("don't know how to marshal %v", V1)
}
return utils.FastMarshal(v.V1())
case V2:
v, ok := ca.(cav2)
if !ok {
return nil, trace.BadParameter("don't know how to marshal %v", V2)
}
v2 := v.V2()
if !cfg.PreserveResourceID {
// avoid modifying the original object
// to prevent unexpected data races
copy := *v2
copy.SetResourceID(0)
v2 = &copy
}
return utils.FastMarshal(v2)
default:
return nil, trace.BadParameter("version %v is not supported", version)
}
}