-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Ensure we don't miss the resolution of an access request (#9193)
This makes it so that tsh will watch for access request resolution on the correct (root) cluster, and it will not create access requests before the event watcher is ready. Fixes #9003 and #9244.
- Loading branch information
1 parent
d19fe1c
commit c3dee23
Showing
3 changed files
with
223 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,13 +35,15 @@ import ( | |
"github.com/gravitational/teleport/api/constants" | ||
apidefaults "github.com/gravitational/teleport/api/defaults" | ||
"github.com/gravitational/teleport/api/types" | ||
"github.com/gravitational/teleport/lib" | ||
"github.com/gravitational/teleport/lib/auth" | ||
"github.com/gravitational/teleport/lib/backend" | ||
"github.com/gravitational/teleport/lib/client" | ||
"github.com/gravitational/teleport/lib/defaults" | ||
"github.com/gravitational/teleport/lib/kube/kubeconfig" | ||
"github.com/gravitational/teleport/lib/modules" | ||
"github.com/gravitational/teleport/lib/service" | ||
"github.com/gravitational/teleport/lib/services" | ||
"github.com/gravitational/teleport/lib/srv" | ||
"github.com/gravitational/teleport/lib/tlsca" | ||
"github.com/gravitational/teleport/lib/utils" | ||
|
@@ -432,6 +434,169 @@ func TestMakeClient(t *testing.T) { | |
require.Greater(t, len(agentKeys), 0) | ||
} | ||
|
||
func TestAccessRequestOnLeaf(t *testing.T) { | ||
tmpHomePath := t.TempDir() | ||
|
||
isInsecure := lib.IsInsecureDevMode() | ||
lib.SetInsecureDevMode(true) | ||
t.Cleanup(func() { | ||
lib.SetInsecureDevMode(isInsecure) | ||
}) | ||
|
||
requester, err := types.NewRole("requester", types.RoleSpecV4{ | ||
Allow: types.RoleConditions{ | ||
Request: &types.AccessRequestConditions{ | ||
Roles: []string{"access"}, | ||
}, | ||
}, | ||
}) | ||
require.NoError(t, err) | ||
|
||
connector := mockConnector(t) | ||
|
||
alice, err := types.NewUser("[email protected]") | ||
require.NoError(t, err) | ||
alice.SetRoles([]string{"requester"}) | ||
|
||
rootAuth, rootProxy := makeTestServers(t, | ||
withBootstrap(requester, connector, alice), | ||
) | ||
|
||
rootAuthServer := rootAuth.GetAuthServer() | ||
require.NotNil(t, rootAuthServer) | ||
rootProxyAddr, err := rootProxy.ProxyWebAddr() | ||
require.NoError(t, err) | ||
rootTunnelAddr, err := rootProxy.ProxyTunnelAddr() | ||
require.NoError(t, err) | ||
|
||
trustedCluster, err := types.NewTrustedCluster("localhost", types.TrustedClusterSpecV2{ | ||
Enabled: true, | ||
Roles: []string{}, | ||
Token: staticToken, | ||
ProxyAddress: rootProxyAddr.String(), | ||
ReverseTunnelAddress: rootTunnelAddr.String(), | ||
RoleMap: []types.RoleMapping{ | ||
{ | ||
Remote: "access", | ||
Local: []string{"access"}, | ||
}, | ||
}, | ||
}) | ||
require.NoError(t, err) | ||
|
||
leafAuth, _ := makeTestServers(t, withClusterName(t, "leafcluster")) | ||
tryCreateTrustedCluster(t, leafAuth.GetAuthServer(), trustedCluster) | ||
|
||
err = Run([]string{ | ||
"login", | ||
"--insecure", | ||
"--debug", | ||
"--auth", connector.GetName(), | ||
"--proxy", rootProxyAddr.String(), | ||
}, setHomePath(tmpHomePath), cliOption(func(cf *CLIConf) error { | ||
cf.mockSSOLogin = mockSSOLogin(t, rootAuthServer, alice) | ||
return nil | ||
})) | ||
require.NoError(t, err) | ||
|
||
err = Run([]string{ | ||
"login", | ||
"--insecure", | ||
"--debug", | ||
"--proxy", rootProxyAddr.String(), | ||
"leafcluster", | ||
}, setHomePath(tmpHomePath)) | ||
require.NoError(t, err) | ||
|
||
err = Run([]string{ | ||
"login", | ||
"--insecure", | ||
"--debug", | ||
"--proxy", rootProxyAddr.String(), | ||
"localhost", | ||
}, setHomePath(tmpHomePath)) | ||
require.NoError(t, err) | ||
|
||
err = Run([]string{ | ||
"login", | ||
"--insecure", | ||
"--debug", | ||
"--proxy", rootProxyAddr.String(), | ||
"leafcluster", | ||
}, setHomePath(tmpHomePath)) | ||
require.NoError(t, err) | ||
|
||
errChan := make(chan error) | ||
go func() { | ||
errChan <- Run([]string{ | ||
"request", | ||
"new", | ||
"--insecure", | ||
"--debug", | ||
"--proxy", rootProxyAddr.String(), | ||
"--roles=access", | ||
}, setHomePath(tmpHomePath)) | ||
}() | ||
|
||
var request types.AccessRequest | ||
for i := 0; i < 5; i++ { | ||
log.Debugf("Waiting for access request %d", i) | ||
requests, err := rootAuth.GetAuthServer().GetAccessRequests(rootAuth.ExitContext(), types.AccessRequestFilter{}) | ||
require.NoError(t, err) | ||
require.LessOrEqual(t, len(requests), 1) | ||
if len(requests) == 1 { | ||
request = requests[0] | ||
break | ||
} | ||
time.Sleep(1 * time.Second) | ||
} | ||
require.NotNil(t, request) | ||
|
||
err = rootAuth.GetAuthServer().SetAccessRequestState( | ||
rootAuth.ExitContext(), | ||
types.AccessRequestUpdate{ | ||
RequestID: request.GetName(), | ||
State: types.RequestState_APPROVED, | ||
}, | ||
) | ||
require.NoError(t, err) | ||
|
||
select { | ||
case err := <-errChan: | ||
require.NoError(t, err) | ||
case <-time.After(2 * time.Minute): | ||
t.Fatal("access request wasn't resolved after 2 minutes") | ||
} | ||
} | ||
|
||
// tryCreateTrustedCluster performs several attempts to create a trusted cluster, | ||
// retries on connection problems and access denied errors to let caches | ||
// propagate and services to start | ||
// | ||
// Duplicated in integration/integration_test.go | ||
func tryCreateTrustedCluster(t *testing.T, authServer *auth.Server, trustedCluster types.TrustedCluster) { | ||
ctx := context.TODO() | ||
for i := 0; i < 10; i++ { | ||
log.Debugf("Will create trusted cluster %v, attempt %v.", trustedCluster, i) | ||
_, err := authServer.UpsertTrustedCluster(ctx, trustedCluster) | ||
if err == nil { | ||
return | ||
} | ||
if trace.IsConnectionProblem(err) { | ||
log.Debugf("Retrying on connection problem: %v.", err) | ||
time.Sleep(500 * time.Millisecond) | ||
continue | ||
} | ||
if trace.IsAccessDenied(err) { | ||
log.Debugf("Retrying on access denied: %v.", err) | ||
time.Sleep(500 * time.Millisecond) | ||
continue | ||
} | ||
require.FailNow(t, "Terminating on unexpected problem", "%v.", err) | ||
} | ||
require.FailNow(t, "Timeout creating trusted cluster") | ||
} | ||
|
||
func TestIdentityRead(t *testing.T) { | ||
// 3 different types of identities | ||
ids := []string{ | ||
|
@@ -942,6 +1107,17 @@ func withAuthConfig(fn func(cfg *service.AuthConfig)) testServerOptFunc { | |
} | ||
} | ||
|
||
func withClusterName(t *testing.T, n string) testServerOptFunc { | ||
return withAuthConfig(func(cfg *service.AuthConfig) { | ||
clusterName, err := services.NewClusterNameWithRandomID( | ||
types.ClusterNameSpecV2{ | ||
ClusterName: n, | ||
}) | ||
require.NoError(t, err) | ||
cfg.ClusterName = clusterName | ||
}) | ||
} | ||
|
||
func makeTestServers(t *testing.T, opts ...testServerOptFunc) (auth *service.TeleportProcess, proxy *service.TeleportProcess) { | ||
var options testServersOpts | ||
for _, opt := range opts { | ||
|
@@ -962,7 +1138,7 @@ func makeTestServers(t *testing.T, opts ...testServerOptFunc) (auth *service.Tel | |
cfg.Auth.StorageConfig.Params = backend.Params{defaults.BackendPath: filepath.Join(cfg.DataDir, defaults.BackendDir)} | ||
cfg.Auth.StaticTokens, err = types.NewStaticTokens(types.StaticTokensSpecV2{ | ||
StaticTokens: []types.ProvisionTokenV1{{ | ||
Roles: []types.SystemRole{types.RoleProxy, types.RoleDatabase}, | ||
Roles: []types.SystemRole{types.RoleProxy, types.RoleDatabase, types.RoleTrustedCluster}, | ||
Expires: time.Now().Add(time.Minute), | ||
Token: staticToken, | ||
}}, | ||
|