Skip to content

Commit 84730b8

Browse files
committed
rbac filter
1 parent 2fbec4b commit 84730b8

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy