2015-10-31 18:56:49 +00:00
|
|
|
/*
|
|
|
|
Copyright 2015 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.
|
|
|
|
*/
|
2016-03-10 03:39:15 +00:00
|
|
|
|
2016-03-10 17:41:01 +00:00
|
|
|
// Package auth implements certificate signing authority and access control server
|
2015-03-02 20:11:23 +00:00
|
|
|
// Authority server is composed of several parts:
|
|
|
|
//
|
|
|
|
// * Authority server itself that implements signing and acl logic
|
|
|
|
// * HTTP server wrapper for authority server
|
|
|
|
// * HTTP client wrapper
|
|
|
|
//
|
|
|
|
package auth
|
|
|
|
|
|
|
|
import (
|
2016-02-13 23:36:22 +00:00
|
|
|
"fmt"
|
2016-04-03 05:20:51 +00:00
|
|
|
"net/url"
|
2016-02-13 23:36:22 +00:00
|
|
|
"os"
|
2016-04-03 05:20:51 +00:00
|
|
|
"sync"
|
2015-03-02 20:11:23 +00:00
|
|
|
"time"
|
|
|
|
|
2016-02-18 22:55:39 +00:00
|
|
|
"github.com/gravitational/teleport"
|
2016-03-10 17:41:01 +00:00
|
|
|
"github.com/gravitational/teleport/lib/backend"
|
2016-04-03 05:20:51 +00:00
|
|
|
"github.com/gravitational/teleport/lib/defaults"
|
2015-10-05 17:36:55 +00:00
|
|
|
"github.com/gravitational/teleport/lib/services"
|
2016-04-05 00:09:00 +00:00
|
|
|
"github.com/gravitational/teleport/lib/services/local"
|
2016-02-13 23:36:22 +00:00
|
|
|
"github.com/gravitational/teleport/lib/utils"
|
|
|
|
|
|
|
|
log "github.com/Sirupsen/logrus"
|
2016-04-03 05:20:51 +00:00
|
|
|
"github.com/coreos/go-oidc/oauth2"
|
|
|
|
"github.com/coreos/go-oidc/oidc"
|
2016-02-18 22:55:39 +00:00
|
|
|
"github.com/gravitational/trace"
|
2016-02-24 01:26:23 +00:00
|
|
|
"github.com/jonboulle/clockwork"
|
2016-10-14 06:51:16 +00:00
|
|
|
|
|
|
|
"github.com/tstranex/u2f"
|
2015-03-02 20:11:23 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Authority implements minimal key-management facility for generating OpenSSH
|
|
|
|
//compatible public/private key pairs and OpenSSH certificates
|
|
|
|
type Authority interface {
|
2016-02-16 17:36:02 +00:00
|
|
|
// GenerateKeyPair generates new keypair
|
2015-03-19 04:13:56 +00:00
|
|
|
GenerateKeyPair(passphrase string) (privKey []byte, pubKey []byte, err error)
|
2016-02-16 17:36:02 +00:00
|
|
|
|
|
|
|
// GetNewKeyPairFromPool returns new keypair from pre-generated in memory pool
|
2016-01-29 22:44:43 +00:00
|
|
|
GetNewKeyPairFromPool() (privKey []byte, pubKey []byte, err error)
|
2015-03-02 20:11:23 +00:00
|
|
|
|
2015-05-12 00:04:22 +00:00
|
|
|
// GenerateHostCert generates host certificate, it takes pkey as a signing
|
|
|
|
// private key (host certificate authority)
|
2016-05-11 03:27:18 +00:00
|
|
|
GenerateHostCert(pkey, key []byte, hostID, authDomain string, roles teleport.Roles, ttl time.Duration) ([]byte, error)
|
2015-03-02 20:11:23 +00:00
|
|
|
|
2016-05-11 03:27:18 +00:00
|
|
|
// GenerateUserCert generates user certificate, it takes pkey as a signing
|
2015-05-12 00:04:22 +00:00
|
|
|
// private key (user certificate authority)
|
2016-03-10 03:39:15 +00:00
|
|
|
GenerateUserCert(pkey, key []byte, teleportUsername string, allowedLogins []string, ttl time.Duration) ([]byte, error)
|
2015-03-02 20:11:23 +00:00
|
|
|
}
|
|
|
|
|
2016-02-24 01:26:23 +00:00
|
|
|
// Session is a web session context, stores temporary key-value pair and session id
|
2015-03-19 04:13:56 +00:00
|
|
|
type Session struct {
|
2016-02-24 01:26:23 +00:00
|
|
|
// ID is a session ID
|
|
|
|
ID string `json:"id"`
|
2016-04-05 16:58:16 +00:00
|
|
|
// Username is a user this session belongs to
|
|
|
|
Username string `json:"username"`
|
2016-04-04 18:58:52 +00:00
|
|
|
// ExpiresAt is an optional expiry time, if set
|
|
|
|
// that means this web session and all derived web sessions
|
|
|
|
// can not continue after this time, used in OIDC use case
|
|
|
|
// when expiry is set by external identity provider, so user
|
|
|
|
// has to relogin (or later on we'd need to refresh the token)
|
|
|
|
ExpiresAt time.Time `json:"expires_at"`
|
2016-02-24 01:26:23 +00:00
|
|
|
// WS is a private keypair used for signing requests
|
|
|
|
WS services.WebSession `json:"web"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// AuthServerOption allows setting options as functional arguments to AuthServer
|
|
|
|
type AuthServerOption func(*AuthServer)
|
|
|
|
|
2016-03-06 00:47:03 +00:00
|
|
|
// NewAuthServer creates and configures a new AuthServer instance
|
|
|
|
func NewAuthServer(cfg *InitConfig, opts ...AuthServerOption) *AuthServer {
|
2016-04-05 00:26:15 +00:00
|
|
|
if cfg.Trust == nil {
|
|
|
|
cfg.Trust = local.NewCAService(cfg.Backend)
|
|
|
|
}
|
|
|
|
if cfg.Lock == nil {
|
|
|
|
cfg.Lock = local.NewLockService(cfg.Backend)
|
|
|
|
}
|
|
|
|
if cfg.Presence == nil {
|
|
|
|
cfg.Presence = local.NewPresenceService(cfg.Backend)
|
|
|
|
}
|
|
|
|
if cfg.Provisioner == nil {
|
|
|
|
cfg.Provisioner = local.NewProvisioningService(cfg.Backend)
|
|
|
|
}
|
|
|
|
if cfg.Identity == nil {
|
2016-05-30 22:50:48 +00:00
|
|
|
cfg.Identity = local.NewIdentityService(cfg.Backend,
|
|
|
|
defaults.MaxLoginAttempts, defaults.AccountLockInterval)
|
2016-04-05 00:26:15 +00:00
|
|
|
}
|
2016-03-06 00:47:03 +00:00
|
|
|
as := AuthServer{
|
2016-04-05 00:09:00 +00:00
|
|
|
bk: cfg.Backend,
|
|
|
|
Authority: cfg.Authority,
|
2016-04-05 00:26:15 +00:00
|
|
|
Trust: cfg.Trust,
|
|
|
|
Lock: cfg.Lock,
|
|
|
|
Presence: cfg.Presence,
|
|
|
|
Provisioner: cfg.Provisioner,
|
|
|
|
Identity: cfg.Identity,
|
2016-04-05 00:09:00 +00:00
|
|
|
DomainName: cfg.DomainName,
|
|
|
|
AuthServiceName: cfg.AuthServiceName,
|
|
|
|
oidcClients: make(map[string]*oidc.Client),
|
2016-05-12 07:44:25 +00:00
|
|
|
StaticTokens: cfg.StaticTokens,
|
2016-10-14 06:51:16 +00:00
|
|
|
U2fAppId: cfg.U2fAppId,
|
2016-10-17 04:03:09 +00:00
|
|
|
U2fTrustedFacets:cfg.U2fTrustedFacets,
|
2016-03-06 00:47:03 +00:00
|
|
|
}
|
2016-02-24 01:26:23 +00:00
|
|
|
for _, o := range opts {
|
|
|
|
o(&as)
|
|
|
|
}
|
|
|
|
if as.clock == nil {
|
|
|
|
as.clock = clockwork.NewRealClock()
|
|
|
|
}
|
2015-08-25 17:54:16 +00:00
|
|
|
return &as
|
2015-03-02 20:11:23 +00:00
|
|
|
}
|
|
|
|
|
2016-03-06 00:47:03 +00:00
|
|
|
// AuthServer keeps the cluster together. It acts as a certificate authority (CA) for
|
|
|
|
// a cluster and:
|
|
|
|
// - generates the keypair for the node it's running on
|
|
|
|
// - invites other SSH nodes to a cluster, by issuing invite tokens
|
|
|
|
// - adds other SSH nodes to a cluster, by checking their token and signing their keys
|
|
|
|
// - same for users and their sessions
|
|
|
|
// - checks public keys to see if they're signed by it (can be trusted or not)
|
2015-03-02 20:11:23 +00:00
|
|
|
type AuthServer struct {
|
2016-04-05 00:09:00 +00:00
|
|
|
lock sync.Mutex
|
2016-04-03 05:20:51 +00:00
|
|
|
oidcClients map[string]*oidc.Client
|
|
|
|
clock clockwork.Clock
|
|
|
|
bk backend.Backend
|
2015-08-27 14:39:33 +00:00
|
|
|
Authority
|
2016-03-06 00:47:03 +00:00
|
|
|
|
|
|
|
// DomainName stores the FQDN of the signing CA (its certificate will have this
|
|
|
|
// name embedded). It is usually set to the GUID of the host the Auth service runs on
|
2016-03-05 00:27:52 +00:00
|
|
|
DomainName string
|
2015-08-25 17:54:16 +00:00
|
|
|
|
2016-03-06 00:47:03 +00:00
|
|
|
// AuthServiceName is a human-readable name of this CA. If several Auth services are running
|
|
|
|
// (managing multiple teleport clusters) this field is used to tell them apart in UIs
|
|
|
|
// It usually defaults to the hostname of the machine the Auth service runs on.
|
|
|
|
AuthServiceName string
|
|
|
|
|
2016-05-12 07:44:25 +00:00
|
|
|
// StaticTokens are pre-defined host provisioning tokens supplied via config file for
|
|
|
|
// environments where paranoid security is not needed
|
2016-05-17 03:10:59 +00:00
|
|
|
StaticTokens []services.ProvisionToken
|
2016-05-12 07:44:25 +00:00
|
|
|
|
2016-10-14 06:51:16 +00:00
|
|
|
U2fAppId string
|
2016-10-17 04:03:09 +00:00
|
|
|
U2fTrustedFacets []string
|
2016-10-14 06:51:16 +00:00
|
|
|
|
2016-04-05 00:09:00 +00:00
|
|
|
services.Trust
|
|
|
|
services.Lock
|
|
|
|
services.Presence
|
|
|
|
services.Provisioner
|
|
|
|
services.Identity
|
2015-03-02 20:11:23 +00:00
|
|
|
}
|
|
|
|
|
2016-04-03 05:59:09 +00:00
|
|
|
func (a *AuthServer) Close() error {
|
|
|
|
if a.bk != nil {
|
|
|
|
return trace.Wrap(a.bk.Close())
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-10-10 00:27:56 +00:00
|
|
|
// GetDomainName returns the domain name that identifies this authority server.
|
|
|
|
// Also known as "cluster name"
|
|
|
|
func (a *AuthServer) GetDomainName() (string, error) {
|
2016-03-05 00:27:52 +00:00
|
|
|
return a.DomainName, nil
|
2015-03-02 20:11:23 +00:00
|
|
|
}
|
|
|
|
|
2016-11-28 08:21:37 +00:00
|
|
|
func (a *AuthServer) GetU2fAppId() (string, error) {
|
|
|
|
return a.U2fAppId, nil
|
|
|
|
}
|
|
|
|
|
2015-05-12 00:04:22 +00:00
|
|
|
// GenerateHostCert generates host certificate, it takes pkey as a signing
|
|
|
|
// private key (host certificate authority)
|
2016-05-11 03:27:18 +00:00
|
|
|
func (s *AuthServer) GenerateHostCert(key []byte, hostID, authDomain string, roles teleport.Roles, ttl time.Duration) ([]byte, error) {
|
2016-04-05 00:09:00 +00:00
|
|
|
ca, err := s.Trust.GetCertAuthority(services.CertAuthID{
|
2016-02-16 17:36:02 +00:00
|
|
|
Type: services.HostCA,
|
2016-03-05 00:27:52 +00:00
|
|
|
DomainName: s.DomainName,
|
2016-02-16 17:36:02 +00:00
|
|
|
}, true)
|
2015-03-02 20:11:23 +00:00
|
|
|
if err != nil {
|
2016-05-12 07:44:25 +00:00
|
|
|
return nil, trace.Errorf("failed to load host CA for '%s': %v", s.DomainName, err)
|
2016-02-16 17:36:02 +00:00
|
|
|
}
|
|
|
|
privateKey, err := ca.FirstSigningKey()
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
2015-03-02 20:11:23 +00:00
|
|
|
}
|
2016-05-11 03:27:18 +00:00
|
|
|
return s.Authority.GenerateHostCert(privateKey, key, hostID, authDomain, roles, ttl)
|
2015-03-02 20:11:23 +00:00
|
|
|
}
|
|
|
|
|
2016-02-16 17:36:02 +00:00
|
|
|
// GenerateUserCert generates user certificate, it takes pkey as a signing
|
2015-05-12 00:04:22 +00:00
|
|
|
// private key (user certificate authority)
|
|
|
|
func (s *AuthServer) GenerateUserCert(
|
2016-02-19 02:07:43 +00:00
|
|
|
key []byte, username string, ttl time.Duration) ([]byte, error) {
|
2015-05-12 00:04:22 +00:00
|
|
|
|
2016-04-05 00:09:00 +00:00
|
|
|
ca, err := s.Trust.GetCertAuthority(services.CertAuthID{
|
2016-02-16 17:36:02 +00:00
|
|
|
Type: services.UserCA,
|
2016-03-05 00:27:52 +00:00
|
|
|
DomainName: s.DomainName,
|
2016-02-16 17:36:02 +00:00
|
|
|
}, true)
|
2015-03-02 20:11:23 +00:00
|
|
|
if err != nil {
|
2016-02-16 17:36:02 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-03-02 20:11:23 +00:00
|
|
|
}
|
2016-02-16 17:36:02 +00:00
|
|
|
privateKey, err := ca.FirstSigningKey()
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2016-03-10 03:39:15 +00:00
|
|
|
user, err := s.GetUser(username)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2016-04-05 01:58:36 +00:00
|
|
|
return s.Authority.GenerateUserCert(privateKey, key, username, user.GetAllowedLogins(), ttl)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
2016-02-24 01:26:23 +00:00
|
|
|
func (s *AuthServer) SignIn(user string, password []byte) (*Session, error) {
|
2015-10-23 20:34:09 +00:00
|
|
|
if err := s.CheckPasswordWOToken(user, password); err != nil {
|
2016-02-24 01:26:23 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
2016-10-16 21:03:43 +00:00
|
|
|
return s.PreAuthenticatedSignIn(user)
|
|
|
|
}
|
|
|
|
|
|
|
|
// PreAuthenticatedSignIn is for 2-way authentication methods like U2F where the password is
|
|
|
|
// already checked before issueing the second factor challenge
|
|
|
|
func (s *AuthServer) PreAuthenticatedSignIn(user string) (*Session, error) {
|
2015-03-19 04:13:56 +00:00
|
|
|
sess, err := s.NewWebSession(user)
|
|
|
|
if err != nil {
|
2016-02-24 01:26:23 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
2015-10-27 23:30:16 +00:00
|
|
|
if err := s.UpsertWebSession(user, sess, WebSessionTTL); err != nil {
|
2016-02-24 01:26:23 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
2016-02-24 01:26:23 +00:00
|
|
|
sess.WS.Priv = nil
|
|
|
|
return sess, nil
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
2016-10-14 06:51:16 +00:00
|
|
|
func (s *AuthServer) U2fSignRequest(user string, password []byte) (*u2f.SignRequest, error) {
|
|
|
|
if err := s.CheckPasswordWOToken(user, password); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
u2fReg, err := s.GetU2fRegistration(user)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2016-10-17 04:03:09 +00:00
|
|
|
challenge, err := u2f.NewChallenge(s.U2fAppId, s.U2fTrustedFacets)
|
2016-10-14 06:51:16 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2016-10-16 21:03:43 +00:00
|
|
|
err = s.UpsertU2fSignChallenge(user, challenge)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2016-10-14 06:51:16 +00:00
|
|
|
|
2016-10-16 21:03:43 +00:00
|
|
|
u2fSignReq := challenge.SignRequest(*u2fReg)
|
2016-10-14 06:51:16 +00:00
|
|
|
|
|
|
|
return u2fSignReq, nil
|
|
|
|
}
|
|
|
|
|
2016-10-16 21:03:43 +00:00
|
|
|
func (s *AuthServer) CheckU2fSignResponse(user string, u2fSignResponse *u2f.SignResponse) (error) {
|
|
|
|
reg, err := s.GetU2fRegistration(user)
|
|
|
|
if err != nil {
|
|
|
|
return trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
counter, err := s.GetU2fRegistrationCounter(user)
|
|
|
|
if err != nil {
|
|
|
|
return trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
challenge, err := s.GetU2fSignChallenge(user)
|
|
|
|
if err != nil {
|
|
|
|
return trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
newCounter, err := reg.Authenticate(*u2fSignResponse, *challenge, counter)
|
|
|
|
if err != nil {
|
|
|
|
return trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = s.UpsertU2fRegistrationCounter(user, newCounter)
|
|
|
|
if err != nil {
|
|
|
|
return trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-10 20:29:32 +00:00
|
|
|
// ExtendWebSession creates a new web session for a user based on a valid previous sessionID,
|
2016-02-26 22:57:51 +00:00
|
|
|
// method is used to renew the web session for a user
|
2016-04-10 20:29:32 +00:00
|
|
|
func (s *AuthServer) ExtendWebSession(user string, prevSessionID string) (*Session, error) {
|
2016-04-04 18:58:52 +00:00
|
|
|
prevSession, err := s.GetWebSession(user, prevSessionID)
|
2016-02-26 22:57:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2016-04-04 18:58:52 +00:00
|
|
|
|
|
|
|
// consider absolute expiry time that may be set for this session
|
|
|
|
// by some external identity serivce, so we can not renew this session
|
|
|
|
// any more without extra logic for renewal with external OIDC provider
|
|
|
|
expiresAt := prevSession.ExpiresAt
|
|
|
|
if !expiresAt.IsZero() && expiresAt.Before(s.clock.Now().UTC()) {
|
2016-04-12 17:54:24 +00:00
|
|
|
return nil, trace.NotFound("web session has expired")
|
2016-04-04 18:58:52 +00:00
|
|
|
}
|
|
|
|
|
2016-02-26 22:57:51 +00:00
|
|
|
sess, err := s.NewWebSession(user)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2016-04-04 18:58:52 +00:00
|
|
|
sessionTTL := minTTL(toTTL(s.clock, expiresAt), WebSessionTTL)
|
|
|
|
sess.ExpiresAt = expiresAt
|
|
|
|
if err := s.UpsertWebSession(user, sess, sessionTTL); err != nil {
|
2016-02-26 22:57:51 +00:00
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
sess.WS.Priv = nil
|
|
|
|
return sess, nil
|
|
|
|
}
|
|
|
|
|
2016-04-10 20:29:32 +00:00
|
|
|
// CreateWebSession creates a new web session for user without any
|
|
|
|
// checks, is used by admins
|
|
|
|
func (s *AuthServer) CreateWebSession(user string) (*Session, error) {
|
|
|
|
sess, err := s.NewWebSession(user)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
if err := s.UpsertWebSession(user, sess, WebSessionTTL); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
sess.WS.Priv = nil
|
|
|
|
return sess, nil
|
|
|
|
}
|
|
|
|
|
2016-05-10 05:28:44 +00:00
|
|
|
func (s *AuthServer) GenerateToken(roles teleport.Roles, ttl time.Duration) (string, error) {
|
|
|
|
for _, role := range roles {
|
|
|
|
if err := role.Check(); err != nil {
|
|
|
|
return "", trace.Wrap(err)
|
|
|
|
}
|
2016-02-19 02:07:43 +00:00
|
|
|
}
|
2016-02-26 22:57:51 +00:00
|
|
|
token, err := utils.CryptoRandomHex(TokenLenBytes)
|
2016-02-18 02:32:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", trace.Wrap(err)
|
2015-05-07 03:10:44 +00:00
|
|
|
}
|
2016-05-10 05:28:44 +00:00
|
|
|
if err := s.Provisioner.UpsertToken(token, roles, ttl); err != nil {
|
2015-05-07 03:10:44 +00:00
|
|
|
return "", err
|
|
|
|
}
|
2016-05-10 05:28:44 +00:00
|
|
|
return token, nil
|
2015-05-07 03:10:44 +00:00
|
|
|
}
|
|
|
|
|
2016-03-12 04:09:40 +00:00
|
|
|
// GenerateServerKeys generates private key and certificate signed
|
|
|
|
// by the host certificate authority, listing the role of this server
|
2016-05-11 03:27:18 +00:00
|
|
|
func (s *AuthServer) GenerateServerKeys(hostID string, roles teleport.Roles) (*PackedKeys, error) {
|
2016-03-12 04:09:40 +00:00
|
|
|
k, pub, err := s.GenerateKeyPair("")
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
// we always append authority's domain to resulting node name,
|
|
|
|
// that's how we make sure that nodes are uniquely identified/found
|
|
|
|
// in cases when we have multiple environments/organizations
|
|
|
|
fqdn := fmt.Sprintf("%s.%s", hostID, s.DomainName)
|
2016-05-11 03:27:18 +00:00
|
|
|
c, err := s.GenerateHostCert(pub, fqdn, s.DomainName, roles, 0)
|
2016-03-12 04:09:40 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Warningf("[AUTH] Node `%v` cannot join: cert generation error. %v", hostID, err)
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &PackedKeys{
|
|
|
|
Key: k,
|
|
|
|
Cert: c,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2016-05-12 23:40:14 +00:00
|
|
|
// ValidteToken takes a provisioning token value and finds if it's valid. Returns
|
|
|
|
// a list of roles this token allows its owner to assume, or an error if the token
|
|
|
|
// cannot be found
|
|
|
|
func (s *AuthServer) ValidateToken(token string) (roles teleport.Roles, e error) {
|
|
|
|
// look at static tokesn first:
|
|
|
|
for _, st := range s.StaticTokens {
|
2016-05-17 03:10:59 +00:00
|
|
|
if st.Token == token {
|
2016-05-12 23:40:14 +00:00
|
|
|
return st.Roles, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// look at the tokens in the token storage
|
|
|
|
tok, err := s.Provisioner.GetToken(token)
|
|
|
|
if err != nil {
|
2016-10-09 23:00:20 +00:00
|
|
|
log.Info(err)
|
2016-05-13 01:51:12 +00:00
|
|
|
return nil, trace.Errorf("token not recognized")
|
2016-05-12 23:40:14 +00:00
|
|
|
}
|
|
|
|
return tok.Roles, nil
|
|
|
|
}
|
|
|
|
|
2016-05-13 01:51:12 +00:00
|
|
|
// enforceTokenTTL deletes the given token if it's TTL is over. Returns 'false'
|
|
|
|
// if this token cannot be used
|
|
|
|
func (s *AuthServer) checkTokenTTL(token string) bool {
|
|
|
|
// look at the tokens in the token storage
|
|
|
|
tok, err := s.Provisioner.GetToken(token)
|
|
|
|
if err != nil {
|
|
|
|
log.Warn(err)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
now := s.clock.Now().UTC()
|
|
|
|
if tok.Expires.Before(now) {
|
|
|
|
if err = s.DeleteToken(token); err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2016-05-12 07:44:25 +00:00
|
|
|
// RegisterUsingToken adds a new node to the Teleport cluster using previously issued token.
|
|
|
|
// A node must also request a specific role (and the role must match one of the roles
|
|
|
|
// the token was generated for).
|
|
|
|
//
|
|
|
|
// If a token was generated with a TTL, it gets enforced (can't register new nodes after TTL expires)
|
|
|
|
// If a token was generated with a TTL=0, it means it's a single-use token and it gets destroyed
|
|
|
|
// after a successful registration.
|
2016-05-10 05:28:44 +00:00
|
|
|
func (s *AuthServer) RegisterUsingToken(token, hostID string, role teleport.Role) (*PackedKeys, error) {
|
2016-05-12 07:44:25 +00:00
|
|
|
log.Infof("[AUTH] Node `%v` is trying to join as %v", hostID, role)
|
2016-03-05 01:48:09 +00:00
|
|
|
if hostID == "" {
|
2016-04-12 17:54:24 +00:00
|
|
|
return nil, trace.BadParameter("HostID cannot be empty")
|
2016-03-05 01:48:09 +00:00
|
|
|
}
|
2016-02-19 02:07:43 +00:00
|
|
|
if err := role.Check(); err != nil {
|
2016-03-12 04:09:40 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2016-02-19 02:07:43 +00:00
|
|
|
}
|
2016-05-13 01:51:12 +00:00
|
|
|
// make sure the token is valid:
|
|
|
|
roles, err := s.ValidateToken(token)
|
|
|
|
if err != nil {
|
|
|
|
msg := fmt.Sprintf("`%v` cannot join the cluster as %s. Token error: %v", hostID, role, err)
|
|
|
|
log.Warnf("[AUTH] %s", msg)
|
|
|
|
return nil, trace.AccessDenied(msg)
|
2015-11-13 01:32:45 +00:00
|
|
|
}
|
2016-05-13 01:51:12 +00:00
|
|
|
// make sure the caller is requested wthe role allowed by the token:
|
|
|
|
if !roles.Include(role) {
|
|
|
|
msg := fmt.Sprintf("'%v' cannot join the cluster, the token does not allow '%s' role", hostID, role)
|
|
|
|
log.Warningf("[AUTH] %s", msg)
|
|
|
|
return nil, trace.BadParameter(msg)
|
2016-05-12 07:44:25 +00:00
|
|
|
}
|
2016-05-13 01:51:12 +00:00
|
|
|
if !s.checkTokenTTL(token) {
|
|
|
|
return nil, trace.AccessDenied("'%v' cannot join the cluster. The token has expired", hostID)
|
|
|
|
}
|
|
|
|
// generate & return the node cert:
|
2016-05-11 03:27:18 +00:00
|
|
|
keys, err := s.GenerateServerKeys(hostID, teleport.Roles{role})
|
2015-11-13 01:32:45 +00:00
|
|
|
if err != nil {
|
2016-03-12 04:09:40 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-11-13 01:32:45 +00:00
|
|
|
}
|
2016-03-05 01:48:09 +00:00
|
|
|
utils.Consolef(os.Stdout, "[AUTH] Node `%v` joined the cluster", hostID)
|
2015-11-13 01:32:45 +00:00
|
|
|
return keys, nil
|
|
|
|
}
|
|
|
|
|
2016-05-10 05:28:44 +00:00
|
|
|
func (s *AuthServer) RegisterNewAuthServer(token string) error {
|
2016-04-05 00:09:00 +00:00
|
|
|
tok, err := s.Provisioner.GetToken(token)
|
2015-11-13 01:32:45 +00:00
|
|
|
if err != nil {
|
2016-03-10 17:41:01 +00:00
|
|
|
return trace.Wrap(err)
|
2015-05-07 03:10:44 +00:00
|
|
|
}
|
2016-05-10 05:28:44 +00:00
|
|
|
if !tok.Roles.Include(teleport.RoleAuth) {
|
2016-04-12 17:54:24 +00:00
|
|
|
return trace.AccessDenied("role does not match")
|
2015-11-13 01:32:45 +00:00
|
|
|
}
|
2016-05-10 05:28:44 +00:00
|
|
|
if err := s.DeleteToken(token); err != nil {
|
2016-03-10 17:41:01 +00:00
|
|
|
return trace.Wrap(err)
|
2015-11-13 01:32:45 +00:00
|
|
|
}
|
2016-03-10 17:41:01 +00:00
|
|
|
return nil
|
2015-05-07 03:10:44 +00:00
|
|
|
}
|
|
|
|
|
2016-05-17 03:10:59 +00:00
|
|
|
func (s *AuthServer) DeleteToken(token string) (err error) {
|
2016-05-17 03:32:17 +00:00
|
|
|
// is this a static token?
|
|
|
|
for _, st := range s.StaticTokens {
|
|
|
|
if st.Token == token {
|
|
|
|
return trace.BadParameter("token %s is statically configured and cannot be removed", token)
|
|
|
|
}
|
|
|
|
}
|
2016-05-17 03:10:59 +00:00
|
|
|
// delete user token:
|
|
|
|
if err = s.Identity.DeleteSignupToken(token); err == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
// delete node token:
|
|
|
|
if err = s.Provisioner.DeleteToken(token); err == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetTokens returns all tokens (machine provisioning ones and user invitation tokens). Machine
|
|
|
|
// tokens usually have "node roles", like auth,proxy,node and user invitation tokens have 'signup' role
|
|
|
|
func (s *AuthServer) GetTokens() (tokens []services.ProvisionToken, err error) {
|
|
|
|
// get node tokens:
|
|
|
|
tokens, err = s.Provisioner.GetTokens()
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
// get static tokens:
|
|
|
|
tokens = append(tokens, s.StaticTokens...)
|
|
|
|
// get user tokens:
|
|
|
|
userTokens, err := s.Identity.GetSignupTokens()
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
// convert user tokens to machine tokens:
|
|
|
|
for _, t := range userTokens {
|
2016-05-17 03:36:03 +00:00
|
|
|
roles := teleport.Roles{teleport.RoleSignup}
|
2016-05-17 03:10:59 +00:00
|
|
|
tokens = append(tokens, services.ProvisionToken{
|
|
|
|
Token: t.Token,
|
|
|
|
Expires: t.Expires,
|
2016-05-17 03:36:03 +00:00
|
|
|
Roles: roles,
|
2016-05-17 03:10:59 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
return tokens, nil
|
2015-05-07 03:10:44 +00:00
|
|
|
}
|
|
|
|
|
2016-02-25 19:30:59 +00:00
|
|
|
func (s *AuthServer) NewWebSession(userName string) (*Session, error) {
|
2016-05-17 03:10:59 +00:00
|
|
|
token, err := utils.CryptoRandomHex(TokenLenBytes)
|
2016-02-26 22:57:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2016-05-17 03:10:59 +00:00
|
|
|
bearerToken, err := utils.CryptoRandomHex(TokenLenBytes)
|
2015-03-19 04:13:56 +00:00
|
|
|
if err != nil {
|
2016-02-18 02:32:11 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
2016-01-29 22:44:43 +00:00
|
|
|
priv, pub, err := s.GetNewKeyPairFromPool()
|
2015-03-19 04:13:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-04-05 00:09:00 +00:00
|
|
|
ca, err := s.Trust.GetCertAuthority(services.CertAuthID{
|
2016-02-16 17:36:02 +00:00
|
|
|
Type: services.UserCA,
|
2016-03-05 00:27:52 +00:00
|
|
|
DomainName: s.DomainName,
|
2016-02-16 17:36:02 +00:00
|
|
|
}, true)
|
2015-03-19 04:13:56 +00:00
|
|
|
if err != nil {
|
2016-02-16 17:36:02 +00:00
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
privateKey, err := ca.FirstSigningKey()
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
2016-03-10 03:39:15 +00:00
|
|
|
user, err := s.GetUser(userName)
|
2015-03-19 04:13:56 +00:00
|
|
|
if err != nil {
|
2016-03-10 03:39:15 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
2016-04-05 01:58:36 +00:00
|
|
|
cert, err := s.Authority.GenerateUserCert(privateKey, pub, user.GetName(), user.GetAllowedLogins(), WebSessionTTL)
|
2016-02-25 19:30:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2015-03-19 04:13:56 +00:00
|
|
|
sess := &Session{
|
2016-04-05 16:58:16 +00:00
|
|
|
ID: token,
|
|
|
|
Username: user.GetName(),
|
2016-02-26 22:57:51 +00:00
|
|
|
WS: services.WebSession{
|
|
|
|
Priv: priv,
|
|
|
|
Pub: cert,
|
|
|
|
Expires: s.clock.Now().UTC().Add(WebSessionTTL),
|
|
|
|
BearerToken: bearerToken,
|
|
|
|
},
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
|
|
|
return sess, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *AuthServer) UpsertWebSession(user string, sess *Session, ttl time.Duration) error {
|
2016-04-05 00:09:00 +00:00
|
|
|
return s.Identity.UpsertWebSession(user, sess.ID, sess.WS, ttl)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
2016-02-25 19:30:59 +00:00
|
|
|
func (s *AuthServer) GetWebSession(userName string, id string) (*Session, error) {
|
2016-04-05 00:09:00 +00:00
|
|
|
ws, err := s.Identity.GetWebSession(userName, id)
|
2016-02-25 19:30:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
user, err := s.GetUser(userName)
|
2015-03-19 04:13:56 +00:00
|
|
|
if err != nil {
|
2016-02-18 02:32:11 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
|
|
|
return &Session{
|
2016-04-05 16:58:16 +00:00
|
|
|
ID: id,
|
|
|
|
Username: user.GetName(),
|
|
|
|
WS: *ws,
|
2015-03-19 04:13:56 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2016-02-25 19:30:59 +00:00
|
|
|
func (s *AuthServer) GetWebSessionInfo(userName string, id string) (*Session, error) {
|
2016-04-05 00:09:00 +00:00
|
|
|
sess, err := s.Identity.GetWebSession(userName, id)
|
2016-02-25 19:30:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
user, err := s.GetUser(userName)
|
2016-02-21 22:39:32 +00:00
|
|
|
if err != nil {
|
2016-02-24 01:26:23 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2016-02-21 22:39:32 +00:00
|
|
|
}
|
2016-02-24 01:26:23 +00:00
|
|
|
sess.Priv = nil
|
|
|
|
return &Session{
|
2016-04-05 16:58:16 +00:00
|
|
|
ID: id,
|
|
|
|
Username: user.GetName(),
|
|
|
|
WS: *sess,
|
2016-02-24 01:26:23 +00:00
|
|
|
}, nil
|
2016-02-21 22:39:32 +00:00
|
|
|
}
|
|
|
|
|
2016-02-18 02:32:11 +00:00
|
|
|
func (s *AuthServer) DeleteWebSession(user string, id string) error {
|
2016-04-05 00:09:00 +00:00
|
|
|
return trace.Wrap(s.Identity.DeleteWebSession(user, id))
|
2016-04-03 05:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *AuthServer) getOIDCClient(conn *services.OIDCConnector) (*oidc.Client, error) {
|
2016-04-05 00:09:00 +00:00
|
|
|
s.lock.Lock()
|
|
|
|
defer s.lock.Unlock()
|
2016-04-03 05:20:51 +00:00
|
|
|
|
|
|
|
client, ok := s.oidcClients[conn.ID]
|
|
|
|
if ok {
|
|
|
|
return client, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
config := oidc.ClientConfig{
|
|
|
|
RedirectURL: conn.RedirectURL,
|
|
|
|
Credentials: oidc.ClientCredentials{
|
|
|
|
ID: conn.ClientID,
|
|
|
|
Secret: conn.ClientSecret,
|
|
|
|
},
|
2016-04-19 21:56:01 +00:00
|
|
|
// open id notifies provider that we are using OIDC scopes
|
|
|
|
Scope: []string{"openid", "email"},
|
2016-04-03 05:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
client, err := oidc.NewClient(config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
client.SyncProviderConfig(conn.IssuerURL)
|
|
|
|
|
|
|
|
s.oidcClients[conn.ID] = client
|
|
|
|
|
|
|
|
return client, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *AuthServer) CreateOIDCAuthRequest(req services.OIDCAuthRequest) (*services.OIDCAuthRequest, error) {
|
2016-04-05 00:09:00 +00:00
|
|
|
connector, err := s.Identity.GetOIDCConnector(req.ConnectorID, true)
|
2016-04-03 05:20:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
oidcClient, err := s.getOIDCClient(connector)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2016-05-17 03:10:59 +00:00
|
|
|
token, err := utils.CryptoRandomHex(TokenLenBytes)
|
2016-04-03 05:20:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
req.StateToken = token
|
|
|
|
|
|
|
|
oauthClient, err := oidcClient.OAuthClient()
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2016-04-19 21:56:01 +00:00
|
|
|
// online is OIDC online scope, "select_account" forces user to always select account
|
|
|
|
redirectURL := oauthClient.AuthCodeURL(req.StateToken, "online", "select_account")
|
2016-04-03 05:20:51 +00:00
|
|
|
req.RedirectURL = redirectURL
|
|
|
|
|
2016-04-05 00:09:00 +00:00
|
|
|
err = s.Identity.CreateOIDCAuthRequest(req, defaults.OIDCAuthRequestTTL)
|
2016-04-03 05:20:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
return &req, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// OIDCAuthResponse is returned when auth server validated callback parameters
|
|
|
|
// returned from OIDC provider
|
|
|
|
type OIDCAuthResponse struct {
|
2016-04-05 17:53:08 +00:00
|
|
|
// Username is authenticated teleport username
|
|
|
|
Username string `json:"username"`
|
2016-04-10 22:44:01 +00:00
|
|
|
// Identity contains validated OIDC identity
|
|
|
|
Identity services.OIDCIdentity `json:"identity"`
|
2016-04-03 05:20:51 +00:00
|
|
|
// Web session will be generated by auth server if requested in OIDCAuthRequest
|
|
|
|
Session *Session `json:"session,omitempty"`
|
|
|
|
// Cert will be generated by certificate authority
|
|
|
|
Cert []byte `json:"cert,omitempty"`
|
|
|
|
// Req is original oidc auth request
|
|
|
|
Req services.OIDCAuthRequest `json:"req"`
|
2016-04-03 22:06:50 +00:00
|
|
|
// HostSigners is a list of signing host public keys
|
|
|
|
// trusted by proxy, used in console login
|
|
|
|
HostSigners []services.CertAuthority `json:"host_signers"`
|
2016-04-03 05:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ValidateOIDCAuthCallback is called by the proxy to check OIDC query parameters
|
|
|
|
// returned by OIDC Provider, if everything checks out, auth server
|
|
|
|
// will respond with OIDCAuthResponse, otherwise it will return error
|
2016-04-15 00:50:54 +00:00
|
|
|
func (a *AuthServer) ValidateOIDCAuthCallback(q url.Values) (*OIDCAuthResponse, error) {
|
2016-04-03 05:20:51 +00:00
|
|
|
if error := q.Get("error"); error != "" {
|
2016-04-12 17:54:24 +00:00
|
|
|
return nil, trace.OAuth2(oauth2.ErrorInvalidRequest, error, q)
|
2016-04-03 05:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
code := q.Get("code")
|
|
|
|
if code == "" {
|
2016-04-12 17:54:24 +00:00
|
|
|
return nil, trace.OAuth2(
|
|
|
|
oauth2.ErrorInvalidRequest, "code query param must be set", q)
|
2016-04-03 05:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
stateToken := q.Get("state")
|
|
|
|
if stateToken == "" {
|
2016-04-12 17:54:24 +00:00
|
|
|
return nil, trace.OAuth2(
|
|
|
|
oauth2.ErrorInvalidRequest, "missing state query param", q)
|
2016-04-03 05:20:51 +00:00
|
|
|
}
|
|
|
|
|
2016-04-05 00:09:00 +00:00
|
|
|
req, err := a.Identity.GetOIDCAuthRequest(stateToken)
|
2016-04-03 05:20:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2016-04-05 00:09:00 +00:00
|
|
|
connector, err := a.Identity.GetOIDCConnector(req.ConnectorID, true)
|
2016-04-03 05:20:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
oidcClient, err := a.getOIDCClient(connector)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
tok, err := oidcClient.ExchangeAuthCode(code)
|
|
|
|
if err != nil {
|
2016-04-12 17:54:24 +00:00
|
|
|
return nil, trace.OAuth2(
|
2016-04-03 05:20:51 +00:00
|
|
|
oauth2.ErrorUnsupportedResponseType,
|
2016-04-12 17:54:24 +00:00
|
|
|
"unable to verify auth code with issuer", q)
|
2016-04-03 05:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
claims, err := tok.Claims()
|
|
|
|
if err != nil {
|
2016-04-12 17:54:24 +00:00
|
|
|
return nil, trace.OAuth2(
|
|
|
|
oauth2.ErrorUnsupportedResponseType, "unable to construct claims", q)
|
2016-04-03 05:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ident, err := oidc.IdentityFromClaims(claims)
|
|
|
|
if err != nil {
|
2016-04-12 17:54:24 +00:00
|
|
|
return nil, trace.OAuth2(
|
|
|
|
oauth2.ErrorUnsupportedResponseType, "unable to convert claims to identity", q)
|
2016-04-03 05:20:51 +00:00
|
|
|
}
|
|
|
|
|
2016-04-04 18:58:52 +00:00
|
|
|
log.Infof("[IDENTITY] expires at: %v", ident.ExpiresAt)
|
|
|
|
|
2016-04-10 22:44:01 +00:00
|
|
|
response := &OIDCAuthResponse{
|
|
|
|
Identity: services.OIDCIdentity{ConnectorID: connector.ID, Email: ident.Email},
|
|
|
|
Req: *req,
|
|
|
|
}
|
|
|
|
|
2016-04-15 00:50:54 +00:00
|
|
|
if !req.CheckUser {
|
2016-04-10 22:44:01 +00:00
|
|
|
return response, nil
|
|
|
|
}
|
|
|
|
|
2016-04-05 00:09:00 +00:00
|
|
|
user, err := a.Identity.GetUserByOIDCIdentity(services.OIDCIdentity{
|
2016-04-03 05:20:51 +00:00
|
|
|
ConnectorID: req.ConnectorID, Email: ident.Email})
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2016-04-10 22:44:01 +00:00
|
|
|
response.Username = user.GetName()
|
2016-04-03 05:20:51 +00:00
|
|
|
|
|
|
|
if req.CreateWebSession {
|
2016-04-05 01:58:36 +00:00
|
|
|
sess, err := a.NewWebSession(user.GetName())
|
2016-04-03 05:20:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2016-04-04 18:58:52 +00:00
|
|
|
sess.ExpiresAt = ident.ExpiresAt.UTC()
|
|
|
|
sessionTTL := minTTL(toTTL(a.clock, ident.ExpiresAt), WebSessionTTL)
|
2016-04-05 01:58:36 +00:00
|
|
|
if err := a.UpsertWebSession(user.GetName(), sess, sessionTTL); err != nil {
|
2016-04-03 05:20:51 +00:00
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
response.Session = sess
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(req.PublicKey) != 0 {
|
2016-04-04 18:58:52 +00:00
|
|
|
certTTL := minTTL(toTTL(a.clock, ident.ExpiresAt), req.CertTTL)
|
2016-04-05 01:58:36 +00:00
|
|
|
cert, err := a.GenerateUserCert(req.PublicKey, user.GetName(), certTTL)
|
2016-04-03 05:20:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
response.Cert = cert
|
2016-04-03 22:06:50 +00:00
|
|
|
|
|
|
|
authorities, err := a.GetCertAuthorities(services.HostCA, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
for _, authority := range authorities {
|
|
|
|
response.HostSigners = append(response.HostSigners, *authority)
|
|
|
|
}
|
2016-04-03 05:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return response, nil
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2016-02-26 22:57:51 +00:00
|
|
|
// WebSessionTTL specifies standard web session time to live
|
|
|
|
WebSessionTTL = 10 * time.Minute
|
|
|
|
// TokenLenBytes is len in bytes of the invite token
|
|
|
|
TokenLenBytes = 16
|
2015-03-19 04:13:56 +00:00
|
|
|
)
|
2016-04-04 18:58:52 +00:00
|
|
|
|
|
|
|
// minTTL finds min non 0 TTL duration,
|
|
|
|
// if both durations are 0, fails
|
|
|
|
func minTTL(a, b time.Duration) time.Duration {
|
|
|
|
if a == 0 {
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
if b == 0 {
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
if a < b {
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
|
|
|
// toTTL converts expiration time to TTL duration
|
|
|
|
// relative to current time as provided by clock
|
|
|
|
func toTTL(c clockwork.Clock, tm time.Time) time.Duration {
|
|
|
|
now := c.Now().UTC()
|
|
|
|
if tm.IsZero() || tm.Before(now) {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return tm.Sub(now)
|
|
|
|
}
|