Merge branch 'roman/anon' of github.com:gravitational/teleport into roman/anon

This commit is contained in:
Roman Tkachenko 2018-01-09 12:27:02 -08:00
commit dd515b4580
14 changed files with 293 additions and 108 deletions

View file

@ -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)
}

View file

@ -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
}

View file

@ -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)

View file

@ -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 {

View file

@ -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

View file

@ -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)
}

View file

@ -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) {

View file

@ -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)
}

View file

@ -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,

View file

@ -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"})

View file

@ -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{}))

View file

@ -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

View file

@ -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)

View file

@ -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)