mirror of
https://github.com/gravitational/teleport
synced 2024-10-21 17:53:28 +00:00
Introduce auth server and proxy heartbeats
This commit introduces heartbeats of AuthServers and Proxies and fixes several issues: 1. Server init problem There was an issue in server init, when certificates of multiple roles were overwriting each otther. Now Teleport stores each keypair and certificate in a separate file <hostid>.role.key and <hostid>.role.cert This also means that it's backwards incompatible with previous on disk format. 2. Proxy and Auth heartbeats Auth servers and proxies now heartbeat into cluster as well 3. Bugfixes: * Proxy role was missing, it is now treated as a separate role with permissions * AdvertiseIP is now a global setting that can be used by all roles * --advertise-ip flag was ignored and was never applied * teleport service initialization has been simplified, now each role get it's own client * minor cleanups
This commit is contained in:
parent
a8f419a0ad
commit
c1e0604dd0
|
@ -2,20 +2,20 @@
|
|||
# This Makefile is used for CI/CD builds. Please be careful!
|
||||
#
|
||||
BBOX=teleport-buildbox:latest
|
||||
OUT="../"
|
||||
OUT="../out"
|
||||
|
||||
#
|
||||
# builds 'teleport' binary and places it $(OUT) dir
|
||||
#
|
||||
build: bbox
|
||||
docker run -i --rm=true \
|
||||
-v $$(pwd)/../:/gopath/src/github.com/gravitational/teleport \
|
||||
-v "$$(pwd)/../":/gopath/src/github.com/gravitational/teleport \
|
||||
-u $$(id -u):$$(id -g) \
|
||||
$(BBOX) \
|
||||
/bin/bash -c "make -C /gopath/src/github.com/gravitational/teleport FLAGS='-cover -race' clean test all"
|
||||
@echo "\nSUCCESS ----> $(OUT)teleport"
|
||||
@echo "SUCCESS ----> $(OUT)tctl"
|
||||
@echo "SUCCESS ----> $(OUT)tsh"
|
||||
@echo "\nSUCCESS ----> $(OUT)/teleport"
|
||||
@echo "SUCCESS ----> $(OUT)/tctl"
|
||||
@echo "SUCCESS ----> $(OUT)/tsh"
|
||||
|
||||
#
|
||||
# builds buildbox:1.0.0 Docker container which is Debian8 with Golang 1.4.2
|
||||
|
|
|
@ -317,6 +317,12 @@ func ConvertSystemError(err error) error {
|
|||
if terr, ok := err.(trace.Error); ok {
|
||||
innerError = terr.OrigError()
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return NotFound(innerError.Error())
|
||||
}
|
||||
if os.IsPermission(err) {
|
||||
return AccessDenied(innerError.Error())
|
||||
}
|
||||
switch realErr := innerError.(type) {
|
||||
case *net.OpError:
|
||||
return ConnectionProblem(
|
||||
|
|
|
@ -32,11 +32,15 @@ type AccessPoint interface {
|
|||
GetLocalDomain() (string, error)
|
||||
|
||||
// GetServers returns a list of registered servers
|
||||
GetServers() ([]services.Server, error)
|
||||
GetNodes() ([]services.Server, error)
|
||||
|
||||
// UpsertServer registers server presence, permanently if ttl is 0 or
|
||||
// for the specified duration with second resolution if it's >= 1 second
|
||||
UpsertServer(s services.Server, ttl time.Duration) error
|
||||
UpsertNode(s services.Server, ttl time.Duration) error
|
||||
|
||||
// UpsertProxy registers server presence, permanently if ttl is 0 or
|
||||
// for the specified duration with second resolution if it's >= 1 second
|
||||
UpsertProxy(s services.Server, ttl time.Duration) error
|
||||
|
||||
// GetCertAuthorities returns a list of cert authorities
|
||||
GetCertAuthorities(caType services.CertAuthType) ([]*services.CertAuthority, error)
|
||||
|
|
|
@ -36,26 +36,34 @@ import (
|
|||
)
|
||||
|
||||
type APIWithRoles struct {
|
||||
config APIConfig
|
||||
listeners map[teleport.Role]*fakeSocket
|
||||
servers map[teleport.Role]*APIServer
|
||||
}
|
||||
|
||||
func NewAPIWithRoles(authServer *AuthServer, elog events.Log,
|
||||
sessions session.Service, recorder recorder.Recorder,
|
||||
permChecker PermissionChecker,
|
||||
roles []teleport.Role) *APIWithRoles {
|
||||
// APIConfig is a configuration file
|
||||
type APIConfig struct {
|
||||
AuthServer *AuthServer
|
||||
EventLog events.Log
|
||||
SessionService session.Service
|
||||
Recorder recorder.Recorder
|
||||
Roles []teleport.Role
|
||||
PermissionChecker PermissionChecker
|
||||
}
|
||||
|
||||
func NewAPIWithRoles(config APIConfig) *APIWithRoles {
|
||||
|
||||
api := APIWithRoles{}
|
||||
api.listeners = make(map[teleport.Role]*fakeSocket)
|
||||
api.servers = make(map[teleport.Role]*APIServer)
|
||||
|
||||
for _, role := range roles {
|
||||
for _, role := range config.Roles {
|
||||
a := AuthWithRoles{
|
||||
authServer: authServer,
|
||||
elog: elog,
|
||||
sessions: sessions,
|
||||
recorder: recorder,
|
||||
permChecker: permChecker,
|
||||
authServer: config.AuthServer,
|
||||
elog: config.EventLog,
|
||||
sessions: config.SessionService,
|
||||
recorder: config.Recorder,
|
||||
permChecker: config.PermissionChecker,
|
||||
role: role,
|
||||
}
|
||||
api.servers[role] = NewAPIServer(&a)
|
||||
|
@ -66,7 +74,7 @@ func NewAPIWithRoles(authServer *AuthServer, elog events.Log,
|
|||
|
||||
func (api *APIWithRoles) Serve() {
|
||||
wg := sync.WaitGroup{}
|
||||
for role, _ := range api.listeners {
|
||||
for role := range api.listeners {
|
||||
wg.Add(1)
|
||||
go func(listener net.Listener, handler http.Handler) {
|
||||
if err := http.Serve(listener, handler); (err != nil) && (err != io.EOF) {
|
||||
|
|
|
@ -32,7 +32,6 @@ import (
|
|||
"github.com/gravitational/teleport/lib/utils"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
|
||||
"github.com/codahale/lunk"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
@ -91,9 +90,12 @@ func NewAPIServer(a *AuthWithRoles) *APIServer {
|
|||
srv.POST("/v1/signuptokens", httplib.MakeHandler(srv.createSignupToken))
|
||||
|
||||
// Servers and presence heartbeat
|
||||
srv.POST("/v1/servers", httplib.MakeHandler(srv.upsertServer))
|
||||
srv.GET("/v1/servers", httplib.MakeHandler(srv.getServers))
|
||||
srv.GET("/v1/auth/servers", httplib.MakeHandler(srv.getAuthServers))
|
||||
srv.POST("/v1/nodes", httplib.MakeHandler(srv.upsertNode))
|
||||
srv.GET("/v1/nodes", httplib.MakeHandler(srv.getNodes))
|
||||
srv.POST("/v1/authservers", httplib.MakeHandler(srv.upsertAuthServer))
|
||||
srv.GET("/v1/authservers", httplib.MakeHandler(srv.getAuthServers))
|
||||
srv.POST("/v1/proxies", httplib.MakeHandler(srv.upsertProxy))
|
||||
srv.GET("/v1/proxies", httplib.MakeHandler(srv.getProxies))
|
||||
|
||||
// Tokens
|
||||
srv.POST("/v1/tokens", httplib.MakeHandler(srv.generateToken))
|
||||
|
@ -127,33 +129,69 @@ type upsertServerReq struct {
|
|||
TTL time.Duration `json:"ttl"`
|
||||
}
|
||||
|
||||
// upsertServer is called by remote SSH nodes when they ping back into the auth service
|
||||
func (s *APIServer) upsertServer(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
|
||||
// upsertNode is called by remote SSH nodes when they ping back into the auth service
|
||||
func (s *APIServer) upsertServer(role teleport.Role, w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
|
||||
var req upsertServerReq
|
||||
if err := httplib.ReadJSON(r, &req); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
log.Debugf("[AUTH API] ping from %v (%v) at %v", req.Server.ID, req.Server.Hostname, r.RemoteAddr)
|
||||
log.Debugf("[AUTH API] ping from %v %v (%v) at %v", role, req.Server.ID, req.Server.Hostname, r.RemoteAddr)
|
||||
|
||||
// if server sent "local" IP address to us, replace the ip/host part with the remote address we see
|
||||
// on the socket, but keep the original port:
|
||||
req.Server.Addr = utils.ReplaceLocalhost(req.Server.Addr, r.RemoteAddr)
|
||||
|
||||
if err := s.a.UpsertServer(req.Server, req.TTL); err != nil {
|
||||
log.Error(err)
|
||||
return nil, trace.Wrap(err)
|
||||
switch role {
|
||||
case teleport.RoleNode:
|
||||
if err := s.a.UpsertNode(req.Server, req.TTL); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
case teleport.RoleAuth:
|
||||
if err := s.a.UpsertAuthServer(req.Server, req.TTL); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
case teleport.RoleProxy:
|
||||
if err := s.a.UpsertProxy(req.Server, req.TTL); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
}
|
||||
return message("ok"), nil
|
||||
}
|
||||
|
||||
func (s *APIServer) getServers(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
|
||||
servers, err := s.a.GetServers()
|
||||
// upsertNode is called by remote SSH nodes when they ping back into the auth service
|
||||
func (s *APIServer) upsertNode(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
|
||||
return s.upsertServer(teleport.RoleNode, w, r, p)
|
||||
}
|
||||
|
||||
// getNodes returns registered SSH nodes
|
||||
func (s *APIServer) getNodes(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
|
||||
servers, err := s.a.GetNodes()
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return servers, nil
|
||||
}
|
||||
|
||||
// upsertProxy is called by remote SSH nodes when they ping back into the auth service
|
||||
func (s *APIServer) upsertProxy(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
|
||||
return s.upsertServer(teleport.RoleProxy, w, r, p)
|
||||
}
|
||||
|
||||
// getProxies returns registered proxies
|
||||
func (s *APIServer) getProxies(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
|
||||
servers, err := s.a.GetProxies()
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return servers, nil
|
||||
}
|
||||
|
||||
// upsertAuthServer is called by remote Auth servers when they ping back into the auth service
|
||||
func (s *APIServer) upsertAuthServer(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
|
||||
return s.upsertServer(teleport.RoleAuth, w, r, p)
|
||||
}
|
||||
|
||||
// getAuthServers returns registered auth servers
|
||||
func (s *APIServer) getAuthServers(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
|
||||
servers, err := s.a.GetAuthServers()
|
||||
if err != nil {
|
||||
|
|
|
@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
|
@ -272,27 +273,47 @@ func (s *APISuite) TestSessions(c *C) {
|
|||
}
|
||||
|
||||
func (s *APISuite) TestServers(c *C) {
|
||||
out, err := s.clt.GetServers()
|
||||
out, err := s.clt.GetNodes()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(out), Equals, 0)
|
||||
|
||||
srv := services.Server{ID: "id1", Addr: "host:1233", Hostname: "host1"}
|
||||
c.Assert(s.clt.UpsertServer(srv, 0), IsNil)
|
||||
c.Assert(s.clt.UpsertNode(srv, 0), IsNil)
|
||||
|
||||
srv1 := services.Server{ID: "id2", Addr: "host:1234", Hostname: "host2"}
|
||||
c.Assert(s.clt.UpsertServer(srv1, 0), IsNil)
|
||||
c.Assert(s.clt.UpsertNode(srv1, 0), IsNil)
|
||||
|
||||
out, err = s.clt.GetServers()
|
||||
out, err = s.clt.GetNodes()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(out, DeepEquals, []services.Server{srv, srv1})
|
||||
|
||||
// note: ID is getting overwritten by addr:
|
||||
if out[0].ID == "id1" {
|
||||
c.Assert(out[0], DeepEquals, services.Server{ID: "id1", Addr: "host:1233", Hostname: "host1"})
|
||||
c.Assert(out[1], DeepEquals, services.Server{ID: "id2", Addr: "host:1234", Hostname: "host2"})
|
||||
} else {
|
||||
c.Assert(out[1], DeepEquals, services.Server{ID: "id1", Addr: "host:1233", Hostname: "host1"})
|
||||
c.Assert(out[0], DeepEquals, services.Server{ID: "id2", Addr: "host:1234", Hostname: "host2"})
|
||||
}
|
||||
out, err = s.clt.GetProxies()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(out), Equals, 0)
|
||||
|
||||
srv = services.Server{ID: "proxy1", Addr: "host:1233", Hostname: "host1"}
|
||||
c.Assert(s.clt.UpsertProxy(srv, 0), IsNil)
|
||||
|
||||
srv1 = services.Server{ID: "proxy2", Addr: "host:1234", Hostname: "host2"}
|
||||
c.Assert(s.clt.UpsertProxy(srv1, 0), IsNil)
|
||||
|
||||
out, err = s.clt.GetProxies()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(out, DeepEquals, []services.Server{srv, srv1})
|
||||
|
||||
out, err = s.clt.GetAuthServers()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(out), Equals, 0)
|
||||
|
||||
srv = services.Server{ID: "auth1", Addr: "host:1233", Hostname: "host1"}
|
||||
c.Assert(s.clt.UpsertAuthServer(srv, 0), IsNil)
|
||||
|
||||
srv1 = services.Server{ID: "auth2", Addr: "host:1234", Hostname: "host2"}
|
||||
c.Assert(s.clt.UpsertAuthServer(srv1, 0), IsNil)
|
||||
|
||||
out, err = s.clt.GetAuthServers()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(out, DeepEquals, []services.Server{srv, srv1})
|
||||
}
|
||||
|
||||
func (s *APISuite) TestEvents(c *C) {
|
||||
|
|
|
@ -238,30 +238,12 @@ func (s *AuthServer) ValidateToken(token string) (role string, e error) {
|
|||
return tok.Role, nil
|
||||
}
|
||||
|
||||
func (s *AuthServer) RegisterUsingToken(outputToken, hostID string, role teleport.Role) (keys PackedKeys, e error) {
|
||||
log.Infof("[AUTH] Node `%v` is trying to join", hostID)
|
||||
if hostID == "" {
|
||||
return PackedKeys{}, trace.Wrap(fmt.Errorf("HostID cannot be empty"))
|
||||
}
|
||||
if err := role.Check(); err != nil {
|
||||
return PackedKeys{}, trace.Wrap(err)
|
||||
}
|
||||
token, _, err := services.SplitTokenRole(outputToken)
|
||||
if err != nil {
|
||||
return PackedKeys{}, trace.Wrap(err)
|
||||
}
|
||||
tok, err := s.ProvisioningService.GetToken(token)
|
||||
if err != nil {
|
||||
log.Warningf("[AUTH] Node `%v` cannot join: token error. %v", hostID, err)
|
||||
return PackedKeys{}, trace.Wrap(err)
|
||||
}
|
||||
if tok.Role != string(role) {
|
||||
return PackedKeys{}, trace.Wrap(
|
||||
teleport.BadParameter("token.Role", "role does not match"))
|
||||
}
|
||||
// GenerateServerKeys generates private key and certificate signed
|
||||
// by the host certificate authority, listing the role of this server
|
||||
func (s *AuthServer) GenerateServerKeys(hostID string, role teleport.Role) (*PackedKeys, error) {
|
||||
k, pub, err := s.GenerateKeyPair("")
|
||||
if err != nil {
|
||||
return PackedKeys{}, trace.Wrap(err)
|
||||
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
|
||||
|
@ -270,18 +252,43 @@ func (s *AuthServer) RegisterUsingToken(outputToken, hostID string, role telepor
|
|||
c, err := s.GenerateHostCert(pub, fqdn, s.DomainName, role, 0)
|
||||
if err != nil {
|
||||
log.Warningf("[AUTH] Node `%v` cannot join: cert generation error. %v", hostID, err)
|
||||
return PackedKeys{}, trace.Wrap(err)
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
keys = PackedKeys{
|
||||
return &PackedKeys{
|
||||
Key: k,
|
||||
Cert: c,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *AuthServer) RegisterUsingToken(outputToken, hostID string, role teleport.Role) (*PackedKeys, error) {
|
||||
log.Infof("[AUTH] Node `%v` is trying to join", hostID)
|
||||
if hostID == "" {
|
||||
return nil, trace.Wrap(fmt.Errorf("HostID cannot be empty"))
|
||||
}
|
||||
if err := role.Check(); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
token, _, err := services.SplitTokenRole(outputToken)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
tok, err := s.ProvisioningService.GetToken(token)
|
||||
if err != nil {
|
||||
log.Warningf("[AUTH] Node `%v` cannot join: token error. %v", hostID, err)
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if tok.Role != string(role) {
|
||||
return nil, trace.Wrap(
|
||||
teleport.BadParameter("token.Role", "role does not match"))
|
||||
}
|
||||
keys, err := s.GenerateServerKeys(hostID, role)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if err := s.DeleteToken(outputToken); err != nil {
|
||||
return PackedKeys{}, trace.Wrap(err)
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
utils.Consolef(os.Stdout, "[AUTH] Node `%v` joined the cluster", hostID)
|
||||
return keys, nil
|
||||
}
|
||||
|
|
|
@ -25,8 +25,6 @@ import (
|
|||
"github.com/gravitational/teleport/lib/services"
|
||||
"github.com/gravitational/teleport/lib/session"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
|
||||
"github.com/codahale/lunk"
|
||||
"github.com/gravitational/trace"
|
||||
)
|
||||
|
@ -127,9 +125,9 @@ func (a *AuthWithRoles) GenerateToken(role teleport.Role, ttl time.Duration) (st
|
|||
return a.authServer.GenerateToken(role, ttl)
|
||||
}
|
||||
}
|
||||
func (a *AuthWithRoles) RegisterUsingToken(token, hostID string, role teleport.Role) (keys PackedKeys, e error) {
|
||||
func (a *AuthWithRoles) RegisterUsingToken(token, hostID string, role teleport.Role) (*PackedKeys, error) {
|
||||
if err := a.permChecker.HasPermission(a.role, ActionRegisterUsingToken); err != nil {
|
||||
return PackedKeys{}, trace.Wrap(err)
|
||||
return nil, trace.Wrap(err)
|
||||
} else {
|
||||
return a.authServer.RegisterUsingToken(token, hostID, role)
|
||||
}
|
||||
|
@ -190,19 +188,25 @@ func (a *AuthWithRoles) GetChunkReader(id string) (recorder.ChunkReadCloser, err
|
|||
return a.recorder.GetChunkReader(id)
|
||||
}
|
||||
}
|
||||
func (a *AuthWithRoles) UpsertServer(s services.Server, ttl time.Duration) error {
|
||||
func (a *AuthWithRoles) UpsertNode(s services.Server, ttl time.Duration) error {
|
||||
if err := a.permChecker.HasPermission(a.role, ActionUpsertServer); err != nil {
|
||||
log.Error(err)
|
||||
return trace.Wrap(err)
|
||||
} else {
|
||||
return a.authServer.UpsertServer(s, ttl)
|
||||
return a.authServer.UpsertNode(s, ttl)
|
||||
}
|
||||
}
|
||||
func (a *AuthWithRoles) GetServers() ([]services.Server, error) {
|
||||
func (a *AuthWithRoles) GetNodes() ([]services.Server, error) {
|
||||
if err := a.permChecker.HasPermission(a.role, ActionGetServers); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
} else {
|
||||
return a.authServer.GetServers()
|
||||
return a.authServer.GetNodes()
|
||||
}
|
||||
}
|
||||
func (a *AuthWithRoles) UpsertAuthServer(s services.Server, ttl time.Duration) error {
|
||||
if err := a.permChecker.HasPermission(a.role, ActionUpsertAuthServer); err != nil {
|
||||
return trace.Wrap(err)
|
||||
} else {
|
||||
return a.authServer.UpsertAuthServer(s, ttl)
|
||||
}
|
||||
}
|
||||
func (a *AuthWithRoles) GetAuthServers() ([]services.Server, error) {
|
||||
|
@ -212,6 +216,20 @@ func (a *AuthWithRoles) GetAuthServers() ([]services.Server, error) {
|
|||
return a.authServer.GetAuthServers()
|
||||
}
|
||||
}
|
||||
func (a *AuthWithRoles) UpsertProxy(s services.Server, ttl time.Duration) error {
|
||||
if err := a.permChecker.HasPermission(a.role, ActionUpsertProxy); err != nil {
|
||||
return trace.Wrap(err)
|
||||
} else {
|
||||
return a.authServer.UpsertProxy(s, ttl)
|
||||
}
|
||||
}
|
||||
func (a *AuthWithRoles) GetProxies() ([]services.Server, error) {
|
||||
if err := a.permChecker.HasPermission(a.role, ActionGetProxies); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return a.authServer.GetProxies()
|
||||
}
|
||||
}
|
||||
func (a *AuthWithRoles) UpsertPassword(user string, password []byte) (hotpURL string, hotpQR []byte, err error) {
|
||||
if err := a.permChecker.HasPermission(a.role, ActionUpsertPassword); err != nil {
|
||||
return "", nil, err
|
||||
|
|
|
@ -226,7 +226,7 @@ func (c *Client) GenerateToken(role teleport.Role, ttl time.Duration) (string, e
|
|||
|
||||
// RegisterUserToken calls the auth service API to register a new node via registration token
|
||||
// which has been previously issued via GenerateToken
|
||||
func (c *Client) RegisterUsingToken(token, hostID string, role teleport.Role) (PackedKeys, error) {
|
||||
func (c *Client) RegisterUsingToken(token, hostID string, role teleport.Role) (*PackedKeys, error) {
|
||||
out, err := c.PostJSON(c.Endpoint("tokens", "register"),
|
||||
registerUsingTokenReq{
|
||||
HostID: hostID,
|
||||
|
@ -234,13 +234,13 @@ func (c *Client) RegisterUsingToken(token, hostID string, role teleport.Role) (P
|
|||
Role: role,
|
||||
})
|
||||
if err != nil {
|
||||
return PackedKeys{}, trace.Wrap(err)
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
var keys PackedKeys
|
||||
if err := json.Unmarshal(out.Bytes(), &keys); err != nil {
|
||||
return PackedKeys{}, trace.Wrap(err)
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return keys, nil
|
||||
return &keys, nil
|
||||
}
|
||||
|
||||
func (c *Client) RegisterNewAuthServer(token string) error {
|
||||
|
@ -306,20 +306,20 @@ func (c *Client) GetChunkReader(id string) (recorder.ChunkReadCloser, error) {
|
|||
return &chunkRW{c: c, id: id}, nil
|
||||
}
|
||||
|
||||
// UpsertServer is used by SSH servers to reprt their presense
|
||||
// UpsertNode is used by SSH servers to reprt their presense
|
||||
// to the auth servers in form of hearbeat expiring after ttl period.
|
||||
func (c *Client) UpsertServer(s services.Server, ttl time.Duration) error {
|
||||
func (c *Client) UpsertNode(s services.Server, ttl time.Duration) error {
|
||||
args := upsertServerReq{
|
||||
Server: s,
|
||||
TTL: ttl,
|
||||
}
|
||||
_, err := c.PostJSON(c.Endpoint("servers"), args)
|
||||
_, err := c.PostJSON(c.Endpoint("nodes"), args)
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
// GetServers returns the list of servers registered in the cluster.
|
||||
func (c *Client) GetServers() ([]services.Server, error) {
|
||||
out, err := c.Get(c.Endpoint("servers"), url.Values{})
|
||||
// GetNodes returns the list of servers registered in the cluster.
|
||||
func (c *Client) GetNodes() ([]services.Server, error) {
|
||||
out, err := c.Get(c.Endpoint("nodes"), url.Values{})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
@ -330,9 +330,44 @@ func (c *Client) GetServers() ([]services.Server, error) {
|
|||
return re, nil
|
||||
}
|
||||
|
||||
// UpsertAuthServer is used by auth servers to report their presense
|
||||
// to other auth servers in form of hearbeat expiring after ttl period.
|
||||
func (c *Client) UpsertAuthServer(s services.Server, ttl time.Duration) error {
|
||||
args := upsertServerReq{
|
||||
Server: s,
|
||||
TTL: ttl,
|
||||
}
|
||||
_, err := c.PostJSON(c.Endpoint("authservers"), args)
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
// GetAuthServers returns the list of auth servers registered in the cluster.
|
||||
func (c *Client) GetAuthServers() ([]services.Server, error) {
|
||||
out, err := c.Get(c.Endpoint("auth", "servers"), url.Values{})
|
||||
out, err := c.Get(c.Endpoint("authservers"), url.Values{})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
var re []services.Server
|
||||
if err := json.Unmarshal(out.Bytes(), &re); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return re, nil
|
||||
}
|
||||
|
||||
// UpsertProxy is used by proxies to report their presense
|
||||
// to other auth servers in form of hearbeat expiring after ttl period.
|
||||
func (c *Client) UpsertProxy(s services.Server, ttl time.Duration) error {
|
||||
args := upsertServerReq{
|
||||
Server: s,
|
||||
TTL: ttl,
|
||||
}
|
||||
_, err := c.PostJSON(c.Endpoint("proxies"), args)
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
// GetProxies returns the list of auth servers registered in the cluster.
|
||||
func (c *Client) GetProxies() ([]services.Server, error) {
|
||||
out, err := c.Get(c.Endpoint("proxies"), url.Values{})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
@ -640,7 +675,7 @@ type ClientI interface {
|
|||
GetCertAuthorities(caType services.CertAuthType) ([]*services.CertAuthority, error)
|
||||
DeleteCertAuthority(caType services.CertAuthID) error
|
||||
GenerateToken(role teleport.Role, ttl time.Duration) (string, error)
|
||||
RegisterUsingToken(token, hostID string, role teleport.Role) (keys PackedKeys, e error)
|
||||
RegisterUsingToken(token, hostID string, role teleport.Role) (*PackedKeys, error)
|
||||
RegisterNewAuthServer(token string) error
|
||||
Log(id lunk.EventID, e lunk.Event)
|
||||
LogEntry(en lunk.Entry) error
|
||||
|
@ -649,8 +684,8 @@ type ClientI interface {
|
|||
GetSessionEvents(filter events.Filter) ([]session.Session, error)
|
||||
GetChunkWriter(id string) (recorder.ChunkWriteCloser, error)
|
||||
GetChunkReader(id string) (recorder.ChunkReadCloser, error)
|
||||
UpsertServer(s services.Server, ttl time.Duration) error
|
||||
GetServers() ([]services.Server, error)
|
||||
UpsertNode(s services.Server, ttl time.Duration) error
|
||||
GetNodes() ([]services.Server, error)
|
||||
GetAuthServers() ([]services.Server, error)
|
||||
UpsertPassword(user string, password []byte) (hotpURL string, hotpQR []byte, err error)
|
||||
CheckPassword(user string, password []byte, hotpToken string) error
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
)
|
||||
|
||||
type limitedClient interface {
|
||||
GetServers() ([]services.Server, error)
|
||||
GetNodes() ([]services.Server, error)
|
||||
GetCertAuthorities(caType services.CertAuthType) ([]*services.CertAuthority, error)
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ func RetryingClient(client limitedClient, retries int) *retryingClient {
|
|||
func (c *retryingClient) GetServers() ([]services.Server, error) {
|
||||
var e error
|
||||
for i := 0; i < c.retries; i++ {
|
||||
servers, err := c.limitedClient.GetServers()
|
||||
servers, err := c.limitedClient.GetNodes()
|
||||
if err == nil {
|
||||
return servers, nil
|
||||
}
|
||||
|
|
|
@ -33,10 +33,14 @@ import (
|
|||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// InitConfig is auth server init config
|
||||
type InitConfig struct {
|
||||
Backend backend.Backend
|
||||
Authority Authority
|
||||
|
||||
// HostUUID is a UUID of this host
|
||||
HostUUID string
|
||||
|
||||
// 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
|
||||
DomainName string
|
||||
|
@ -59,9 +63,13 @@ type InitConfig struct {
|
|||
}
|
||||
|
||||
// Init instantiates and configures an instance of AuthServer
|
||||
func Init(cfg InitConfig) (*AuthServer, ssh.Signer, error) {
|
||||
func Init(cfg InitConfig) (*AuthServer, *Identity, error) {
|
||||
if cfg.DataDir == "" {
|
||||
return nil, nil, fmt.Errorf("path can not be empty")
|
||||
return nil, nil, trace.Wrap(teleport.BadParameter("data_dir", "data dir can not be empty"))
|
||||
}
|
||||
|
||||
if cfg.HostUUID == "" {
|
||||
return nil, nil, trace.Wrap(teleport.BadParameter("HostUUID", "host UUID can not be empty"))
|
||||
}
|
||||
|
||||
err := os.MkdirAll(cfg.DataDir, os.ModeDir|0777)
|
||||
|
@ -152,17 +160,17 @@ func Init(cfg InitConfig) (*AuthServer, ssh.Signer, error) {
|
|||
}
|
||||
}
|
||||
|
||||
signer, err := InitKeys(asrv, cfg.DataDir)
|
||||
identity, err := initKeys(asrv, cfg.DataDir, IdentityID{HostUUID: cfg.HostUUID, Role: teleport.RoleAdmin})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return asrv, signer, nil
|
||||
return asrv, identity, nil
|
||||
}
|
||||
|
||||
// InitKeys initializes this node's host certificate signed by host authority
|
||||
func InitKeys(a *AuthServer, dataDir string) (ssh.Signer, error) {
|
||||
kp, cp := keysPath(dataDir)
|
||||
// initKeys initializes this node's host certificate signed by host authority
|
||||
func initKeys(a *AuthServer, dataDir string, id IdentityID) (*Identity, error) {
|
||||
kp, cp := keysPath(dataDir, id)
|
||||
|
||||
keyExists, err := pathExists(kp)
|
||||
if err != nil {
|
||||
|
@ -175,29 +183,29 @@ func InitKeys(a *AuthServer, dataDir string) (ssh.Signer, error) {
|
|||
}
|
||||
|
||||
if !keyExists || !certExists {
|
||||
k, pub, err := a.GenerateKeyPair("")
|
||||
privateKey, publicKey, err := a.GenerateKeyPair("")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
c, err := a.GenerateHostCert(pub, a.DomainName, a.DomainName, teleport.RoleAdmin, 0)
|
||||
cert, err := a.GenerateHostCert(publicKey, id.HostUUID, a.DomainName, id.Role, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if err := writeKeys(dataDir, k, c); err != nil {
|
||||
return nil, err
|
||||
if err := writeKeys(dataDir, id, privateKey, cert); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
}
|
||||
i, err := ReadIdentity(dataDir)
|
||||
i, err := ReadIdentity(dataDir, id)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return i.KeySigner, nil
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// writeKeys saves the key/cert pair for a given domain onto disk. This usually means the
|
||||
// domain trusts us (signed our public key)
|
||||
func writeKeys(dataDir string, key []byte, cert []byte) error {
|
||||
kp, cp := keysPath(dataDir)
|
||||
func writeKeys(dataDir string, id IdentityID, key []byte, cert []byte) error {
|
||||
kp, cp := keysPath(dataDir, id)
|
||||
log.Debugf("write key to %v, cert from %v", kp, cp)
|
||||
|
||||
if err := ioutil.WriteFile(kp, key, 0600); err != nil {
|
||||
|
@ -209,6 +217,7 @@ func writeKeys(dataDir string, key []byte, cert []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Identity is a collection of certificates and signers that represent identity
|
||||
type Identity struct {
|
||||
KeyBytes []byte
|
||||
CertBytes []byte
|
||||
|
@ -217,13 +226,19 @@ type Identity struct {
|
|||
Cert *ssh.Certificate
|
||||
}
|
||||
|
||||
// IdentityID is a combination of role and host UUID
|
||||
type IdentityID struct {
|
||||
Role teleport.Role
|
||||
HostUUID string
|
||||
}
|
||||
|
||||
// ReadIdentity reads, parses and returns the given pub/pri key + cert from the
|
||||
// key storage (dataDir).
|
||||
func ReadIdentity(dataDir string) (i *Identity, err error) {
|
||||
kp, cp := keysPath(dataDir)
|
||||
func ReadIdentity(dataDir string, id IdentityID) (i *Identity, err error) {
|
||||
kp, cp := keysPath(dataDir, id)
|
||||
log.Debugf("host identity: [key: %v, cert: %v]", kp, cp)
|
||||
|
||||
i = new(Identity)
|
||||
i = &Identity{}
|
||||
|
||||
i.KeyBytes, err = utils.ReadPath(kp)
|
||||
if err != nil {
|
||||
|
@ -260,8 +275,8 @@ func ReadIdentity(dataDir string) (i *Identity, err error) {
|
|||
}
|
||||
|
||||
// HaveHostKeys checks either the host keys are in place
|
||||
func HaveHostKeys(dataDir string) (bool, error) {
|
||||
kp, cp := keysPath(dataDir)
|
||||
func HaveHostKeys(dataDir string, id IdentityID) (bool, error) {
|
||||
kp, cp := keysPath(dataDir, id)
|
||||
|
||||
exists, err := pathExists(kp)
|
||||
if !exists || err != nil {
|
||||
|
@ -277,9 +292,9 @@ func HaveHostKeys(dataDir string) (bool, error) {
|
|||
}
|
||||
|
||||
// keysPath returns two full file paths: to the host.key and host.cert
|
||||
func keysPath(dataDir string) (key string, cert string) {
|
||||
return filepath.Join(dataDir, "host.key"),
|
||||
filepath.Join(dataDir, "host.cert")
|
||||
func keysPath(dataDir string, id IdentityID) (key string, cert string) {
|
||||
return filepath.Join(dataDir, fmt.Sprintf("host.%v.%v.key", id.HostUUID, string(id.Role))),
|
||||
filepath.Join(dataDir, fmt.Sprintf("host.%v.%v.cert", id.HostUUID, string(id.Role)))
|
||||
}
|
||||
|
||||
func pathExists(path string) (bool, error) {
|
||||
|
|
|
@ -38,6 +38,11 @@ func NewStandardPermissions() PermissionChecker {
|
|||
sp := standardPermissions{}
|
||||
sp.permissions = make(map[teleport.Role](map[string]bool))
|
||||
|
||||
sp.permissions[teleport.RoleAuth] = map[string]bool{
|
||||
ActionUpsertAuthServer: true,
|
||||
ActionGetAuthServers: true,
|
||||
}
|
||||
|
||||
sp.permissions[teleport.RoleUser] = map[string]bool{
|
||||
ActionSignIn: true,
|
||||
ActionCreateWebSession: true,
|
||||
|
@ -55,11 +60,13 @@ func NewStandardPermissions() PermissionChecker {
|
|||
|
||||
sp.permissions[teleport.RoleNode] = map[string]bool{
|
||||
ActionUpsertServer: true,
|
||||
ActionGetServers: true,
|
||||
ActionGetProxies: true,
|
||||
ActionGetAuthServers: true,
|
||||
ActionGetCertAuthorities: true,
|
||||
ActionGetUsers: true,
|
||||
ActionGetLocalDomain: true,
|
||||
ActionGetUserKeys: true,
|
||||
ActionGetServers: true,
|
||||
ActionUpsertParty: true,
|
||||
ActionUpsertSession: true,
|
||||
ActionLogEntry: true,
|
||||
|
@ -68,6 +75,20 @@ func NewStandardPermissions() PermissionChecker {
|
|||
ActionGetSessions: true,
|
||||
}
|
||||
|
||||
sp.permissions[teleport.RoleProxy] = map[string]bool{
|
||||
ActionGetServers: true,
|
||||
ActionUpsertProxy: true,
|
||||
ActionGetProxies: true,
|
||||
ActionGetAuthServers: true,
|
||||
ActionGetCertAuthorities: true,
|
||||
ActionGetUsers: true,
|
||||
ActionGetLocalDomain: true,
|
||||
ActionGetUserKeys: true,
|
||||
ActionLogEntry: true,
|
||||
ActionGetSession: true,
|
||||
ActionGetSessions: true,
|
||||
}
|
||||
|
||||
sp.permissions[teleport.RoleWeb] = map[string]bool{
|
||||
ActionUpsertSession: true,
|
||||
ActionCreateWebSession: true,
|
||||
|
@ -86,54 +107,6 @@ func NewStandardPermissions() PermissionChecker {
|
|||
return &sp
|
||||
}
|
||||
|
||||
// NewHangoutPermissions is a set of permissions allowed to various
|
||||
// roles when auth server is started in hangout mode on user's computer
|
||||
func NewHangoutPermissions() PermissionChecker {
|
||||
sp := standardPermissions{}
|
||||
sp.permissions = make(map[teleport.Role](map[string]bool))
|
||||
|
||||
sp.permissions[teleport.RoleUser] = map[string]bool{
|
||||
ActionGenerateUserCert: true,
|
||||
ActionGetCertAuthorities: true,
|
||||
}
|
||||
|
||||
sp.permissions[teleport.RoleProvisionToken] = map[string]bool{
|
||||
ActionRegisterUsingToken: true,
|
||||
ActionRegisterNewAuthServer: true,
|
||||
ActionGenerateUserCert: true,
|
||||
}
|
||||
|
||||
sp.permissions[teleport.RoleHangoutRemoteUser] = map[string]bool{
|
||||
ActionGenerateUserCert: true,
|
||||
}
|
||||
|
||||
sp.permissions[teleport.RoleNode] = map[string]bool{
|
||||
ActionUpsertServer: true,
|
||||
ActionGetCertAuthorities: true,
|
||||
ActionGetLocalDomain: true,
|
||||
ActionGetUserKeys: true,
|
||||
ActionGetServers: true,
|
||||
ActionUpsertParty: true,
|
||||
ActionLogEntry: true,
|
||||
ActionGetChunkWriter: true,
|
||||
ActionUpsertCertAuthority: true,
|
||||
ActionUpsertSession: true,
|
||||
ActionGetAuthServers: true,
|
||||
}
|
||||
|
||||
sp.permissions[teleport.RoleWeb] = map[string]bool{
|
||||
ActionGetWebSession: true,
|
||||
ActionDeleteWebSession: true,
|
||||
}
|
||||
|
||||
sp.permissions[teleport.RoleSignup] = map[string]bool{
|
||||
ActionGetSignupTokenData: true,
|
||||
ActionCreateUserWithToken: true,
|
||||
}
|
||||
|
||||
return &sp
|
||||
}
|
||||
|
||||
type standardPermissions struct {
|
||||
permissions map[teleport.Role](map[string]bool)
|
||||
}
|
||||
|
@ -174,17 +147,12 @@ var StandardRoles = []teleport.Role{
|
|||
teleport.RoleUser,
|
||||
teleport.RoleWeb,
|
||||
teleport.RoleNode,
|
||||
teleport.RoleProxy,
|
||||
teleport.RoleAdmin,
|
||||
teleport.RoleProvisionToken,
|
||||
teleport.RoleSignup,
|
||||
}
|
||||
|
||||
var HangoutRoles = []teleport.Role{
|
||||
teleport.RoleAdmin,
|
||||
teleport.RoleProvisionToken,
|
||||
teleport.RoleHangoutRemoteUser,
|
||||
}
|
||||
|
||||
const (
|
||||
ActionGetSessions = "GetSessions"
|
||||
ActionGetSession = "GetSession"
|
||||
|
@ -205,7 +173,10 @@ const (
|
|||
ActionGetChunkReader = "GetChunkReader"
|
||||
ActionUpsertServer = "UpsertServer"
|
||||
ActionGetServers = "GetServers"
|
||||
ActionUpsertAuthServer = "UpsertAuthServer"
|
||||
ActionGetAuthServers = "GetAuthServers"
|
||||
ActionUpsertProxy = "UpsertProxy"
|
||||
ActionGetProxies = "GetProxies"
|
||||
ActionUpsertPassword = "UpsertPassword"
|
||||
ActionCheckPassword = "CheckPassword"
|
||||
ActionSignIn = "SignIn"
|
||||
|
|
|
@ -20,42 +20,50 @@ import (
|
|||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/gravitational/teleport"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
)
|
||||
|
||||
// LocalRegister is used in standalone mode to register roles without
|
||||
// connecting to remote clients and provisioning tokens
|
||||
func LocalRegister(dataDir string, id IdentityID, authServer *AuthServer) error {
|
||||
keys, err := authServer.GenerateServerKeys(id.HostUUID, id.Role)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
return writeKeys(dataDir, id, keys.Key, keys.Cert)
|
||||
}
|
||||
|
||||
// Register is used by auth service clients (other services, like proxy or SSH) when a new node
|
||||
// joins the cluster
|
||||
func Register(hostUUID, dataDir, token string, role teleport.Role, servers []utils.NetAddr) error {
|
||||
func Register(dataDir, token string, id IdentityID, servers []utils.NetAddr) error {
|
||||
tok, err := readToken(token)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
method, err := NewTokenAuth(hostUUID, tok)
|
||||
method, err := NewTokenAuth(id.HostUUID, tok)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
client, err := NewTunClient(
|
||||
servers[0],
|
||||
hostUUID,
|
||||
id.HostUUID,
|
||||
method)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
keys, err := client.RegisterUsingToken(tok, hostUUID, role)
|
||||
keys, err := client.RegisterUsingToken(tok, id.HostUUID, id.Role)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
return writeKeys(dataDir, keys.Key, keys.Cert)
|
||||
return writeKeys(dataDir, id, keys.Key, keys.Cert)
|
||||
}
|
||||
|
||||
func RegisterNewAuth(domainName, token string, servers []utils.NetAddr) error {
|
||||
|
||||
tok, err := readToken(token)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
|
|
|
@ -316,22 +316,6 @@ func (s *AuthTunnel) keyAuth(
|
|||
return nil, trace.Errorf("ERROR: Server doesn't support provided key type")
|
||||
}
|
||||
|
||||
if cert.CertType == ssh.UserCert {
|
||||
_, err := s.userCertChecker.Authenticate(conn, key)
|
||||
if err != nil {
|
||||
log.Warningf("conn(%v->%v, user=%v) ERROR: Failed to authorize user %v, err: %v",
|
||||
conn.RemoteAddr(), conn.LocalAddr(), conn.User(), conn.User(), err)
|
||||
return nil, err
|
||||
}
|
||||
perms := &ssh.Permissions{
|
||||
Extensions: map[string]string{
|
||||
ExtHost: conn.User(),
|
||||
ExtRole: string(teleport.RoleHangoutRemoteUser),
|
||||
},
|
||||
}
|
||||
return perms, nil
|
||||
}
|
||||
|
||||
err := s.hostCertChecker.CheckHostKey(conn.User(), conn.RemoteAddr(), key)
|
||||
if err != nil {
|
||||
log.Warningf("conn(%v->%v, user=%v) ERROR: failed auth user %v, err: %v",
|
||||
|
@ -584,7 +568,7 @@ func (t *TunDialer) getClient() (*ssh.Client, error) {
|
|||
return nil, teleport.AccessDenied(
|
||||
fmt.Sprintf("access denied to '%v': bad username or credentials", t.user))
|
||||
}
|
||||
return nil, trace.Wrap(err)
|
||||
return nil, trace.Wrap(teleport.ConvertSystemError(err))
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
|
|
@ -79,10 +79,13 @@ func (s *TunSuite) SetUpTest(c *C) {
|
|||
Authority: authority.New(),
|
||||
DomainName: "localhost",
|
||||
})
|
||||
s.srv = NewAPIWithRoles(s.a, s.bl, sessionServer, s.rec,
|
||||
NewStandardPermissions(),
|
||||
StandardRoles,
|
||||
)
|
||||
s.srv = NewAPIWithRoles(APIConfig{
|
||||
AuthServer: s.a,
|
||||
EventLog: s.bl,
|
||||
SessionService: sessionServer,
|
||||
Recorder: s.rec,
|
||||
PermissionChecker: NewStandardPermissions(),
|
||||
Roles: StandardRoles})
|
||||
go s.srv.Serve()
|
||||
|
||||
// set up host private key and certificate
|
||||
|
@ -111,10 +114,13 @@ func (s *TunSuite) SetUpTest(c *C) {
|
|||
func (s *TunSuite) TestUnixServerClient(c *C) {
|
||||
sessionServer, err := session.New(s.bk)
|
||||
c.Assert(err, IsNil)
|
||||
srv := NewAPIWithRoles(s.a, s.bl, sessionServer, s.rec,
|
||||
NewAllowAllPermissions(),
|
||||
StandardRoles,
|
||||
)
|
||||
srv := NewAPIWithRoles(APIConfig{
|
||||
AuthServer: s.a,
|
||||
EventLog: s.bl,
|
||||
SessionService: sessionServer,
|
||||
Recorder: s.rec,
|
||||
PermissionChecker: NewAllowAllPermissions(),
|
||||
Roles: StandardRoles})
|
||||
go srv.Serve()
|
||||
|
||||
tsrv, err := NewTunnel(
|
||||
|
@ -145,7 +151,7 @@ func (s *TunSuite) TestUnixServerClient(c *C) {
|
|||
"test", authMethod)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
err = clt.UpsertServer(
|
||||
err = clt.UpsertNode(
|
||||
services.Server{ID: "a.example.com", Addr: "hello", Hostname: "hello"}, 0)
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
@ -343,7 +349,7 @@ func (s *TunSuite) TestPermissions(c *C) {
|
|||
c.Assert(ws, Not(Equals), "")
|
||||
|
||||
// Requesting forbidded for User action
|
||||
_, err = clt.GetServers()
|
||||
_, err = clt.GetNodes()
|
||||
c.Assert(err, NotNil)
|
||||
|
||||
// Requesting forbidded for User action
|
||||
|
@ -360,7 +366,7 @@ func (s *TunSuite) TestPermissions(c *C) {
|
|||
defer cltw.Close()
|
||||
|
||||
// Requesting forbidden for Web action
|
||||
_, err = cltw.GetServers()
|
||||
_, err = cltw.GetNodes()
|
||||
c.Assert(err, NotNil)
|
||||
|
||||
// Requesting forbidden for Web action
|
||||
|
|
|
@ -147,6 +147,7 @@ func (s *ConfigTestSuite) TestConfigReading(c *check.C) {
|
|||
c.Assert(conf.Proxy.Configured(), check.Equals, false) // Missing "proxy_service" section must lead to 'not configured'
|
||||
c.Assert(conf.Proxy.Enabled(), check.Equals, true) // Missing "proxy_service" section must lead to 'true'
|
||||
c.Assert(conf.Proxy.Disabled(), check.Equals, false) // Missing "proxy_service" does NOT mean it's been disabled
|
||||
c.Assert(conf.AdvertiseIP.String(), check.Equals, "10.10.10.1")
|
||||
|
||||
c.Assert(conf.Limits.MaxConnections, check.Equals, int64(90))
|
||||
c.Assert(conf.Limits.MaxUsers, check.Equals, 91)
|
||||
|
@ -167,7 +168,7 @@ func (s *ConfigTestSuite) TestConfigReading(c *check.C) {
|
|||
c.Assert(conf.SSH.Commands[1].Name, check.Equals, "date")
|
||||
c.Assert(conf.SSH.Commands[1].Command, check.DeepEquals, []string{"/bin/date"})
|
||||
c.Assert(conf.SSH.Commands[1].Period.Nanoseconds(), check.Equals, int64(20000000))
|
||||
c.Assert(conf.SSH.AdvertiseIP.String(), check.Equals, "10.10.10.1")
|
||||
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -246,6 +247,7 @@ const (
|
|||
#
|
||||
teleport:
|
||||
nodename: edsger.example.com
|
||||
advertise_ip: 10.10.10.1
|
||||
auth_servers:
|
||||
- tcp://auth0.server.example.org:3024
|
||||
- tcp://auth1.server.example.org:3024
|
||||
|
@ -274,7 +276,6 @@ auth_service:
|
|||
ssh_service:
|
||||
enabled: no
|
||||
listen_addr: tcp://ssh
|
||||
advertise_ip: 10.10.10.1
|
||||
labels:
|
||||
name: mondoserver
|
||||
role: slave
|
||||
|
|
|
@ -41,6 +41,7 @@ var (
|
|||
"auth_service": true,
|
||||
"auth_token": true,
|
||||
"auth_servers": true,
|
||||
"domain_name": true,
|
||||
"storage": true,
|
||||
"nodename": true,
|
||||
"log": true,
|
||||
|
@ -190,6 +191,46 @@ func MakeSampleFileConfig() (fc *FileConfig) {
|
|||
return fc
|
||||
}
|
||||
|
||||
// MakeAuthPeerFileConfig returns a sample configuration for auth
|
||||
// server peer that shares etcd backend
|
||||
func MakeAuthPeerFileConfig(domainName string, token string) (fc *FileConfig) {
|
||||
conf := service.MakeDefaultConfig()
|
||||
|
||||
// sample global config:
|
||||
var g Global
|
||||
g.NodeName = conf.Hostname
|
||||
g.AuthToken = token
|
||||
g.Logger.Output = "stderr"
|
||||
g.Logger.Severity = "INFO"
|
||||
g.AuthServers = []string{"<insert auth server peer address here>"}
|
||||
g.Limits.MaxConnections = defaults.LimiterMaxConnections
|
||||
g.Limits.MaxUsers = defaults.LimiterMaxConcurrentUsers
|
||||
g.Storage.DirName = defaults.DataDir
|
||||
g.Storage.Type = teleport.ETCDBackendType
|
||||
g.Storage.Prefix = defaults.ETCDPrefix
|
||||
g.Storage.Peers = []string{"insert ETCD peers addresses here"}
|
||||
|
||||
// sample Auth config:
|
||||
var a Auth
|
||||
a.ListenAddress = conf.Auth.SSHAddr.Addr
|
||||
a.EnabledFlag = "yes"
|
||||
a.DomainName = domainName
|
||||
|
||||
var p Proxy
|
||||
p.EnabledFlag = "no"
|
||||
|
||||
var s SSH
|
||||
s.EnabledFlag = "no"
|
||||
|
||||
fc = &FileConfig{
|
||||
Global: g,
|
||||
Auth: a,
|
||||
Proxy: p,
|
||||
SSH: s,
|
||||
}
|
||||
return fc
|
||||
}
|
||||
|
||||
// DebugDumpToYAML allows for quick YAML dumping of the config
|
||||
func (conf *FileConfig) DebugDumpToYAML() string {
|
||||
bytes, err := yaml.Marshal(&conf)
|
||||
|
@ -239,6 +280,7 @@ type Global struct {
|
|||
Limits ConnectionLimits `yaml:"connection_limits,omitempty"`
|
||||
Logger Log `yaml:"log,omitempty"`
|
||||
Storage StorageBackend `yaml:"storage,omitempty"`
|
||||
AdvertiseIP net.IP `yaml:"advertise_ip,omitempty"`
|
||||
}
|
||||
|
||||
// Service is a common configuration of a teleport service
|
||||
|
@ -266,27 +308,29 @@ func (s *Service) Disabled() bool {
|
|||
return s.Configured() && !s.Enabled()
|
||||
}
|
||||
|
||||
// 'auth_service' section of the config file
|
||||
// Auth is 'auth_service' section of the config file
|
||||
type Auth struct {
|
||||
Service `yaml:",inline"`
|
||||
// DomainName is the name of the certificate authority
|
||||
// managed by this domain
|
||||
DomainName string `yaml:"domain_name,omitempty"`
|
||||
}
|
||||
|
||||
// 'ssh_service' section of the config file
|
||||
// SSH is 'ssh_service' section of the config file
|
||||
type SSH struct {
|
||||
Service `yaml:",inline"`
|
||||
Labels map[string]string `yaml:"labels,omitempty"`
|
||||
Commands []CommandLabel `yaml:"commands,omitempty"`
|
||||
AdvertiseIP net.IP `yaml:"advertise_ip,omitempty"`
|
||||
Service `yaml:",inline"`
|
||||
Labels map[string]string `yaml:"labels,omitempty"`
|
||||
Commands []CommandLabel `yaml:"commands,omitempty"`
|
||||
}
|
||||
|
||||
// `command` section of `ssh_service` in the config file
|
||||
// CommandLabel is `command` section of `ssh_service` in the config file
|
||||
type CommandLabel struct {
|
||||
Name string `yaml:"name"`
|
||||
Command []string `yaml:"command,flow"`
|
||||
Period time.Duration `yaml:"period"`
|
||||
}
|
||||
|
||||
// `proxy_service` section of the config file:
|
||||
// Proxy is `proxy_service` section of the config file:
|
||||
type Proxy struct {
|
||||
Service `yaml:",inline"`
|
||||
WebAddr string `yaml:"web_listen_addr,omitempty"`
|
||||
|
|
|
@ -115,6 +115,9 @@ var (
|
|||
|
||||
// StartRoles is default roles teleport assumes when started via 'start' command
|
||||
StartRoles = []string{RoleProxy, RoleNode, RoleAuthService}
|
||||
|
||||
// ETCDPrefix is default key in ETCD clustered configurations
|
||||
ETCDPrefix = "/teleport"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -756,7 +756,7 @@ func (s *tunnelSite) DialServer(addr string) (net.Conn, error) {
|
|||
}
|
||||
var knownServers []services.Server
|
||||
for i := 0; i < 10; i++ {
|
||||
knownServers, err = clt.GetServers()
|
||||
knownServers, err = clt.GetNodes()
|
||||
if err != nil {
|
||||
log.Infof("failed to get servers: %v", err)
|
||||
time.Sleep(time.Second)
|
||||
|
|
|
@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
|
@ -44,6 +45,10 @@ type Config struct {
|
|||
|
||||
AuthServers NetAddrSlice
|
||||
|
||||
// AdvertiseIP is used to "publish" an alternative IP address this node
|
||||
// can be reached on, if running behind NAT
|
||||
AdvertiseIP net.IP
|
||||
|
||||
// SSH role an SSH endpoint server
|
||||
SSH SSHConfig
|
||||
|
||||
|
@ -184,6 +189,12 @@ type AuthConfig struct {
|
|||
// TrustedAuthorities is a set of trusted user certificate authorities
|
||||
TrustedAuthorities CertificateAuthorities
|
||||
|
||||
// DomainName is a name that identifies this authority and all
|
||||
// host nodes in the cluster that will share this authority domain name
|
||||
// as a base name, e.g. if authority domain name is example.com,
|
||||
// all nodes in the cluster will have UUIDs in the form: <uuid>.example.com
|
||||
DomainName string
|
||||
|
||||
// UserCA allows to pass preconfigured user certificate authority keypair
|
||||
// to auth server so it will use it on the first start instead of generating
|
||||
// a new keypair
|
||||
|
@ -225,16 +236,13 @@ type AuthConfig struct {
|
|||
|
||||
// SSHConfig configures SSH server node role
|
||||
type SSHConfig struct {
|
||||
Enabled bool
|
||||
Token string
|
||||
Addr utils.NetAddr
|
||||
// AdvertiseIP is used to "publish" an alternative IP address this node
|
||||
// can be reached on, if running behind NAT
|
||||
AdvertiseIP net.IP
|
||||
Shell string
|
||||
Limiter limiter.LimiterConfig
|
||||
Labels map[string]string
|
||||
CmdLabels services.CommandLabels
|
||||
Enabled bool
|
||||
Token string
|
||||
Addr utils.NetAddr
|
||||
Shell string
|
||||
Limiter limiter.LimiterConfig
|
||||
Labels map[string]string
|
||||
CmdLabels services.CommandLabels
|
||||
}
|
||||
|
||||
// ReverseTunnelConfig configures reverse tunnel role
|
||||
|
|
|
@ -20,8 +20,10 @@ package service
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
@ -40,6 +42,7 @@ import (
|
|||
"github.com/gravitational/teleport/lib/recorder"
|
||||
"github.com/gravitational/teleport/lib/recorder/boltrec"
|
||||
"github.com/gravitational/teleport/lib/reversetunnel"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
"github.com/gravitational/teleport/lib/session"
|
||||
"github.com/gravitational/teleport/lib/srv"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
|
@ -60,58 +63,50 @@ type RoleConfig struct {
|
|||
Console io.Writer
|
||||
}
|
||||
|
||||
// connector has all resources process needs to connect
|
||||
// to other parts of the cluster: client and identity
|
||||
type connector struct {
|
||||
identity *auth.Identity
|
||||
client *auth.TunClient
|
||||
}
|
||||
|
||||
// TeleportProcess structure holds the state of the Teleport daemon, controlling
|
||||
// execution and configuration of the teleport services: ssh, auth and proxy.
|
||||
type TeleportProcess struct {
|
||||
sync.Mutex
|
||||
|
||||
Supervisor
|
||||
Config *Config
|
||||
Identity *auth.Identity
|
||||
AuthClient *auth.TunClient
|
||||
Config *Config
|
||||
// localAuth has local auth server listed in case if this process
|
||||
// has started with auth server role enabled
|
||||
localAuth *auth.AuthServer
|
||||
}
|
||||
|
||||
// loginIntoAuthService attempts to login into the auth servers specified in the
|
||||
// configuration. Returns 'true' if successful
|
||||
func (process *TeleportProcess) loginIntoAuthService() bool {
|
||||
process.Lock()
|
||||
defer process.Unlock()
|
||||
|
||||
// already logged in?
|
||||
if process.AuthClient != nil {
|
||||
return true
|
||||
func (process *TeleportProcess) connectToAuthService(role teleport.Role) (*connector, error) {
|
||||
identity, err := auth.ReadIdentity(
|
||||
process.Config.DataDir, auth.IdentityID{HostUUID: process.Config.HostUUID, Role: role})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
identity, err := auth.ReadIdentity(process.Config.DataDir)
|
||||
if identity != nil && err == nil {
|
||||
authUser := identity.Cert.ValidPrincipals[0]
|
||||
// try all auth servers in the list:
|
||||
for _, authServerAddr := range process.Config.AuthServers {
|
||||
authClient, err := auth.NewTunClient(
|
||||
authServerAddr,
|
||||
authUser,
|
||||
[]ssh.AuthMethod{ssh.PublicKeys(identity.KeySigner)})
|
||||
// success?
|
||||
if err != nil {
|
||||
log.Warning(err)
|
||||
continue
|
||||
}
|
||||
// try calling a test method via auth api:
|
||||
_, err = authClient.GetLocalDomain()
|
||||
if err != nil {
|
||||
log.Warning(err)
|
||||
continue
|
||||
}
|
||||
// success ? we're logged in!
|
||||
log.Infof("%s logged into %v", authUser, authServerAddr)
|
||||
process.AuthClient = authClient
|
||||
process.Identity = identity
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
log.Warn("no host identity has been found")
|
||||
authUser := identity.Cert.ValidPrincipals[0]
|
||||
authClient, err := auth.NewTunClient(
|
||||
process.Config.AuthServers[0],
|
||||
authUser,
|
||||
[]ssh.AuthMethod{ssh.PublicKeys(identity.KeySigner)})
|
||||
// success?
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return false
|
||||
// try calling a test method via auth api:
|
||||
_, err = authClient.GetLocalDomain()
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
// success ? we're logged in!
|
||||
log.Infof("%s connected to the cluster", authUser)
|
||||
return &connector{client: authClient, identity: identity}, nil
|
||||
}
|
||||
|
||||
// NewTeleport takes the daemon configuration, instantiates all required services
|
||||
|
@ -142,22 +137,24 @@ func NewTeleport(cfg *Config) (Supervisor, error) {
|
|||
cfg.AuthServers = []utils.NetAddr{cfg.Auth.SSHAddr}
|
||||
}
|
||||
|
||||
// if user did not provide auth domain name, use this host UUID
|
||||
if cfg.Auth.Enabled && cfg.Auth.DomainName == "" {
|
||||
cfg.Auth.DomainName = cfg.HostUUID
|
||||
}
|
||||
|
||||
// try to login into the auth service:
|
||||
|
||||
// if there are no certificates, use self signed
|
||||
process := TeleportProcess{
|
||||
process := &TeleportProcess{
|
||||
Supervisor: NewSupervisor(),
|
||||
Config: cfg,
|
||||
Identity: nil,
|
||||
AuthClient: nil,
|
||||
}
|
||||
process.loginIntoAuthService()
|
||||
|
||||
serviceStarted := false
|
||||
|
||||
if cfg.Auth.Enabled {
|
||||
if err := process.InitAuthService(); err != nil {
|
||||
return nil, err
|
||||
if err := process.initAuthService(); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
serviceStarted = true
|
||||
}
|
||||
|
@ -190,8 +187,20 @@ func NewTeleport(cfg *Config) (Supervisor, error) {
|
|||
return process, nil
|
||||
}
|
||||
|
||||
// InitAuthService can be called to initialize auth server service
|
||||
func (process *TeleportProcess) InitAuthService() error {
|
||||
func (process *TeleportProcess) setLocalAuth(a *auth.AuthServer) {
|
||||
process.Lock()
|
||||
defer process.Unlock()
|
||||
process.localAuth = a
|
||||
}
|
||||
|
||||
func (process *TeleportProcess) getLocalAuth() *auth.AuthServer {
|
||||
process.Lock()
|
||||
defer process.Unlock()
|
||||
return process.localAuth
|
||||
}
|
||||
|
||||
// initAuthService can be called to initialize auth server service
|
||||
func (process *TeleportProcess) initAuthService() error {
|
||||
cfg := process.Config
|
||||
// Initialize the storage back-ends for keys, events and records
|
||||
b, err := process.initAuthStorage()
|
||||
|
@ -208,30 +217,39 @@ func (process *TeleportProcess) InitAuthService() error {
|
|||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
// configure the auth service:
|
||||
|
||||
acfg := auth.InitConfig{
|
||||
Backend: b,
|
||||
Authority: authority.New(),
|
||||
DomainName: cfg.HostUUID,
|
||||
DomainName: cfg.Auth.DomainName,
|
||||
AuthServiceName: cfg.Hostname,
|
||||
DataDir: cfg.DataDir,
|
||||
SecretKey: cfg.Auth.SecretKey,
|
||||
AllowedTokens: cfg.Auth.AllowedTokens,
|
||||
HostUUID: cfg.HostUUID,
|
||||
}
|
||||
asrv, signer, err := auth.Init(acfg)
|
||||
authServer, identity, err := auth.Init(acfg)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
sessionService, err := session.New(b)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
// set local auth to use from the same process (to simplify setup
|
||||
// if there are some other roles started in the same process)
|
||||
process.setLocalAuth(authServer)
|
||||
|
||||
sess, err := session.New(b)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
apisrv := auth.NewAPIWithRoles(asrv, elog, sess, rec,
|
||||
auth.NewStandardPermissions(), auth.StandardRoles,
|
||||
)
|
||||
apiServer := auth.NewAPIWithRoles(auth.APIConfig{
|
||||
AuthServer: authServer,
|
||||
EventLog: elog,
|
||||
SessionService: sessionService,
|
||||
Recorder: rec,
|
||||
PermissionChecker: auth.NewStandardPermissions(),
|
||||
Roles: auth.StandardRoles,
|
||||
})
|
||||
process.RegisterFunc(func() error {
|
||||
apisrv.Serve()
|
||||
apiServer.Serve()
|
||||
return nil
|
||||
})
|
||||
|
||||
|
@ -245,9 +263,9 @@ func (process *TeleportProcess) InitAuthService() error {
|
|||
process.RegisterFunc(func() error {
|
||||
utils.Consolef(cfg.Console, "[AUTH] Auth service is starting on %v", cfg.Auth.SSHAddr.Addr)
|
||||
tsrv, err := auth.NewTunnel(
|
||||
cfg.Auth.SSHAddr, []ssh.Signer{signer},
|
||||
apisrv,
|
||||
asrv,
|
||||
cfg.Auth.SSHAddr, []ssh.Signer{identity.KeySigner},
|
||||
apiServer,
|
||||
authServer,
|
||||
auth.SetLimiter(limiter),
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -260,15 +278,50 @@ func (process *TeleportProcess) InitAuthService() error {
|
|||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// Heart beat auth server presence, this is not the best place for this
|
||||
// logic, consolidate it into auth package later
|
||||
process.RegisterFunc(func() error {
|
||||
authClient, err := auth.NewTunClient(
|
||||
cfg.Auth.SSHAddr,
|
||||
identity.Cert.ValidPrincipals[0],
|
||||
[]ssh.AuthMethod{ssh.PublicKeys(identity.KeySigner)})
|
||||
// success?
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
srv := services.Server{
|
||||
ID: process.Config.HostUUID,
|
||||
Addr: cfg.Auth.SSHAddr.Addr,
|
||||
Hostname: process.Config.Hostname,
|
||||
}
|
||||
if process.Config.AdvertiseIP != nil {
|
||||
_, port, err := net.SplitHostPort(srv.Addr)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
srv.Addr = fmt.Sprintf("%v:%v", process.Config.AdvertiseIP.String(), port)
|
||||
}
|
||||
for {
|
||||
err := authClient.UpsertAuthServer(srv, defaults.ServerHeartbeatTTL)
|
||||
if err != nil {
|
||||
log.Warningf("failed to announce presence: %v", err)
|
||||
}
|
||||
sleepTime := defaults.ServerHeartbeatTTL/2 + utils.RandomDuration(defaults.ServerHeartbeatTTL/10)
|
||||
log.Infof("[AUTH] will ping auth service in %v", sleepTime)
|
||||
time.Sleep(sleepTime)
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (process *TeleportProcess) initSSH() error {
|
||||
return process.RegisterWithAuthServer(process.Config.SSH.Token, teleport.RoleNode,
|
||||
func() error { return process.initSSHEndpoint() })
|
||||
return process.RegisterWithAuthServer(
|
||||
process.Config.SSH.Token, teleport.RoleNode,
|
||||
process.initSSHEndpoint)
|
||||
}
|
||||
|
||||
func (process *TeleportProcess) initSSHEndpoint() error {
|
||||
func (process *TeleportProcess) initSSHEndpoint(conn *connector) error {
|
||||
cfg := process.Config
|
||||
|
||||
limiter, err := limiter.NewLimiter(cfg.SSH.Limiter)
|
||||
|
@ -278,15 +331,15 @@ func (process *TeleportProcess) initSSHEndpoint() error {
|
|||
|
||||
s, err := srv.New(cfg.SSH.Addr,
|
||||
cfg.Hostname,
|
||||
[]ssh.Signer{process.Identity.KeySigner},
|
||||
process.AuthClient,
|
||||
[]ssh.Signer{conn.identity.KeySigner},
|
||||
conn.client,
|
||||
cfg.DataDir,
|
||||
cfg.SSH.AdvertiseIP,
|
||||
cfg.AdvertiseIP,
|
||||
srv.SetLimiter(limiter),
|
||||
srv.SetShell(cfg.SSH.Shell),
|
||||
srv.SetEventLogger(process.AuthClient),
|
||||
srv.SetSessionServer(process.AuthClient),
|
||||
srv.SetRecorder(process.AuthClient),
|
||||
srv.SetEventLogger(conn.client),
|
||||
srv.SetSessionServer(conn.client),
|
||||
srv.SetRecorder(conn.client),
|
||||
srv.SetLabels(cfg.SSH.Labels, cfg.SSH.CmdLabels),
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -308,62 +361,69 @@ func (process *TeleportProcess) initSSHEndpoint() 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, callback func() error) error {
|
||||
func (process *TeleportProcess) RegisterWithAuthServer(token string, role teleport.Role, callback func(conn *connector) error) error {
|
||||
cfg := process.Config
|
||||
authServer := cfg.AuthServers[0].Addr
|
||||
identityID := auth.IdentityID{Role: role, HostUUID: cfg.HostUUID}
|
||||
|
||||
// this means the server has not been initialized yet, we are starting
|
||||
// the registering client that attempts to connect to the auth server
|
||||
// and provision the keys
|
||||
process.RegisterFunc(func() error {
|
||||
loggedIn := false
|
||||
for !loggedIn {
|
||||
if token != "" {
|
||||
log.Infof("joining the cluster with a token %v", token)
|
||||
err := auth.Register(cfg.HostUUID, cfg.DataDir, token, role, cfg.AuthServers)
|
||||
if err != nil {
|
||||
log.Errorf("[SSH] failed to join the cluster: %v", err)
|
||||
}
|
||||
for {
|
||||
conn, err := process.connectToAuthService(role)
|
||||
if err == nil {
|
||||
return callback(conn)
|
||||
}
|
||||
loggedIn = process.loginIntoAuthService()
|
||||
if !loggedIn {
|
||||
time.Sleep(time.Second * 5)
|
||||
if teleport.IsConnectionProblem(err) {
|
||||
log.Errorf("[%v] failed connect to auth serverr: %v", role, err)
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
if !teleport.IsNotFound(err) {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
// we haven't connected yet, so we expect the token to exist
|
||||
if process.getLocalAuth() != nil {
|
||||
// Auth service is on the same host, no need to go though the invitation
|
||||
// procedure
|
||||
log.Infof("this server has local Auth server started, using it to add role to the cluster")
|
||||
err = auth.LocalRegister(cfg.DataDir, identityID, process.getLocalAuth())
|
||||
} else {
|
||||
// Auth server is remote, so we need a provisioning token
|
||||
if token == "" {
|
||||
return trace.Wrap(teleport.BadParameter(role.String(), "role has no identity and no provisioning token"))
|
||||
}
|
||||
log.Infof("%v joining the cluster with a token %v", role, token)
|
||||
err = auth.Register(cfg.DataDir, token, identityID, cfg.AuthServers)
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("[%v] failed to join the cluster: %v", role, err)
|
||||
time.Sleep(time.Second)
|
||||
} else {
|
||||
utils.Consolef(os.Stdout, "[%v] Successfully registered with the cluster", role)
|
||||
continue
|
||||
}
|
||||
}
|
||||
utils.Consolef(os.Stdout, "[SSH] Successfully registered with the auth server %v", authServer)
|
||||
return callback()
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (process *TeleportProcess) initReverseTunnel() error {
|
||||
return process.RegisterWithAuthServer(process.Config.Proxy.Token, teleport.RoleNode,
|
||||
func() error { return process.initTunAgent() })
|
||||
return process.RegisterWithAuthServer(
|
||||
process.Config.Proxy.Token,
|
||||
teleport.RoleNode,
|
||||
process.initTunAgent)
|
||||
}
|
||||
|
||||
func (process *TeleportProcess) initTunAgent() error {
|
||||
func (process *TeleportProcess) initTunAgent(conn *connector) error {
|
||||
cfg := process.Config
|
||||
i, err := auth.ReadIdentity(cfg.DataDir)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
endpointUser := i.Cert.ValidPrincipals[0]
|
||||
|
||||
client, err := auth.NewTunClient(
|
||||
cfg.AuthServers[0],
|
||||
endpointUser,
|
||||
[]ssh.AuthMethod{ssh.PublicKeys(i.KeySigner)})
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
a, err := reversetunnel.NewAgent(
|
||||
cfg.ReverseTunnel.DialAddr,
|
||||
cfg.Hostname,
|
||||
[]ssh.Signer{i.KeySigner},
|
||||
client,
|
||||
reversetunnel.SetEventLogger(client))
|
||||
[]ssh.Signer{conn.identity.KeySigner},
|
||||
conn.client,
|
||||
reversetunnel.SetEventLogger(conn.client))
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
@ -392,11 +452,12 @@ func (process *TeleportProcess) initProxy() (err error) {
|
|||
return trace.Wrap(err)
|
||||
}
|
||||
}
|
||||
return process.RegisterWithAuthServer(process.Config.Proxy.Token, teleport.RoleNode,
|
||||
func() error { return process.initProxyEndpoint() })
|
||||
return process.RegisterWithAuthServer(
|
||||
process.Config.Proxy.Token, teleport.RoleProxy,
|
||||
process.initProxyEndpoint)
|
||||
}
|
||||
|
||||
func (process *TeleportProcess) initProxyEndpoint() error {
|
||||
func (process *TeleportProcess) initProxyEndpoint(conn *connector) error {
|
||||
cfg := process.Config
|
||||
proxyLimiter, err := limiter.NewLimiter(cfg.Proxy.Limiter)
|
||||
if err != nil {
|
||||
|
@ -408,26 +469,12 @@ func (process *TeleportProcess) initProxyEndpoint() error {
|
|||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
i, err := auth.ReadIdentity(cfg.DataDir)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
endpointUser := i.Cert.ValidPrincipals[0]
|
||||
client, err := auth.NewTunClient(
|
||||
cfg.AuthServers[0],
|
||||
endpointUser,
|
||||
[]ssh.AuthMethod{ssh.PublicKeys(i.KeySigner)})
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
tsrv, err := reversetunnel.NewServer(
|
||||
cfg.Proxy.ReverseTunnelListenAddr,
|
||||
[]ssh.Signer{i.KeySigner},
|
||||
client,
|
||||
[]ssh.Signer{conn.identity.KeySigner},
|
||||
conn.client,
|
||||
reversetunnel.SetLimiter(reverseTunnelLimiter),
|
||||
reversetunnel.DirectSite(i.Cert.Extensions[utils.CertExtensionAuthority], client),
|
||||
reversetunnel.DirectSite(conn.identity.Cert.Extensions[utils.CertExtensionAuthority], conn.client),
|
||||
)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
|
@ -435,13 +482,13 @@ func (process *TeleportProcess) initProxyEndpoint() error {
|
|||
|
||||
SSHProxy, err := srv.New(cfg.Proxy.SSHAddr,
|
||||
cfg.Hostname,
|
||||
[]ssh.Signer{i.KeySigner},
|
||||
client,
|
||||
[]ssh.Signer{conn.identity.KeySigner},
|
||||
conn.client,
|
||||
cfg.DataDir,
|
||||
nil,
|
||||
srv.SetLimiter(proxyLimiter),
|
||||
srv.SetProxyMode(tsrv),
|
||||
srv.SetSessionServer(client),
|
||||
srv.SetSessionServer(conn.client),
|
||||
)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
|
|
|
@ -19,32 +19,33 @@ package services
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/teleport/lib/backend"
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// PresenceService records and reports the presence of all components
|
||||
// of the cluster - Nodes, Proxies and SSH nodes
|
||||
type PresenceService struct {
|
||||
backend backend.Backend
|
||||
}
|
||||
|
||||
// NewPresenceService returns new presence service instance
|
||||
func NewPresenceService(backend backend.Backend) *PresenceService {
|
||||
return &PresenceService{backend}
|
||||
}
|
||||
|
||||
// GetServers returns a list of registered servers
|
||||
func (s *PresenceService) GetServers() ([]Server, error) {
|
||||
IDs, err := s.backend.GetKeys([]string{"servers"})
|
||||
func (s *PresenceService) getServers(prefix string) ([]Server, error) {
|
||||
IDs, err := s.backend.GetKeys([]string{prefix})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
servers := make([]Server, len(IDs))
|
||||
for i, id := range IDs {
|
||||
data, err := s.backend.GetVal([]string{"servers"}, id)
|
||||
data, err := s.backend.GetVal([]string{prefix}, id)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
@ -52,57 +53,71 @@ func (s *PresenceService) GetServers() ([]Server, error) {
|
|||
return nil, trace.Wrap(err)
|
||||
}
|
||||
}
|
||||
// sorting helps with tests and makes it all deterministic
|
||||
sort.Sort(sortedServers(servers))
|
||||
return servers, nil
|
||||
}
|
||||
|
||||
// UpsertServer registers server presence, permanently if ttl is 0 or
|
||||
// for the specified duration with second resolution if it's >= 1 second
|
||||
func (s *PresenceService) UpsertServer(server Server, ttl time.Duration) error {
|
||||
type sortedServers []Server
|
||||
|
||||
func (s sortedServers) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s sortedServers) Less(i, j int) bool {
|
||||
return s[i].ID < s[j].ID
|
||||
}
|
||||
|
||||
func (s sortedServers) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
func (s *PresenceService) upsertServer(prefix string, server Server, ttl time.Duration) error {
|
||||
data, err := json.Marshal(server)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
err = s.backend.UpsertVal([]string{"servers"}, server.ID, data, ttl)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
return err
|
||||
err = s.backend.UpsertVal([]string{prefix}, server.ID, data, ttl)
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
// GetServers returns a list of registered servers
|
||||
const (
|
||||
nodesPrefix = "nodes"
|
||||
authServersPrefix = "authservers"
|
||||
proxiesPrefix = "proxies"
|
||||
)
|
||||
|
||||
// GetNodes returns a list of registered servers
|
||||
func (s *PresenceService) GetNodes() ([]Server, error) {
|
||||
return s.getServers(nodesPrefix)
|
||||
}
|
||||
|
||||
// UpsertNode registers node presence, permanently if ttl is 0 or
|
||||
// for the specified duration with second resolution if it's >= 1 second
|
||||
func (s *PresenceService) UpsertNode(server Server, ttl time.Duration) error {
|
||||
return s.upsertServer(nodesPrefix, server, ttl)
|
||||
}
|
||||
|
||||
// GetAuthServers returns a list of registered servers
|
||||
func (s *PresenceService) GetAuthServers() ([]Server, error) {
|
||||
IDs, err := s.backend.GetKeys([]string{"authservers"})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
servers := make([]Server, len(IDs))
|
||||
for i, id := range IDs {
|
||||
data, err := s.backend.GetVal([]string{"authservers"}, id)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if err := json.Unmarshal(data, &servers[i]); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
}
|
||||
return servers, nil
|
||||
return s.getServers(authServersPrefix)
|
||||
}
|
||||
|
||||
// UpsertServer registers server presence, permanently if ttl is 0 or
|
||||
// UpsertAuthServer registers auth server presence, permanently if ttl is 0 or
|
||||
// for the specified duration with second resolution if it's >= 1 second
|
||||
func (s *PresenceService) UpsertAuthServer(server Server, ttl time.Duration) error {
|
||||
data, err := json.Marshal(server)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
err = s.backend.UpsertVal([]string{"authservers"},
|
||||
server.ID, data, ttl)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
return err
|
||||
return s.upsertServer(authServersPrefix, server, ttl)
|
||||
}
|
||||
|
||||
// UpsertProxy registers proxy server presence, permanently if ttl is 0 or
|
||||
// for the specified duration with second resolution if it's >= 1 second
|
||||
func (s *PresenceService) UpsertProxy(server Server, ttl time.Duration) error {
|
||||
return s.upsertServer(proxiesPrefix, server, ttl)
|
||||
}
|
||||
|
||||
// GetProxies returns a list of registered proxies
|
||||
func (s *PresenceService) GetProxies() ([]Server, error) {
|
||||
return s.getServers(proxiesPrefix)
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
|
|
|
@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package services
|
||||
|
||||
import (
|
||||
|
|
|
@ -156,16 +156,38 @@ func (s *ServicesTestSuite) CertAuthCRUD(c *C) {
|
|||
}
|
||||
|
||||
func (s *ServicesTestSuite) ServerCRUD(c *C) {
|
||||
out, err := s.PresenceS.GetServers()
|
||||
out, err := s.PresenceS.GetNodes()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(out), Equals, 0)
|
||||
|
||||
srv := Server{ID: "srv1", Addr: "localhost:2022"}
|
||||
c.Assert(s.PresenceS.UpsertServer(srv, 0), IsNil)
|
||||
c.Assert(s.PresenceS.UpsertNode(srv, 0), IsNil)
|
||||
|
||||
out, err = s.PresenceS.GetServers()
|
||||
out, err = s.PresenceS.GetNodes()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(out, DeepEquals, []Server{srv})
|
||||
|
||||
out, err = s.PresenceS.GetProxies()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(out), Equals, 0)
|
||||
|
||||
proxy := Server{ID: "proxy1", Addr: "localhost:2023"}
|
||||
c.Assert(s.PresenceS.UpsertProxy(proxy, 0), IsNil)
|
||||
|
||||
out, err = s.PresenceS.GetProxies()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(out, DeepEquals, []Server{proxy})
|
||||
|
||||
out, err = s.PresenceS.GetAuthServers()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(out), Equals, 0)
|
||||
|
||||
auth := Server{ID: "auth1", Addr: "localhost:2025"}
|
||||
c.Assert(s.PresenceS.UpsertAuthServer(auth, 0), IsNil)
|
||||
|
||||
out, err = s.PresenceS.GetAuthServers()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(out, DeepEquals, []Server{auth})
|
||||
}
|
||||
|
||||
func (s *ServicesTestSuite) PasswordHashCRUD(c *C) {
|
||||
|
|
|
@ -77,7 +77,7 @@ func (t *proxySubsys) start(sconn *ssh.ServerConn, ch ssh.Channel, req *ssh.Requ
|
|||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
servers, err := clt.GetServers()
|
||||
servers, err := clt.GetNodes()
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ func (b *backendResolver) resolve(query string) ([]string, error) {
|
|||
// simply expand the query to all known hosts
|
||||
if query == "*" {
|
||||
out := []string{}
|
||||
srvs, err := b.authService.GetServers()
|
||||
srvs, err := b.authService.GetNodes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ func (t *proxySitesSubsys) start(sconn *ssh.ServerConn, ch ssh.Channel, req *ssh
|
|||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
servers, err := clt.GetServers()
|
||||
servers, err := clt.GetNodes()
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
|
|
@ -264,7 +264,10 @@ func (s *Server) registerServer() error {
|
|||
Labels: s.labels,
|
||||
CmdLabels: s.getCommandLabels(),
|
||||
}
|
||||
return trace.Wrap(s.authService.UpsertServer(srv, defaults.ServerHeartbeatTTL))
|
||||
if !s.proxyMode {
|
||||
return trace.Wrap(s.authService.UpsertNode(srv, defaults.ServerHeartbeatTTL))
|
||||
}
|
||||
return trace.Wrap(s.authService.UpsertProxy(srv, defaults.ServerHeartbeatTTL))
|
||||
}
|
||||
|
||||
// heartbeatPresence periodically calls into the auth server to let everyone
|
||||
|
@ -482,12 +485,10 @@ func (s *Server) Close() error {
|
|||
|
||||
// Start starts server
|
||||
func (s *Server) Start() error {
|
||||
if !s.proxyMode {
|
||||
if len(s.cmdLabels) > 0 {
|
||||
s.updateLabels()
|
||||
}
|
||||
go s.heartbeatPresence()
|
||||
if len(s.cmdLabels) > 0 {
|
||||
s.updateLabels()
|
||||
}
|
||||
go s.heartbeatPresence()
|
||||
return s.srv.Start()
|
||||
}
|
||||
|
||||
|
|
|
@ -340,10 +340,13 @@ func (s *SrvSuite) TestProxyReverseTunnel(c *C) {
|
|||
|
||||
sessionServer, err := sess.New(s.bk)
|
||||
c.Assert(err, IsNil)
|
||||
apiSrv := auth.NewAPIWithRoles(s.a, bl, sessionServer, rec,
|
||||
auth.NewAllowAllPermissions(),
|
||||
auth.StandardRoles,
|
||||
)
|
||||
apiSrv := auth.NewAPIWithRoles(auth.APIConfig{
|
||||
AuthServer: s.a,
|
||||
EventLog: bl,
|
||||
SessionService: sessionServer,
|
||||
Recorder: rec,
|
||||
PermissionChecker: auth.NewAllowAllPermissions(),
|
||||
Roles: auth.StandardRoles})
|
||||
go apiSrv.Serve()
|
||||
|
||||
tsrv, err := auth.NewTunnel(
|
||||
|
@ -504,10 +507,13 @@ func (s *SrvSuite) TestProxyRoundRobin(c *C) {
|
|||
|
||||
sessionServer, err := sess.New(s.bk)
|
||||
c.Assert(err, IsNil)
|
||||
apiSrv := auth.NewAPIWithRoles(s.a, bl, sessionServer, rec,
|
||||
auth.NewAllowAllPermissions(),
|
||||
auth.StandardRoles,
|
||||
)
|
||||
apiSrv := auth.NewAPIWithRoles(auth.APIConfig{
|
||||
AuthServer: s.a,
|
||||
EventLog: bl,
|
||||
SessionService: sessionServer,
|
||||
Recorder: rec,
|
||||
PermissionChecker: auth.NewAllowAllPermissions(),
|
||||
Roles: auth.StandardRoles})
|
||||
go apiSrv.Serve()
|
||||
|
||||
tsrv, err := auth.NewTunnel(
|
||||
|
@ -602,10 +608,13 @@ func (s *SrvSuite) TestProxyDirectAccess(c *C) {
|
|||
sessionServer, err := sess.New(s.bk)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
apiSrv := auth.NewAPIWithRoles(s.a, bl, sessionServer, rec,
|
||||
auth.NewAllowAllPermissions(),
|
||||
auth.StandardRoles,
|
||||
)
|
||||
apiSrv := auth.NewAPIWithRoles(auth.APIConfig{
|
||||
AuthServer: s.a,
|
||||
EventLog: bl,
|
||||
SessionService: sessionServer,
|
||||
Recorder: rec,
|
||||
PermissionChecker: auth.NewAllowAllPermissions(),
|
||||
Roles: auth.StandardRoles})
|
||||
go apiSrv.Serve()
|
||||
|
||||
tsrv, err := auth.NewTunnel(
|
||||
|
|
|
@ -17,9 +17,6 @@ limitations under the License.
|
|||
package utils
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"fmt"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
|
@ -28,6 +25,9 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gravitational/teleport"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/pborman/uuid"
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
@ -38,56 +38,19 @@ type HostKeyCallback func(hostID string, remote net.Addr, key ssh.PublicKey) err
|
|||
func ReadPath(path string) ([]byte, error) {
|
||||
s, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert path %v, err %v", s, err)
|
||||
return nil, trace.Wrap(teleport.ConvertSystemError(err))
|
||||
}
|
||||
abs, err := filepath.EvalSymlinks(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to eval symlinks in path %v, err %v", path, err)
|
||||
return nil, trace.Wrap(teleport.ConvertSystemError(err))
|
||||
}
|
||||
bytes, err := ioutil.ReadFile(abs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, trace.Wrap(teleport.ConvertSystemError(err))
|
||||
}
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
func WriteArchive(root_directory string, w io.Writer) error {
|
||||
ar := tar.NewWriter(w)
|
||||
|
||||
walkFn := func(path string, info os.FileInfo, err error) error {
|
||||
if info.Mode().IsDir() {
|
||||
return nil
|
||||
}
|
||||
// Because of scoping we can reference the external root_directory variable
|
||||
new_path := path[len(root_directory):]
|
||||
if len(new_path) == 0 {
|
||||
return nil
|
||||
}
|
||||
fr, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
if h, err := tar.FileInfoHeader(info, new_path); err != nil {
|
||||
return err
|
||||
} else {
|
||||
h.Name = new_path
|
||||
if err = ar.WriteHeader(h); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if length, err := io.Copy(ar, fr); err != nil {
|
||||
return err
|
||||
} else {
|
||||
fmt.Println(length)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return filepath.Walk(root_directory, walkFn)
|
||||
}
|
||||
|
||||
type multiCloser struct {
|
||||
closers []io.Closer
|
||||
}
|
||||
|
@ -101,8 +64,7 @@ func (mc *multiCloser) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// MultiCloser implements io.Close,
|
||||
// it sequentially calls Close() on each object
|
||||
// MultiCloser implements io.Close, it sequentially calls Close() on each object
|
||||
func MultiCloser(closers ...io.Closer) *multiCloser {
|
||||
return &multiCloser{
|
||||
closers: closers,
|
||||
|
|
|
@ -50,7 +50,7 @@ func newConnectHandler(req connectReq, ctx *sessionContext, site reversetunnel.R
|
|||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
servers, err := clt.GetServers()
|
||||
servers, err := clt.GetNodes()
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ func (w *sessionStreamHandler) stream(ws *websocket.Conn) error {
|
|||
}
|
||||
}
|
||||
|
||||
servers, err := clt.GetServers()
|
||||
servers, err := clt.GetNodes()
|
||||
if err != nil {
|
||||
if !teleport.IsNotFound(err) {
|
||||
return trace.Wrap(err)
|
||||
|
|
|
@ -429,7 +429,7 @@ func (m *Handler) getSiteNodes(w http.ResponseWriter, r *http.Request, _ httprou
|
|||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
servers, err := clt.GetServers()
|
||||
servers, err := clt.GetNodes()
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
|
|
@ -170,10 +170,13 @@ func (s *WebSuite) SetUpTest(c *C) {
|
|||
apiPort := s.freePorts[len(s.freePorts)-1]
|
||||
s.freePorts = s.freePorts[:len(s.freePorts)-1]
|
||||
|
||||
apiServer := auth.NewAPIWithRoles(authServer, eventsLog, sessionServer, recorder,
|
||||
auth.NewAllowAllPermissions(),
|
||||
auth.StandardRoles,
|
||||
)
|
||||
apiServer := auth.NewAPIWithRoles(auth.APIConfig{
|
||||
AuthServer: authServer,
|
||||
EventLog: eventsLog,
|
||||
SessionService: sessionServer,
|
||||
Recorder: recorder,
|
||||
PermissionChecker: auth.NewAllowAllPermissions(),
|
||||
Roles: auth.StandardRoles})
|
||||
go apiServer.Serve()
|
||||
|
||||
tunAddr := utils.NetAddr{
|
||||
|
|
14
roles.go
14
roles.go
|
@ -2,6 +2,7 @@ package teleport
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
)
|
||||
|
@ -9,11 +10,16 @@ import (
|
|||
// Role identifies the role of SSH server connection
|
||||
type Role string
|
||||
|
||||
// String returns debug-friendly representation of this role
|
||||
func (r Role) String() string {
|
||||
return fmt.Sprintf("%v", strings.ToUpper(string(r)))
|
||||
}
|
||||
|
||||
// Check checks if this a a valid role value, returns nil
|
||||
// if it's ok, false otherwise
|
||||
func (r Role) Check() error {
|
||||
switch r {
|
||||
case RoleAuth, RoleUser, RoleWeb, RoleNode, RoleAdmin, RoleProvisionToken, RoleSignup, RoleHangoutRemoteUser:
|
||||
case RoleAuth, RoleUser, RoleWeb, RoleNode, RoleAdmin, RoleProvisionToken, RoleSignup, RoleProxy:
|
||||
return nil
|
||||
}
|
||||
return trace.Wrap(BadParameter("role", fmt.Sprintf("%v is not supported", r)))
|
||||
|
@ -28,12 +34,12 @@ const (
|
|||
RoleWeb Role = "Web"
|
||||
// RoleNode is a role for SSH node in the cluster
|
||||
RoleNode Role = "Node"
|
||||
// RoleProxy is a role for SSH proxy in the cluster
|
||||
RoleProxy Role = "Proxy"
|
||||
// RoleAdmin is admin role
|
||||
RoleAdmin Role = "Admin"
|
||||
// RoleProvisionToken is a role for
|
||||
// RoleProvisionToken is a role for nodes authenticated using provisioning tokens
|
||||
RoleProvisionToken Role = "ProvisionToken"
|
||||
// RoleSignup is for first time signing up users
|
||||
RoleSignup Role = "Signup"
|
||||
// RoleHangoutRemoteUser is for users joining remote hangouts
|
||||
RoleHangoutRemoteUser Role = "HangoutRemoteUser"
|
||||
)
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
"github.com/gravitational/teleport"
|
||||
"github.com/gravitational/teleport/lib/auth"
|
||||
"github.com/gravitational/teleport/lib/config"
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/service"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
|
@ -56,6 +57,10 @@ type AuthCommand struct {
|
|||
authType string
|
||||
}
|
||||
|
||||
type AuthServerCommand struct {
|
||||
config *service.Config
|
||||
}
|
||||
|
||||
func main() {
|
||||
utils.InitLoggerCLI()
|
||||
app := utils.InitCLIParser("tctl", GlobalHelpString)
|
||||
|
@ -65,6 +70,7 @@ func main() {
|
|||
cmdUsers := UserCommand{config: cfg}
|
||||
cmdNodes := NodeCommand{config: cfg}
|
||||
cmdAuth := AuthCommand{config: cfg}
|
||||
cmdAuthServers := AuthServerCommand{config: cfg}
|
||||
|
||||
// define global flags:
|
||||
var ccf CLIConfig
|
||||
|
@ -105,6 +111,10 @@ func main() {
|
|||
authList := auth.Command("ls", "List trusted user certificate authorities").Hidden()
|
||||
authExport := auth.Command("export", "Export concatenated keys to standard output").Hidden()
|
||||
|
||||
// operations with auth servers
|
||||
authServers := app.Command("authservers", "Operations with user and host certificate authorities").Hidden()
|
||||
authServerAdd := authServers.Command("add", "Add a new auth server node to the cluster").Hidden()
|
||||
|
||||
// parse CLI commands+flags:
|
||||
command, err := app.Parse(os.Args[1:])
|
||||
if err != nil {
|
||||
|
@ -142,6 +152,8 @@ func main() {
|
|||
err = cmdAuth.ListAuthorities(client)
|
||||
case authExport.FullCommand():
|
||||
err = cmdAuth.ExportAuthorities(client)
|
||||
case authServerAdd.FullCommand():
|
||||
err = cmdAuthServers.Invite(client)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -233,7 +245,7 @@ func (u *NodeCommand) Invite(client *auth.TunClient) error {
|
|||
// ListActive retreives the list of nodes who recently sent heartbeats to
|
||||
// to a cluster and prints it to stdout
|
||||
func (u *NodeCommand) ListActive(client *auth.TunClient) error {
|
||||
nodes, err := client.GetServers()
|
||||
nodes, err := client.GetNodes()
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
@ -310,6 +322,27 @@ func (a *AuthCommand) ExportAuthorities(client *auth.TunClient) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Invite generates a token which can be used to add another SSH auth server
|
||||
// to the cluster
|
||||
func (u *AuthServerCommand) Invite(client *auth.TunClient) error {
|
||||
authDomainName, err := client.GetLocalDomain()
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
invitationTTL := time.Minute * 15
|
||||
token, err := client.GenerateToken(teleport.RoleAuth, invitationTTL)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
cfg := config.MakeAuthPeerFileConfig(authDomainName, token)
|
||||
out := cfg.DebugDumpToYAML()
|
||||
|
||||
fmt.Printf(
|
||||
"# Run this config the new auth server to join the cluster:\n# > teleport start --config config.yaml\n# Fill in auth peers in this config:\n")
|
||||
fmt.Println(out)
|
||||
return nil
|
||||
}
|
||||
|
||||
// connectToAuthService creates a valid client connection to the auth service
|
||||
func connectToAuthService(cfg *service.Config) (client *auth.TunClient, err error) {
|
||||
// connect to the local auth server by default:
|
||||
|
@ -319,7 +352,7 @@ func connectToAuthService(cfg *service.Config) (client *auth.TunClient, err erro
|
|||
}
|
||||
|
||||
// read the host SSH keys and use them to open an SSH connection to the auth service
|
||||
i, err := auth.ReadIdentity(cfg.DataDir)
|
||||
i, err := auth.ReadIdentity(cfg.DataDir, auth.IdentityID{Role: teleport.RoleAdmin, HostUUID: cfg.HostUUID})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
|
|
@ -95,6 +95,15 @@ func applyFileConfig(fc *config.FileConfig, cfg *service.Config) error {
|
|||
}
|
||||
applyString(fc.NodeName, &cfg.Hostname)
|
||||
|
||||
// apply "advertise_ip" setting:
|
||||
advertiseIP := fc.AdvertiseIP
|
||||
if advertiseIP != nil {
|
||||
if err := validateAdvertiseIP(advertiseIP); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
cfg.AdvertiseIP = advertiseIP
|
||||
}
|
||||
|
||||
// config file has auth servers in there?
|
||||
if len(fc.AuthServers) > 0 {
|
||||
cfg.AuthServers = make(service.NetAddrSlice, 0, len(fc.AuthServers))
|
||||
|
@ -107,6 +116,7 @@ func applyFileConfig(fc *config.FileConfig, cfg *service.Config) error {
|
|||
}
|
||||
}
|
||||
cfg.ApplyToken(fc.AuthToken)
|
||||
cfg.Auth.DomainName = fc.Auth.DomainName
|
||||
|
||||
// configure storage:
|
||||
switch fc.Storage.Type {
|
||||
|
@ -235,14 +245,6 @@ func applyFileConfig(fc *config.FileConfig, cfg *service.Config) error {
|
|||
}
|
||||
}
|
||||
}
|
||||
// apply "advertise_ip" setting:
|
||||
advertiseIP := fc.SSH.AdvertiseIP
|
||||
if advertiseIP != nil {
|
||||
if advertiseIP.IsLoopback() || advertiseIP.IsUnspecified() || advertiseIP.IsMulticast() {
|
||||
return teleport.BadParameter("advertise-ip", fmt.Sprintf("unreachable advertise IP: %v", advertiseIP))
|
||||
}
|
||||
cfg.SSH.AdvertiseIP = advertiseIP
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -270,7 +272,6 @@ func configure(clf *CommandLineFlags) (cfg *service.Config, err error) {
|
|||
if err = applyFileConfig(fileConf, cfg); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// apply --debug flag:
|
||||
if clf.Debug {
|
||||
cfg.Console = ioutil.Discard
|
||||
|
@ -315,6 +316,13 @@ func configure(clf *CommandLineFlags) (cfg *service.Config, err error) {
|
|||
applyListenIP(clf.ListenIP, cfg)
|
||||
}
|
||||
|
||||
if clf.AdvertiseIP != nil {
|
||||
if err := validateAdvertiseIP(clf.AdvertiseIP); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
cfg.AdvertiseIP = clf.AdvertiseIP
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
|
@ -366,3 +374,10 @@ func validateRoles(roles string) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAdvertiseIP(advertiseIP net.IP) error {
|
||||
if advertiseIP.IsLoopback() || advertiseIP.IsUnspecified() || advertiseIP.IsMulticast() {
|
||||
return teleport.BadParameter("advertise-ip", fmt.Sprintf("unreachable advertise IP: %v", advertiseIP))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -101,11 +101,12 @@ func (s *MainTestSuite) TestConfigFile(c *check.C) {
|
|||
c.Assert(log.GetLevel(), check.Equals, log.DebugLevel)
|
||||
c.Assert(conf.Hostname, check.Equals, "hvostongo.example.org")
|
||||
c.Assert(conf.SSH.Token, check.Equals, "xxxyyy")
|
||||
c.Assert(conf.SSH.AdvertiseIP, check.DeepEquals, net.ParseIP("10.5.5.5"))
|
||||
c.Assert(conf.AdvertiseIP, check.DeepEquals, net.ParseIP("10.5.5.5"))
|
||||
}
|
||||
|
||||
const YAMLConfig = `
|
||||
teleport:
|
||||
advertise_ip: 10.5.5.5
|
||||
nodename: hvostongo.example.org
|
||||
auth_servers:
|
||||
- tcp://auth.server.example.org:3024
|
||||
|
@ -131,7 +132,6 @@ auth_service:
|
|||
ssh_service:
|
||||
enabled: no
|
||||
listen_addr: tcp://ssh
|
||||
advertise_ip: 10.5.5.5
|
||||
labels:
|
||||
name: mondoserver
|
||||
role: slave
|
||||
|
|
Loading…
Reference in a new issue