mirror of
https://github.com/gravitational/teleport
synced 2024-10-22 10:13:21 +00:00
Merge branch 'roman/anon' of github.com:gravitational/teleport into roman/anon
This commit is contained in:
commit
dd515b4580
|
@ -892,24 +892,15 @@ func (s *APIServer) generateToken(auth ClientI, w http.ResponseWriter, r *http.R
|
|||
return string(token), nil
|
||||
}
|
||||
|
||||
type registerUsingTokenReq struct {
|
||||
HostID string `json:"hostID"`
|
||||
NodeName string `json:"node_name"`
|
||||
Role teleport.Role `json:"role"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
func (s *APIServer) registerUsingToken(auth ClientI, w http.ResponseWriter, r *http.Request, _ httprouter.Params, version string) (interface{}, error) {
|
||||
var req *registerUsingTokenReq
|
||||
var req RegisterUsingTokenRequest
|
||||
if err := httplib.ReadJSON(r, &req); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
keys, err := auth.RegisterUsingToken(req.Token, req.HostID, req.NodeName, req.Role)
|
||||
keys, err := auth.RegisterUsingToken(req)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
|
@ -929,22 +920,13 @@ func (s *APIServer) registerNewAuthServer(auth ClientI, w http.ResponseWriter, r
|
|||
return message("ok"), nil
|
||||
}
|
||||
|
||||
type generateServerKeysReq struct {
|
||||
// HostID is unique ID of the host
|
||||
HostID string `json:"host_id"`
|
||||
// NodeName is user friendly host name
|
||||
NodeName string `json:"node_name"`
|
||||
// Roles is a list of roles assigned to node
|
||||
Roles teleport.Roles `json:"roles"`
|
||||
}
|
||||
|
||||
func (s *APIServer) generateServerKeys(auth ClientI, w http.ResponseWriter, r *http.Request, _ httprouter.Params, version string) (interface{}, error) {
|
||||
var req *generateServerKeysReq
|
||||
var req GenerateServerKeysRequest
|
||||
if err := httplib.ReadJSON(r, &req); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
keys, err := auth.GenerateServerKeys(req.HostID, req.NodeName, req.Roles)
|
||||
keys, err := auth.GenerateServerKeys(req)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
|
112
lib/auth/auth.go
112
lib/auth/auth.go
|
@ -597,9 +597,36 @@ func HostFQDN(hostUUID, clusterName string) string {
|
|||
return fmt.Sprintf("%v.%v", hostUUID, clusterName)
|
||||
}
|
||||
|
||||
// GenerateServerKeysRequest is a request to generate server keys
|
||||
type GenerateServerKeysRequest struct {
|
||||
// HostID is a unique ID of the host
|
||||
HostID string `json:"host_id"`
|
||||
// NodeName is a user friendly host name
|
||||
NodeName string `json:"node_name"`
|
||||
// Roles is a list of roles assigned to node
|
||||
Roles teleport.Roles `json:"roles"`
|
||||
// AdditionalPrincipals is a list of additional principals
|
||||
// to include in OpenSSH and X509 certificates
|
||||
AdditionalPrincipals []string `json:"additional_principals"`
|
||||
}
|
||||
|
||||
// CheckAndSetDefaults checks and sets default values
|
||||
func (req *GenerateServerKeysRequest) CheckAndSetDefaults() error {
|
||||
if req.HostID == "" {
|
||||
return trace.BadParameter("missing parameter HostID")
|
||||
}
|
||||
if len(req.Roles) != 1 {
|
||||
return trace.BadParameter("expected only one system role, got %v", len(req.Roles))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateServerKeys generates new host private keys and certificates (signed
|
||||
// by the host certificate authority) for a node.
|
||||
func (s *AuthServer) GenerateServerKeys(hostID string, nodeName string, roles teleport.Roles) (*PackedKeys, error) {
|
||||
func (s *AuthServer) GenerateServerKeys(req GenerateServerKeysRequest) (*PackedKeys, error) {
|
||||
if err := req.CheckAndSetDefaults(); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
clusterName, err := s.GetDomainName()
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
|
@ -636,33 +663,36 @@ func (s *AuthServer) GenerateServerKeys(hostID string, nodeName string, roles te
|
|||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// generate hostSSH certificate
|
||||
hostSSHCert, err := s.Authority.GenerateHostCert(services.HostCertParams{
|
||||
PrivateCASigningKey: caPrivateKey,
|
||||
PublicHostKey: pubSSHKey,
|
||||
HostID: hostID,
|
||||
NodeName: nodeName,
|
||||
HostID: req.HostID,
|
||||
NodeName: req.NodeName,
|
||||
ClusterName: clusterName,
|
||||
Roles: roles,
|
||||
Roles: req.Roles,
|
||||
Principals: append([]string{}, req.AdditionalPrincipals...),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
// generate host TLS certificate
|
||||
identity := tlsca.Identity{
|
||||
Username: HostFQDN(hostID, clusterName),
|
||||
Groups: roles.StringSlice(),
|
||||
Username: HostFQDN(req.HostID, clusterName),
|
||||
Groups: req.Roles.StringSlice(),
|
||||
}
|
||||
certRequest := tlsca.CertificateRequest{
|
||||
Clock: s.clock,
|
||||
PublicKey: cryptoPubKey,
|
||||
Subject: identity.Subject(),
|
||||
NotAfter: s.clock.Now().UTC().Add(defaults.CATTL),
|
||||
DNSNames: append([]string{}, req.AdditionalPrincipals...),
|
||||
}
|
||||
// HTTPS requests need to specify DNS name that should be present in the
|
||||
// certificate as one of the DNS Names. It is not known in advance,
|
||||
// that is why there is a default one for all certificates
|
||||
if roles.Include(teleport.RoleAuth) || roles.Include(teleport.RoleAdmin) {
|
||||
certRequest.DNSNames = []string{teleport.APIDomain}
|
||||
if req.Roles.Include(teleport.RoleAuth) || req.Roles.Include(teleport.RoleAdmin) {
|
||||
certRequest.DNSNames = append(certRequest.DNSNames, teleport.APIDomain)
|
||||
}
|
||||
hostTLSCert, err := tlsAuthority.GenerateCertificate(certRequest)
|
||||
if err != nil {
|
||||
|
@ -720,6 +750,35 @@ func (s *AuthServer) checkTokenTTL(token string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// RegisterUsingTokenRequest is a request to register with
|
||||
// auth server using authentication token
|
||||
type RegisterUsingTokenRequest struct {
|
||||
// HostID is a unique host ID, usually a UUID
|
||||
HostID string `json:"hostID"`
|
||||
// NodeName is a node name
|
||||
NodeName string `json:"node_name"`
|
||||
// Role is a system role, e.g. Proxy
|
||||
Role teleport.Role `json:"role"`
|
||||
// Token is an authentication token
|
||||
Token string `json:"token"`
|
||||
// AdditionalPrincipals is a list of additional principals
|
||||
AdditionalPrincipals []string `json:"additional_principals"`
|
||||
}
|
||||
|
||||
// CheckAndSetDefaults checks for errors and sets defaults
|
||||
func (r *RegisterUsingTokenRequest) CheckAndSetDefaults() error {
|
||||
if r.HostID == "" {
|
||||
return trace.BadParameter("missing parameter HostID")
|
||||
}
|
||||
if r.Token == "" {
|
||||
return trace.BadParameter("missing parameter Token")
|
||||
}
|
||||
if err := r.Role.Check(); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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).
|
||||
|
@ -727,40 +786,41 @@ func (s *AuthServer) checkTokenTTL(token string) bool {
|
|||
// 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.
|
||||
func (s *AuthServer) RegisterUsingToken(token, hostID string, nodeName string, role teleport.Role) (*PackedKeys, error) {
|
||||
log.Infof("Node %q [%v] is trying to join with role: %v.", nodeName, hostID, role)
|
||||
if hostID == "" {
|
||||
return nil, trace.BadParameter("HostID cannot be empty")
|
||||
}
|
||||
|
||||
if err := role.Check(); err != nil {
|
||||
func (s *AuthServer) RegisterUsingToken(req RegisterUsingTokenRequest) (*PackedKeys, error) {
|
||||
log.Infof("Node %q [%v] is trying to join with role: %v.", req.NodeName, req.HostID, req.Role)
|
||||
if err := req.CheckAndSetDefaults(); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// make sure the token is valid
|
||||
roles, err := s.ValidateToken(token)
|
||||
roles, err := s.ValidateToken(req.Token)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("%q [%v] can not join the cluster with role %s, token error: %v", nodeName, hostID, role, err)
|
||||
msg := fmt.Sprintf("%q [%v] can not join the cluster with role %s, token error: %v", req.NodeName, req.HostID, req.Role, err)
|
||||
log.Warn(msg)
|
||||
return nil, trace.AccessDenied(msg)
|
||||
}
|
||||
|
||||
// make sure the caller is requested wthe role allowed by the token
|
||||
if !roles.Include(role) {
|
||||
msg := fmt.Sprintf("node %q [%v] can not join the cluster, the token does not allow %q role", nodeName, hostID, role)
|
||||
// make sure the caller is requested the role allowed by the token
|
||||
if !roles.Include(req.Role) {
|
||||
msg := fmt.Sprintf("node %q [%v] can not join the cluster, the token does not allow %q role", req.NodeName, req.HostID, req.Role)
|
||||
log.Warn(msg)
|
||||
return nil, trace.BadParameter(msg)
|
||||
}
|
||||
if !s.checkTokenTTL(token) {
|
||||
return nil, trace.AccessDenied("node %q [%v] can not join the cluster, token has expired", nodeName, hostID)
|
||||
if !s.checkTokenTTL(req.Token) {
|
||||
return nil, trace.AccessDenied("node %q [%v] can not join the cluster, token has expired", req.NodeName, req.HostID)
|
||||
}
|
||||
|
||||
// generate and return host certificate and keys
|
||||
keys, err := s.GenerateServerKeys(hostID, nodeName, teleport.Roles{role})
|
||||
keys, err := s.GenerateServerKeys(GenerateServerKeysRequest{
|
||||
HostID: req.HostID,
|
||||
NodeName: req.NodeName,
|
||||
Roles: teleport.Roles{req.Role},
|
||||
AdditionalPrincipals: req.AdditionalPrincipals,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
log.Infof("Node %q [%v] has joined the cluster.", nodeName, hostID)
|
||||
log.Infof("Node %q [%v] has joined the cluster.", req.NodeName, req.HostID)
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/gravitational/teleport"
|
||||
authority "github.com/gravitational/teleport/lib/auth/testauthority"
|
||||
"github.com/gravitational/teleport/lib/backend"
|
||||
|
@ -31,10 +33,9 @@ import (
|
|||
"github.com/gravitational/teleport/lib/services/suite"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
"github.com/coreos/go-oidc/jose"
|
||||
"github.com/coreos/go-oidc/oidc"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/jonboulle/clockwork"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
@ -183,7 +184,12 @@ func (s *AuthSuite) TestTokensCRUD(c *C) {
|
|||
c.Assert(roles.Include(teleport.RoleProxy), Equals, false)
|
||||
|
||||
// unsuccessful registration (wrong role)
|
||||
keys, err := s.a.RegisterUsingToken(tok, "bad-host-id", "bad-node-name", teleport.RoleProxy)
|
||||
keys, err := s.a.RegisterUsingToken(RegisterUsingTokenRequest{
|
||||
Token: tok,
|
||||
HostID: "bad-host-id",
|
||||
NodeName: "bad-node-name",
|
||||
Role: teleport.RoleProxy,
|
||||
})
|
||||
c.Assert(keys, IsNil)
|
||||
c.Assert(err, NotNil)
|
||||
c.Assert(err, ErrorMatches, `node "bad-node-name" \[bad-host-id\] can not join the cluster, the token does not allow "Proxy" role`)
|
||||
|
@ -198,14 +204,38 @@ func (s *AuthSuite) TestTokensCRUD(c *C) {
|
|||
c.Assert(err, IsNil)
|
||||
|
||||
// use it twice:
|
||||
_, err = s.a.RegisterUsingToken(multiUseToken, "once", "node-name", teleport.RoleProxy)
|
||||
keys, err = s.a.RegisterUsingToken(RegisterUsingTokenRequest{
|
||||
Token: multiUseToken,
|
||||
HostID: "once",
|
||||
NodeName: "node-name",
|
||||
Role: teleport.RoleProxy,
|
||||
AdditionalPrincipals: []string{"example.com"},
|
||||
})
|
||||
c.Assert(err, IsNil)
|
||||
_, err = s.a.RegisterUsingToken(multiUseToken, "twice", "node-name", teleport.RoleProxy)
|
||||
|
||||
// along the way, make sure that additional principals work
|
||||
key, _, _, _, err := ssh.ParseAuthorizedKey(keys.Cert)
|
||||
c.Assert(err, IsNil)
|
||||
hostCert := key.(*ssh.Certificate)
|
||||
comment := Commentf("can't find example.com in %v", hostCert.ValidPrincipals)
|
||||
c.Assert(utils.SliceContainsStr(hostCert.ValidPrincipals, "example.com"), Equals, true, comment)
|
||||
|
||||
_, err = s.a.RegisterUsingToken(RegisterUsingTokenRequest{
|
||||
Token: multiUseToken,
|
||||
HostID: "twice",
|
||||
NodeName: "node-name",
|
||||
Role: teleport.RoleProxy,
|
||||
})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// try to use after TTL:
|
||||
s.a.clock = clockwork.NewFakeClockAt(time.Now().UTC().Add(time.Hour + 1))
|
||||
_, err = s.a.RegisterUsingToken(multiUseToken, "late.bird", "node-name", teleport.RoleProxy)
|
||||
_, err = s.a.RegisterUsingToken(RegisterUsingTokenRequest{
|
||||
Token: multiUseToken,
|
||||
HostID: "late.bird",
|
||||
NodeName: "node-name",
|
||||
Role: teleport.RoleProxy,
|
||||
})
|
||||
c.Assert(err, ErrorMatches, `node "node-name" \[late.bird\] can not join the cluster, token has expired`)
|
||||
|
||||
// expired token should be gone now
|
||||
|
@ -220,9 +250,19 @@ func (s *AuthSuite) TestTokensCRUD(c *C) {
|
|||
c.Assert(err, IsNil)
|
||||
err = s.a.SetStaticTokens(st)
|
||||
c.Assert(err, IsNil)
|
||||
_, err = s.a.RegisterUsingToken("static-token-value", "static.host", "node-name", teleport.RoleProxy)
|
||||
_, err = s.a.RegisterUsingToken(RegisterUsingTokenRequest{
|
||||
Token: "static-token-value",
|
||||
HostID: "static.host",
|
||||
NodeName: "node-name",
|
||||
Role: teleport.RoleProxy,
|
||||
})
|
||||
c.Assert(err, IsNil)
|
||||
_, err = s.a.RegisterUsingToken("static-token-value", "wrong.role", "node-name", teleport.RoleAuth)
|
||||
_, err = s.a.RegisterUsingToken(RegisterUsingTokenRequest{
|
||||
Token: "static-token-value",
|
||||
HostID: "wrong.role",
|
||||
NodeName: "node-name",
|
||||
Role: teleport.RoleAuth,
|
||||
})
|
||||
c.Assert(err, NotNil)
|
||||
r, err := s.a.ValidateToken("static-token-value")
|
||||
c.Assert(err, IsNil)
|
||||
|
|
|
@ -214,9 +214,9 @@ func (a *AuthWithRoles) GenerateToken(roles teleport.Roles, ttl time.Duration) (
|
|||
return a.authServer.GenerateToken(roles, ttl)
|
||||
}
|
||||
|
||||
func (a *AuthWithRoles) RegisterUsingToken(token, hostID string, nodeName string, role teleport.Role) (*PackedKeys, error) {
|
||||
func (a *AuthWithRoles) RegisterUsingToken(req RegisterUsingTokenRequest) (*PackedKeys, error) {
|
||||
// tokens have authz mechanism on their own, no need to check
|
||||
return a.authServer.RegisterUsingToken(token, hostID, nodeName, role)
|
||||
return a.authServer.RegisterUsingToken(req)
|
||||
}
|
||||
|
||||
func (a *AuthWithRoles) RegisterNewAuthServer(token string) error {
|
||||
|
@ -226,24 +226,24 @@ func (a *AuthWithRoles) RegisterNewAuthServer(token string) error {
|
|||
|
||||
// GenerateServerKeys generates new host private keys and certificates (signed
|
||||
// by the host certificate authority) for a node.
|
||||
func (a *AuthWithRoles) GenerateServerKeys(hostID string, nodeName string, roles teleport.Roles) (*PackedKeys, error) {
|
||||
func (a *AuthWithRoles) GenerateServerKeys(req GenerateServerKeysRequest) (*PackedKeys, error) {
|
||||
clusterName, err := a.authServer.GetDomainName()
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
// username is hostID + cluster name, so make sure server requests new keys for itself
|
||||
if a.user.GetName() != HostFQDN(hostID, clusterName) {
|
||||
return nil, trace.AccessDenied("username mismatch %q and %q", a.user.GetName(), HostFQDN(hostID, clusterName))
|
||||
if a.user.GetName() != HostFQDN(req.HostID, clusterName) {
|
||||
return nil, trace.AccessDenied("username mismatch %q and %q", a.user.GetName(), HostFQDN(req.HostID, clusterName))
|
||||
}
|
||||
existingRoles, err := teleport.NewRoles(a.user.GetRoles())
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
// prohibit privilege escalations through role changes
|
||||
if !existingRoles.Equals(roles) {
|
||||
return nil, trace.AccessDenied("roles do not match: %v and %v", existingRoles, roles)
|
||||
if !existingRoles.Equals(req.Roles) {
|
||||
return nil, trace.AccessDenied("roles do not match: %v and %v", existingRoles, req.Roles)
|
||||
}
|
||||
return a.authServer.GenerateServerKeys(hostID, nodeName, roles)
|
||||
return a.authServer.GenerateServerKeys(req)
|
||||
}
|
||||
|
||||
func (a *AuthWithRoles) UpsertNode(s services.Server) error {
|
||||
|
|
|
@ -424,34 +424,28 @@ func (c *Client) GenerateToken(roles teleport.Roles, ttl time.Duration) (string,
|
|||
|
||||
// RegisterUsingToken calls the auth service API to register a new node using a registration token
|
||||
// which was previously issued via GenerateToken.
|
||||
func (c *Client) RegisterUsingToken(token, hostID string, nodeName string, role teleport.Role) (*PackedKeys, error) {
|
||||
out, err := c.PostJSON(c.Endpoint("tokens", "register"),
|
||||
registerUsingTokenReq{
|
||||
HostID: hostID,
|
||||
NodeName: nodeName,
|
||||
Token: token,
|
||||
Role: role,
|
||||
})
|
||||
func (c *Client) RegisterUsingToken(req RegisterUsingTokenRequest) (*PackedKeys, error) {
|
||||
if err := req.CheckAndSetDefaults(); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
out, err := c.PostJSON(c.Endpoint("tokens", "register"), req)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
var keys PackedKeys
|
||||
if err := json.Unmarshal(out.Bytes(), &keys); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return &keys, nil
|
||||
}
|
||||
|
||||
// RenewCredentials returns a new set of credentials associated
|
||||
// with the server with the same privileges
|
||||
func (c *Client) GenerateServerKeys(hostID string, nodeName string, roles teleport.Roles) (*PackedKeys, error) {
|
||||
out, err := c.PostJSON(c.Endpoint("server", "credentials"), generateServerKeysReq{
|
||||
HostID: hostID,
|
||||
NodeName: nodeName,
|
||||
Roles: roles,
|
||||
})
|
||||
func (c *Client) GenerateServerKeys(req GenerateServerKeysRequest) (*PackedKeys, error) {
|
||||
if err := req.CheckAndSetDefaults(); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
out, err := c.PostJSON(c.Endpoint("server", "credentials"), req)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
@ -2243,7 +2237,7 @@ type ProvisioningService interface {
|
|||
|
||||
// RegisterUsingToken calls the auth service API to register a new node via registration token
|
||||
// which has been previously issued via GenerateToken
|
||||
RegisterUsingToken(token, hostID string, nodeName string, role teleport.Role) (*PackedKeys, error)
|
||||
RegisterUsingToken(req RegisterUsingTokenRequest) (*PackedKeys, error)
|
||||
|
||||
// RegisterNewAuthServer is used to register new auth server with token
|
||||
RegisterNewAuthServer(token string) error
|
||||
|
@ -2265,7 +2259,7 @@ type ClientI interface {
|
|||
GetDomainName() (string, error)
|
||||
// GenerateServerKeys generates new host private keys and certificates (signed
|
||||
// by the host certificate authority) for a node
|
||||
GenerateServerKeys(hostID string, nodeName string, roles teleport.Roles) (*PackedKeys, error)
|
||||
GenerateServerKeys(GenerateServerKeysRequest) (*PackedKeys, error)
|
||||
// DELETE IN: 2.6.0
|
||||
// AccessPointDialer is no longer used for communication with auth server
|
||||
// GetDialer returns dialer that will connect to auth server API
|
||||
|
|
|
@ -216,7 +216,11 @@ func (a *TestAuthServer) NewCertificate(identity TestIdentity) (*tls.Certificate
|
|||
}
|
||||
return &cert, nil
|
||||
case BuiltinRole:
|
||||
keys, err := a.AuthServer.GenerateServerKeys(id.Username, id.Username, teleport.Roles{id.Role})
|
||||
keys, err := a.AuthServer.GenerateServerKeys(GenerateServerKeysRequest{
|
||||
HostID: id.Username,
|
||||
NodeName: id.Username,
|
||||
Roles: teleport.Roles{id.Role},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
@ -518,7 +522,11 @@ func (t *TestTLSServer) Stop() error {
|
|||
|
||||
// NewServerIdentity generates new server identity, used in tests
|
||||
func NewServerIdentity(clt *AuthServer, hostID string, role teleport.Role) (*Identity, error) {
|
||||
keys, err := clt.GenerateServerKeys(hostID, hostID, teleport.Roles{teleport.RoleAuth})
|
||||
keys, err := clt.GenerateServerKeys(GenerateServerKeysRequest{
|
||||
HostID: hostID,
|
||||
NodeName: hostID,
|
||||
Roles: teleport.Roles{teleport.RoleAuth},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
|
|
@ -543,7 +543,11 @@ func initKeys(a *AuthServer, dataDir string, id IdentityID) (*Identity, error) {
|
|||
}
|
||||
|
||||
if !keyExists || !sshCertExists || !tlsCertExists {
|
||||
packedKeys, err := a.GenerateServerKeys(id.HostUUID, id.NodeName, teleport.Roles{id.Role})
|
||||
packedKeys, err := a.GenerateServerKeys(GenerateServerKeysRequest{
|
||||
HostID: id.HostUUID,
|
||||
NodeName: id.NodeName,
|
||||
Roles: teleport.Roles{id.Role},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
@ -610,6 +614,17 @@ func (i *Identity) HasTLSConfig() bool {
|
|||
return len(i.TLSCACertBytes) != 0 && len(i.TLSCertBytes) != 0 && len(i.TLSCACertBytes) != 0
|
||||
}
|
||||
|
||||
// HasPrincipals returns whether identity has principals
|
||||
func (i *Identity) HasPrincipals(additionalPrincipals []string) bool {
|
||||
set := utils.StringsSet(additionalPrincipals)
|
||||
for _, principal := range i.Cert.ValidPrincipals {
|
||||
if _, ok := set[principal]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// TLSConfig returns TLS config for mutual TLS authentication
|
||||
// can return NotFound error if there are no TLS credentials setup for identity
|
||||
func (i *Identity) TLSConfig() (*tls.Config, error) {
|
||||
|
|
|
@ -33,8 +33,13 @@ import (
|
|||
|
||||
// LocalRegister is used to generate host keys when a node or proxy is running within the same process
|
||||
// as the auth server. This method does not need to use provisioning tokens.
|
||||
func LocalRegister(dataDir string, id IdentityID, authServer *AuthServer) error {
|
||||
keys, err := authServer.GenerateServerKeys(id.HostUUID, id.NodeName, teleport.Roles{id.Role})
|
||||
func LocalRegister(dataDir string, id IdentityID, authServer *AuthServer, additionalPrincipals []string) error {
|
||||
keys, err := authServer.GenerateServerKeys(GenerateServerKeysRequest{
|
||||
HostID: id.HostUUID,
|
||||
NodeName: id.NodeName,
|
||||
Roles: teleport.Roles{id.Role},
|
||||
AdditionalPrincipals: additionalPrincipals,
|
||||
})
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
@ -44,7 +49,7 @@ func LocalRegister(dataDir string, id IdentityID, authServer *AuthServer) error
|
|||
// Register is used to generate host keys when a node or proxy are running on different hosts
|
||||
// than the auth server. This method requires provisioning tokens to prove a valid auth server
|
||||
// was used to issue the joining request.
|
||||
func Register(dataDir, token string, id IdentityID, servers []utils.NetAddr) error {
|
||||
func Register(dataDir, token string, id IdentityID, servers []utils.NetAddr, additionalPrincipals []string) error {
|
||||
tok, err := readToken(token)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
|
@ -78,7 +83,13 @@ func Register(dataDir, token string, id IdentityID, servers []utils.NetAddr) err
|
|||
defer client.Close()
|
||||
|
||||
// get the host certificate and keys
|
||||
keys, err := client.RegisterUsingToken(tok, id.HostUUID, id.NodeName, id.Role)
|
||||
keys, err := client.RegisterUsingToken(RegisterUsingTokenRequest{
|
||||
Token: tok,
|
||||
HostID: id.HostUUID,
|
||||
NodeName: id.NodeName,
|
||||
Role: id.Role,
|
||||
AdditionalPrincipals: additionalPrincipals,
|
||||
})
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
@ -88,13 +99,17 @@ func Register(dataDir, token string, id IdentityID, servers []utils.NetAddr) err
|
|||
|
||||
// ReRegister renews the certificates and private keys based on the existing
|
||||
// identity ID
|
||||
func ReRegister(dataDir string, clt ClientI, id IdentityID) error {
|
||||
func ReRegister(dataDir string, clt ClientI, id IdentityID, additionalPrincipals []string) error {
|
||||
hostID, err := id.HostID()
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
keys, err := clt.GenerateServerKeys(
|
||||
hostID, id.NodeName, teleport.Roles{id.Role})
|
||||
keys, err := clt.GenerateServerKeys(GenerateServerKeysRequest{
|
||||
HostID: hostID,
|
||||
NodeName: id.NodeName,
|
||||
Roles: teleport.Roles{id.Role},
|
||||
AdditionalPrincipals: additionalPrincipals,
|
||||
})
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ func (n *Keygen) GenerateHostCert(c services.HostCertParams) ([]byte, error) {
|
|||
validBefore = uint64(b.Unix())
|
||||
}
|
||||
principals := native.BuildPrincipals(c.HostID, c.NodeName, c.ClusterName, c.Roles)
|
||||
principals = append(principals, c.Principals...)
|
||||
cert := &ssh.Certificate{
|
||||
ValidPrincipals: principals,
|
||||
Key: pubKey,
|
||||
|
|
|
@ -521,20 +521,35 @@ func (s *TLSSuite) TestGenerateCerts(c *check.C) {
|
|||
c.Assert(err, check.IsNil)
|
||||
|
||||
certs, err := hostClient.GenerateServerKeys(
|
||||
hostID, s.server.AuthServer.ClusterName, teleport.Roles{teleport.RoleNode})
|
||||
GenerateServerKeysRequest{
|
||||
HostID: hostID,
|
||||
NodeName: s.server.AuthServer.ClusterName,
|
||||
Roles: teleport.Roles{teleport.RoleNode},
|
||||
AdditionalPrincipals: []string{"example.com"},
|
||||
})
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
_, _, _, _, err = ssh.ParseAuthorizedKey(certs.Cert)
|
||||
key, _, _, _, err := ssh.ParseAuthorizedKey(certs.Cert)
|
||||
c.Assert(err, check.IsNil)
|
||||
hostCert := key.(*ssh.Certificate)
|
||||
comment := check.Commentf("can't find example.com in %v", hostCert.ValidPrincipals)
|
||||
c.Assert(utils.SliceContainsStr(hostCert.ValidPrincipals, "example.com"), check.Equals, true, comment)
|
||||
|
||||
// attempt to elevate privileges by getting admin role in the certificate
|
||||
_, err = hostClient.GenerateServerKeys(
|
||||
hostID, s.server.AuthServer.ClusterName, teleport.Roles{teleport.RoleAdmin})
|
||||
GenerateServerKeysRequest{
|
||||
HostID: hostID,
|
||||
NodeName: s.server.AuthServer.ClusterName,
|
||||
Roles: teleport.Roles{teleport.RoleAdmin},
|
||||
})
|
||||
fixtures.ExpectAccessDenied(c, err)
|
||||
|
||||
// attempt to get certificate for different host id
|
||||
_, err = hostClient.GenerateServerKeys(
|
||||
"some-other-host-id", s.server.AuthServer.ClusterName, teleport.Roles{teleport.RoleNode})
|
||||
_, err = hostClient.GenerateServerKeys(GenerateServerKeysRequest{
|
||||
HostID: "some-other-host-id",
|
||||
NodeName: s.server.AuthServer.ClusterName,
|
||||
Roles: teleport.Roles{teleport.RoleNode},
|
||||
})
|
||||
fixtures.ExpectAccessDenied(c, err)
|
||||
|
||||
user1, userRole, err := CreateUserAndRole(clt, "user1", []string{"user1"})
|
||||
|
|
|
@ -195,7 +195,7 @@ func (process *TeleportProcess) GetIdentity(role teleport.Role) (i *auth.Identit
|
|||
|
||||
// connectToAuthService attempts to login into the auth servers specified in the
|
||||
// configuration. Returns 'true' if successful
|
||||
func (process *TeleportProcess) connectToAuthService(role teleport.Role) (*Connector, error) {
|
||||
func (process *TeleportProcess) connectToAuthService(role teleport.Role, additionalPrincipals []string) (*Connector, error) {
|
||||
identity, err := process.GetIdentity(role)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
|
@ -223,7 +223,7 @@ func (process *TeleportProcess) connectToAuthService(role teleport.Role) (*Conne
|
|||
return nil, trace.Wrap(err)
|
||||
}
|
||||
defer authClient.Close()
|
||||
if err := auth.ReRegister(process.Config.DataDir, authClient, identity.ID); err != nil {
|
||||
if err := auth.ReRegister(process.Config.DataDir, authClient, identity.ID, additionalPrincipals); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if identity, err = process.readIdentity(role); err != nil {
|
||||
|
@ -240,6 +240,19 @@ func (process *TeleportProcess) connectToAuthService(role teleport.Role) (*Conne
|
|||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if len(additionalPrincipals) != 0 && !identity.HasPrincipals(additionalPrincipals) {
|
||||
log.Infof("Identity %v needs principals %v, going to re-register.", identity.ID, additionalPrincipals)
|
||||
if err := auth.ReRegister(process.Config.DataDir, client, identity.ID, additionalPrincipals); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if identity, err = process.readIdentity(role); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
tlsConfig, err = identity.TLSConfig()
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
}
|
||||
// success ? we're logged in!
|
||||
return &Connector{Client: client, Identity: identity}, nil
|
||||
}
|
||||
|
@ -535,7 +548,7 @@ func (process *TeleportProcess) initAuthService(authority sshca.Authority) error
|
|||
process.RegisterFunc("auth.heartbeat.broadcast", func() error {
|
||||
// Heart beat auth server presence, this is not the best place for this
|
||||
// logic, consolidate it into auth package later
|
||||
connector, err := process.connectToAuthService(teleport.RoleAdmin)
|
||||
connector, err := process.connectToAuthService(teleport.RoleAdmin, nil)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
@ -642,7 +655,7 @@ func (process *TeleportProcess) newLocalCache(clt auth.ClientI, cacheName []stri
|
|||
// initSSH initializes the "node" role, i.e. a simple SSH server connected to the auth server.
|
||||
func (process *TeleportProcess) initSSH() error {
|
||||
process.RegisterWithAuthServer(
|
||||
process.Config.Token, teleport.RoleNode, SSHIdentityEvent)
|
||||
process.Config.Token, teleport.RoleNode, SSHIdentityEvent, nil)
|
||||
eventsC := make(chan Event)
|
||||
process.WaitForEvent(SSHIdentityEvent, eventsC, make(chan struct{}))
|
||||
|
||||
|
@ -722,7 +735,7 @@ func (process *TeleportProcess) initSSH() error {
|
|||
// RegisterWithAuthServer uses one time provisioning token obtained earlier
|
||||
// from the server to get a pair of SSH keys signed by Auth server host
|
||||
// certificate authority
|
||||
func (process *TeleportProcess) RegisterWithAuthServer(token string, role teleport.Role, eventName string) {
|
||||
func (process *TeleportProcess) RegisterWithAuthServer(token string, role teleport.Role, eventName string, additionalPrincipals []string) {
|
||||
cfg := process.Config
|
||||
identityID := auth.IdentityID{Role: role, HostUUID: cfg.HostUUID, NodeName: cfg.Hostname}
|
||||
|
||||
|
@ -733,7 +746,7 @@ func (process *TeleportProcess) RegisterWithAuthServer(token string, role telepo
|
|||
process.RegisterFunc(fmt.Sprintf("register.%v", strings.ToLower(role.String())), func() error {
|
||||
retryTime := defaults.ServerHeartbeatTTL / 3
|
||||
for {
|
||||
connector, err := process.connectToAuthService(role)
|
||||
connector, err := process.connectToAuthService(role, additionalPrincipals)
|
||||
if err == nil {
|
||||
process.BroadcastEvent(Event{Name: eventName, Payload: connector})
|
||||
authClient = connector.Client
|
||||
|
@ -752,14 +765,14 @@ func (process *TeleportProcess) RegisterWithAuthServer(token string, role telepo
|
|||
// Auth service is on the same host, no need to go though the invitation
|
||||
// procedure
|
||||
log.Debugf("This server has local Auth server started, using it to add role to the cluster.")
|
||||
err = auth.LocalRegister(cfg.DataDir, identityID, process.getLocalAuth())
|
||||
err = auth.LocalRegister(cfg.DataDir, identityID, process.getLocalAuth(), additionalPrincipals)
|
||||
} else {
|
||||
// Auth server is remote, so we need a provisioning token
|
||||
if token == "" {
|
||||
return trace.BadParameter("%v must join a cluster and needs a provisioning token", role)
|
||||
}
|
||||
log.Infof("Joining the cluster with a token %v.", token)
|
||||
err = auth.Register(cfg.DataDir, token, identityID, cfg.AuthServers)
|
||||
err = auth.Register(cfg.DataDir, token, identityID, cfg.AuthServers, additionalPrincipals)
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("Failed to join the cluster: %v.", err)
|
||||
|
@ -792,7 +805,16 @@ func (process *TeleportProcess) initProxy() error {
|
|||
}
|
||||
}
|
||||
|
||||
process.RegisterWithAuthServer(process.Config.Token, teleport.RoleProxy, ProxyIdentityEvent)
|
||||
var additionalPrincipals []string
|
||||
if process.Config.Proxy.PublicAddr.Addr != "" {
|
||||
host, err := utils.Host(process.Config.Proxy.PublicAddr.Addr)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
additionalPrincipals = []string{host}
|
||||
}
|
||||
|
||||
process.RegisterWithAuthServer(process.Config.Token, teleport.RoleProxy, ProxyIdentityEvent, additionalPrincipals)
|
||||
process.RegisterFunc("proxy.init", func() error {
|
||||
eventsC := make(chan Event)
|
||||
process.WaitForEvent(ProxyIdentityEvent, eventsC, make(chan struct{}))
|
||||
|
|
|
@ -111,7 +111,11 @@ func (s *SrvSuite) SetUpTest(c *C) {
|
|||
c.Assert(err, IsNil)
|
||||
|
||||
// set up host private key and certificate
|
||||
certs, err := s.server.Auth().GenerateServerKeys(hostID, s.server.ClusterName(), teleport.Roles{teleport.RoleNode})
|
||||
certs, err := s.server.Auth().GenerateServerKeys(auth.GenerateServerKeysRequest{
|
||||
HostID: hostID,
|
||||
NodeName: s.server.ClusterName(),
|
||||
Roles: teleport.Roles{teleport.RoleNode},
|
||||
})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// set up user CA and set up a user that has access to the server
|
||||
|
|
|
@ -33,6 +33,19 @@ import (
|
|||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// StringsSet creates set of string (map[string]struct{})
|
||||
// from a list of strings
|
||||
func StringsSet(in []string) map[string]struct{} {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := make(map[string]struct{})
|
||||
for _, v := range in {
|
||||
out[v] = struct{}{}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ParseOnOff parses whether value is "on" or "off", parameterName is passed for error
|
||||
// reporting purposes, defaultValue is returned when no value is set
|
||||
func ParseOnOff(parameterName, val string, defaultValue bool) (bool, error) {
|
||||
|
@ -62,6 +75,18 @@ func IsGroupMember(gid int) (bool, error) {
|
|||
return false, nil
|
||||
}
|
||||
|
||||
// Host extracts host from host:port string
|
||||
func Host(hostname string) (string, error) {
|
||||
if hostname == "" {
|
||||
return "", trace.BadParameter("missing parameter hostname")
|
||||
}
|
||||
if !strings.Contains(hostname, ":") {
|
||||
return hostname, nil
|
||||
}
|
||||
host, _, err := SplitHostPort(hostname)
|
||||
return host, err
|
||||
}
|
||||
|
||||
// SplitHostPort splits host and port and checks that host is not empty
|
||||
func SplitHostPort(hostname string) (string, string, error) {
|
||||
host, port, err := net.SplitHostPort(hostname)
|
||||
|
|
|
@ -152,7 +152,11 @@ func (s *WebSuite) SetUpTest(c *C) {
|
|||
nodePort := s.freePorts[len(s.freePorts)-1]
|
||||
s.freePorts = s.freePorts[:len(s.freePorts)-1]
|
||||
|
||||
certs, err := s.server.Auth().GenerateServerKeys(hostID, s.server.ClusterName(), teleport.Roles{teleport.RoleNode})
|
||||
certs, err := s.server.Auth().GenerateServerKeys(auth.GenerateServerKeysRequest{
|
||||
HostID: hostID,
|
||||
NodeName: s.server.ClusterName(),
|
||||
Roles: teleport.Roles{teleport.RoleNode},
|
||||
})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
signer, err := sshutils.NewSigner(certs.Key, certs.Cert)
|
||||
|
|
Loading…
Reference in a new issue