Skip to content

Commit b11895f

Browse files
committed
rbac filter
1 parent 182d623 commit b11895f

File tree

15 files changed

+198
-169
lines changed

15 files changed

+198
-169
lines changed

cli/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
722722
options.Database = dbmetrics.NewDBMetrics(options.Database, options.Logger, options.PrometheusRegistry)
723723
}
724724

725-
wsUpdates, err := coderd.NewUpdatesProvider(logger.Named("workspace_updates"), options.Database, options.Pubsub)
725+
wsUpdates, err := coderd.NewUpdatesProvider(logger.Named("workspace_updates"), options.Pubsub, options.Database, options.Authorizer)
726726
if err != nil {
727727
return xerrors.Errorf("create workspace updates provider: %w", err)
728728
}

coderd/apidoc/docs.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/coderd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1331,7 +1331,7 @@ func New(options *Options) *API {
13311331
})
13321332
r.Route("/tailnet", func(r chi.Router) {
13331333
r.Use(apiKeyMiddleware)
1334-
r.Get("/", api.tailnet)
1334+
r.Get("/", api.tailnetRPCConn)
13351335
})
13361336
})
13371337

coderd/coderdtest/coderdtest.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,10 @@ type Options struct {
159159
DatabaseRolluper *dbrollup.Rolluper
160160
WorkspaceUsageTrackerFlush chan int
161161
WorkspaceUsageTrackerTick chan time.Time
162+
NotificationsEnqueuer notifications.Enqueuer
162163
APIKeyEncryptionCache cryptokeys.EncryptionKeycache
163164
OIDCConvertKeyCache cryptokeys.SigningKeycache
164165
Clock quartz.Clock
165-
NotificationsEnqueuer notifications.Enqueuer
166166

167167
WorkspaceUpdatesProvider tailnet.WorkspaceUpdatesProvider
168168
}
@@ -258,7 +258,12 @@ func NewOptions(t testing.TB, options *Options) (func(http.Handler), context.Can
258258

259259
if options.WorkspaceUpdatesProvider == nil {
260260
var err error
261-
options.WorkspaceUpdatesProvider, err = coderd.NewUpdatesProvider(options.Logger.Named("workspace_updates"), options.Database, options.Pubsub)
261+
options.WorkspaceUpdatesProvider, err = coderd.NewUpdatesProvider(
262+
options.Logger.Named("workspace_updates"),
263+
options.Pubsub,
264+
options.Database,
265+
options.Authorizer,
266+
)
262267
require.NoError(t, err)
263268
t.Cleanup(func() {
264269
_ = options.WorkspaceUpdatesProvider.Close()

coderd/database/dbfake/dbfake.go

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -105,20 +105,6 @@ func (b WorkspaceBuildBuilder) WithAgent(mutations ...func([]*sdkproto.Agent) []
105105
Type: "aws_instance",
106106
Agents: agents,
107107
})
108-
if b.ps != nil {
109-
for _, agent := range agents {
110-
uid, err := uuid.Parse(agent.Id)
111-
require.NoError(b.t, err)
112-
msg, err := json.Marshal(wspubsub.WorkspaceEvent{
113-
Kind: wspubsub.WorkspaceEventKindAgentConnectionUpdate,
114-
WorkspaceID: b.ws.ID,
115-
AgentID: &uid,
116-
})
117-
require.NoError(b.t, err)
118-
err = b.ps.Publish(wspubsub.WorkspaceEventChannel(b.ws.OwnerID), msg)
119-
require.NoError(b.t, err)
120-
}
121-
}
122108
return b
123109
}
124110

coderd/workspaceagents.go

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -871,9 +871,12 @@ func (api *API) workspaceAgentClientCoordinate(rw http.ResponseWriter, r *http.R
871871
go httpapi.Heartbeat(ctx, conn)
872872

873873
defer conn.Close(websocket.StatusNormalClosure, "")
874-
err = api.TailnetClientService.ServeClient(ctx, version, wsNetConn, tailnet.ServeClientOptions{
875-
Peer: peerID,
876-
Agent: &workspaceAgent.ID,
874+
err = api.TailnetClientService.ServeClient(ctx, version, wsNetConn, tailnet.StreamID{
875+
Name: "client",
876+
ID: peerID,
877+
Auth: tailnet.ClientCoordinateeAuth{
878+
AgentID: workspaceAgent.ID,
879+
},
877880
})
878881
if err != nil && !xerrors.Is(err, io.EOF) && !xerrors.Is(err, context.Canceled) {
879882
_ = conn.Close(websocket.StatusInternalError, err.Error())
@@ -891,6 +894,7 @@ func (api *API) handleResumeToken(ctx context.Context, rw http.ResponseWriter, r
891894
// case we just want to generate a new peer ID.
892895
if xerrors.Is(err, jwtutils.ErrMissingKeyID) {
893896
peerID = uuid.New()
897+
err = nil
894898
} else if err != nil {
895899
httpapi.Write(ctx, rw, http.StatusUnauthorized, codersdk.Response{
896900
Message: workspacesdk.CoordinateAPIInvalidResumeToken,
@@ -899,7 +903,7 @@ func (api *API) handleResumeToken(ctx context.Context, rw http.ResponseWriter, r
899903
{Field: "resume_token", Detail: workspacesdk.CoordinateAPIInvalidResumeToken},
900904
},
901905
})
902-
return
906+
return peerID, err
903907
} else {
904908
api.Logger.Debug(ctx, "accepted coordinate resume token for peer",
905909
slog.F("peer_id", peerID.String()))
@@ -1479,13 +1483,13 @@ func (api *API) workspaceAgentsExternalAuthListen(ctx context.Context, rw http.R
14791483
}
14801484
}
14811485

1482-
// @Summary User-scoped agent coordination
1483-
// @ID user-scoped-agent-coordination
1486+
// @Summary User-scoped tailnet RPC connection
1487+
// @ID user-scoped-tailnet-rpc-connection
14841488
// @Security CoderSessionToken
14851489
// @Tags Agents
14861490
// @Success 101
14871491
// @Router /tailnet [get]
1488-
func (api *API) tailnet(rw http.ResponseWriter, r *http.Request) {
1492+
func (api *API) tailnetRPCConn(rw http.ResponseWriter, r *http.Request) {
14891493
ctx := r.Context()
14901494

14911495
version := "2.0"
@@ -1509,8 +1513,8 @@ func (api *API) tailnet(rw http.ResponseWriter, r *http.Request) {
15091513
return
15101514
}
15111515

1512-
// Used to authorize tunnel requests, and filter workspace update DB queries
1513-
prepared, err := api.HTTPAuth.AuthorizeSQLFilter(r, policy.ActionRead, rbac.ResourceWorkspace.Type)
1516+
// Used to authorize tunnel request
1517+
sshPrep, err := api.HTTPAuth.AuthorizeSQLFilter(r, policy.ActionSSH, rbac.ResourceWorkspace.Type)
15141518
if err != nil {
15151519
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
15161520
Message: "Internal error preparing sql filter.",
@@ -1537,11 +1541,14 @@ func (api *API) tailnet(rw http.ResponseWriter, r *http.Request) {
15371541
defer conn.Close(websocket.StatusNormalClosure, "")
15381542

15391543
go httpapi.Heartbeat(ctx, conn)
1540-
err = api.TailnetClientService.ServeClient(ctx, version, wsNetConn, tailnet.ServeClientOptions{
1541-
Peer: peerID,
1542-
Auth: &tunnelAuthorizer{
1543-
prep: prepared,
1544-
db: api.Database,
1544+
err = api.TailnetClientService.ServeClient(ctx, version, wsNetConn, tailnet.StreamID{
1545+
Name: "client",
1546+
ID: peerID,
1547+
Auth: tailnet.ClientUserCoordinateeAuth{
1548+
Auth: &rbacAuthorizer{
1549+
sshPrep: sshPrep,
1550+
db: api.Database,
1551+
},
15451552
},
15461553
})
15471554
if err != nil && !xerrors.Is(err, io.EOF) && !xerrors.Is(err, context.Canceled) {

coderd/workspaceagents_test.go

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1937,21 +1937,14 @@ func TestOwnedWorkspacesCoordinate(t *testing.T) {
19371937

19381938
ctx := testutil.Context(t, testutil.WaitLong)
19391939
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
1940-
firstClient, closer, api := coderdtest.NewWithAPI(t, &coderdtest.Options{
1941-
Coordinator: tailnet.NewCoordinator(logger),
1942-
IncludeProvisionerDaemon: true,
1943-
})
1944-
t.Cleanup(func() {
1945-
_ = closer.Close()
1940+
firstClient, _, api := coderdtest.NewWithAPI(t, &coderdtest.Options{
1941+
Coordinator: tailnet.NewCoordinator(logger),
19461942
})
19471943
firstUser := coderdtest.CreateFirstUser(t, firstClient)
19481944
member, memberUser := coderdtest.CreateAnotherUser(t, firstClient, firstUser.OrganizationID, rbac.RoleTemplateAdmin())
19491945

19501946
// Create a workspace with an agent
1951-
dbfake.WorkspaceBuild(t, api.Database, database.Workspace{
1952-
OrganizationID: firstUser.OrganizationID,
1953-
OwnerID: memberUser.ID,
1954-
}).WithAgent().Do()
1947+
firstWorkspace := buildWorkspaceWithAgent(t, member, firstUser.OrganizationID, memberUser.ID, api.Database, api.Pubsub)
19551948

19561949
u, err := member.URL.Parse("/api/v2/tailnet")
19571950
require.NoError(t, err)
@@ -1984,52 +1977,61 @@ func TestOwnedWorkspacesCoordinate(t *testing.T) {
19841977
})
19851978
require.NoError(t, err)
19861979

1987-
// Existing workspace
1980+
// First update will contain the existing workspace and agent
19881981
update, err := stream.Recv()
19891982
require.NoError(t, err)
19901983
require.Len(t, update.UpsertedWorkspaces, 1)
1991-
require.Equal(t, update.UpsertedWorkspaces[0].Status, tailnetproto.Workspace_RUNNING)
1992-
wsID := update.UpsertedWorkspaces[0].Id
1993-
1994-
// Existing agent
1984+
require.EqualValues(t, update.UpsertedWorkspaces[0].Id, firstWorkspace.ID)
19951985
require.Len(t, update.UpsertedAgents, 1)
1996-
require.Equal(t, update.UpsertedAgents[0].WorkspaceId, wsID)
1997-
1986+
require.EqualValues(t, update.UpsertedAgents[0].WorkspaceId, firstWorkspace.ID)
19981987
require.Len(t, update.DeletedWorkspaces, 0)
19991988
require.Len(t, update.DeletedAgents, 0)
20001989

20011990
// Build a second workspace
2002-
secondWorkspace := dbfake.WorkspaceBuild(t, api.Database, database.Workspace{
2003-
OrganizationID: firstUser.OrganizationID,
2004-
OwnerID: memberUser.ID,
2005-
}).WithAgent().Pubsub(api.Pubsub).Do()
1991+
secondWorkspace := buildWorkspaceWithAgent(t, member, firstUser.OrganizationID, memberUser.ID, api.Database, api.Pubsub)
20061992

20071993
// Wait for the second workspace to be running with an agent
20081994
expectedState := map[uuid.UUID]workspace{
2009-
secondWorkspace.Workspace.ID: {
1995+
secondWorkspace.ID: {
20101996
Status: tailnetproto.Workspace_RUNNING,
20111997
NumAgents: 1,
20121998
},
20131999
}
20142000
waitForUpdates(t, ctx, stream, map[uuid.UUID]workspace{}, expectedState)
20152001

20162002
// Wait for the workspace and agent to be deleted
2017-
secondWorkspace.Workspace.Deleted = true
2018-
dbfake.WorkspaceBuild(t, api.Database, secondWorkspace.Workspace).
2003+
secondWorkspace.Deleted = true
2004+
dbfake.WorkspaceBuild(t, api.Database, secondWorkspace).
20192005
Seed(database.WorkspaceBuild{
20202006
Transition: database.WorkspaceTransitionDelete,
20212007
BuildNumber: 2,
2022-
}).Pubsub(api.Pubsub).Do()
2008+
}).Do()
20232009

2024-
priorState := expectedState
2025-
waitForUpdates(t, ctx, stream, priorState, map[uuid.UUID]workspace{
2026-
secondWorkspace.Workspace.ID: {
2010+
waitForUpdates(t, ctx, stream, expectedState, map[uuid.UUID]workspace{
2011+
secondWorkspace.ID: {
20272012
Status: tailnetproto.Workspace_DELETED,
20282013
NumAgents: 0,
20292014
},
20302015
})
20312016
}
20322017

2018+
func buildWorkspaceWithAgent(
2019+
t *testing.T,
2020+
client *codersdk.Client,
2021+
orgID uuid.UUID,
2022+
ownerID uuid.UUID,
2023+
db database.Store,
2024+
ps pubsub.Pubsub,
2025+
) database.WorkspaceTable {
2026+
r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
2027+
OrganizationID: orgID,
2028+
OwnerID: ownerID,
2029+
}).WithAgent().Pubsub(ps).Do()
2030+
_ = agenttest.New(t, client.URL, r.AgentToken)
2031+
coderdtest.NewWorkspaceAgentWaiter(t, client, r.Workspace.ID).Wait()
2032+
return r.Workspace
2033+
}
2034+
20332035
func requireGetManifest(ctx context.Context, t testing.TB, aAPI agentproto.DRPCAgentClient) agentsdk.Manifest {
20342036
mp, err := aAPI.GetManifest(ctx, &agentproto.GetManifestRequest{})
20352037
require.NoError(t, err)
@@ -2134,6 +2136,6 @@ func waitForUpdates(
21342136
t.Fatal(err)
21352137
}
21362138
case <-ctx.Done():
2137-
t.Fatal("Timeout waiting for desired state")
2139+
t.Fatal("Timeout waiting for desired state", currentState)
21382140
}
21392141
}

0 commit comments

Comments
 (0)