Skip to content

Commit 2fb2908

Browse files
committed
auth tunnels via updater
1 parent ab6e559 commit 2fb2908

File tree

7 files changed

+36
-38
lines changed

7 files changed

+36
-38
lines changed

coderd/workspaceagents.go

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1492,7 +1492,6 @@ func (api *API) workspaceAgentsExternalAuthListen(ctx context.Context, rw http.R
14921492
func (api *API) tailnet(rw http.ResponseWriter, r *http.Request) {
14931493
ctx := r.Context()
14941494
owner := httpmw.UserParam(r)
1495-
ownerRoles := httpmw.UserAuthorization(r)
14961495

14971496
// Check if the actor is allowed to access any workspace owned by the user.
14981497
if !api.Authorize(r, policy.ActionSSH, rbac.ResourceWorkspace.WithOwner(owner.ID.String())) {
@@ -1540,32 +1539,16 @@ func (api *API) tailnet(rw http.ResponseWriter, r *http.Request) {
15401539

15411540
go httpapi.Heartbeat(ctx, conn)
15421541
err = api.TailnetClientService.ServeUserClient(ctx, version, wsNetConn, tailnet.ServeUserClientOptions{
1543-
PeerID: peerID,
1544-
UserID: owner.ID,
1545-
AuthFn: authAgentFn(api.Database, api.Authorizer, &ownerRoles),
1542+
PeerID: peerID,
1543+
UserID: owner.ID,
1544+
UpdatesProvider: api.WorkspaceUpdatesProvider,
15461545
})
15471546
if err != nil && !xerrors.Is(err, io.EOF) && !xerrors.Is(err, context.Canceled) {
15481547
_ = conn.Close(websocket.StatusInternalError, err.Error())
15491548
return
15501549
}
15511550
}
15521551

1553-
// authAgentFn accepts a subject, and returns a closure that authorizes against
1554-
// passed agent IDs.
1555-
func authAgentFn(db database.Store, auth rbac.Authorizer, user *rbac.Subject) func(context.Context, uuid.UUID) error {
1556-
return func(ctx context.Context, agentID uuid.UUID) error {
1557-
ws, err := db.GetWorkspaceByAgentID(ctx, agentID)
1558-
if err != nil {
1559-
return xerrors.Errorf("get workspace by agent id: %w", err)
1560-
}
1561-
err = auth.Authorize(ctx, *user, policy.ActionSSH, ws.RBACObject())
1562-
if err != nil {
1563-
return xerrors.Errorf("workspace agent not found or you do not have permission: %w", sql.ErrNoRows)
1564-
}
1565-
return nil
1566-
}
1567-
}
1568-
15691552
// createExternalAuthResponse creates an ExternalAuthResponse based on the
15701553
// provider type. This is to support legacy `/workspaceagents/me/gitauth`
15711554
// which uses `Username` and `Password`.

coderd/workspaceupdates.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,24 @@ type updatesProvider struct {
9191
cancelFn func()
9292
}
9393

94+
func (u *updatesProvider) IsOwner(userID uuid.UUID, agentID uuid.UUID) error {
95+
u.mu.RLock()
96+
defer u.mu.RUnlock()
97+
98+
workspaces, exists := u.latest[userID]
99+
if !exists {
100+
return xerrors.Errorf("workspace agent not found or you do not have permission: %w", sql.ErrNoRows)
101+
}
102+
for _, workspace := range workspaces {
103+
for _, agent := range workspace.Agents {
104+
if agent.ID == agentID {
105+
return nil
106+
}
107+
}
108+
}
109+
return xerrors.Errorf("workspace agent not found or you do not have permission: %w", sql.ErrNoRows)
110+
}
111+
94112
var _ tailnet.WorkspaceUpdatesProvider = (*updatesProvider)(nil)
95113

96114
func NewUpdatesProvider(ctx context.Context, db UpdateQuerier, ps pubsub.Pubsub) (tailnet.WorkspaceUpdatesProvider, error) {

enterprise/tailnet/connio.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ var errDisconnect = xerrors.New("graceful disconnect")
133133

134134
func (c *connIO) handleRequest(req *proto.CoordinateRequest) error {
135135
c.logger.Debug(c.peerCtx, "got request")
136-
err := c.auth.Authorize(c.coordCtx, req)
136+
err := c.auth.Authorize(req)
137137
if err != nil {
138138
c.logger.Warn(c.peerCtx, "unauthorized request", slog.Error(err))
139139
return xerrors.Errorf("authorize request: %w", err)

tailnet/coordinator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ func (c *core) handleRequest(p *peer, req *proto.CoordinateRequest) error {
577577
return ErrAlreadyRemoved
578578
}
579579

580-
if err := pr.auth.Authorize(context.Background(), req); err != nil {
580+
if err := pr.auth.Authorize(req); err != nil {
581581
return xerrors.Errorf("authorize request: %w", err)
582582
}
583583

tailnet/service.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type WorkspaceUpdatesProvider interface {
4343
Subscribe(peerID uuid.UUID, userID uuid.UUID) (<-chan *proto.WorkspaceUpdate, error)
4444
Unsubscribe(peerID uuid.UUID)
4545
Stop()
46+
IsOwner(userID uuid.UUID, agentID uuid.UUID) error
4647
}
4748

4849
type ClientServiceOptions struct {
@@ -119,11 +120,9 @@ func (s *ClientService) ServeClient(ctx context.Context, version string, conn ne
119120
}
120121

121122
type ServeUserClientOptions struct {
122-
PeerID uuid.UUID
123-
UserID uuid.UUID
124-
// AuthFn authorizes the user to `ActionSSH` against the workspace given
125-
// an agent ID.
126-
AuthFn func(context.Context, uuid.UUID) error
123+
PeerID uuid.UUID
124+
UserID uuid.UUID
125+
UpdatesProvider WorkspaceUpdatesProvider
127126
}
128127

129128
func (s *ClientService) ServeUserClient(ctx context.Context, version string, conn net.Conn, opts ServeUserClientOptions) error {
@@ -136,7 +135,6 @@ func (s *ClientService) ServeUserClient(ctx context.Context, version string, con
136135
case 2:
137136
auth := ClientUserCoordinateeAuth{
138137
UserID: opts.UserID,
139-
AuthFn: opts.AuthFn,
140138
}
141139
streamID := StreamID{
142140
Name: "client",

tailnet/service_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func TestClientService_ServeClient_V2(t *testing.T) {
7474
require.NotNil(t, call)
7575
require.Equal(t, call.ID, clientID)
7676
require.Equal(t, call.Name, "client")
77-
require.NoError(t, call.Auth.Authorize(ctx, &proto.CoordinateRequest{
77+
require.NoError(t, call.Auth.Authorize(&proto.CoordinateRequest{
7878
AddTunnel: &proto.CoordinateRequest_Tunnel{Id: agentID[:]},
7979
}))
8080
req := testutil.RequireRecvCtx(ctx, t, call.Reqs)

tailnet/tunnel.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package tailnet
22

33
import (
4-
"context"
54
"database/sql"
65
"net/netip"
76

@@ -14,13 +13,13 @@ import (
1413
var legacyWorkspaceAgentIP = netip.MustParseAddr("fd7a:115c:a1e0:49d6:b259:b7ac:b1b2:48f4")
1514

1615
type CoordinateeAuth interface {
17-
Authorize(ctx context.Context, req *proto.CoordinateRequest) error
16+
Authorize(req *proto.CoordinateRequest) error
1817
}
1918

2019
// SingleTailnetCoordinateeAuth allows all tunnels, since Coderd and wsproxy are allowed to initiate a tunnel to any agent
2120
type SingleTailnetCoordinateeAuth struct{}
2221

23-
func (SingleTailnetCoordinateeAuth) Authorize(context.Context, *proto.CoordinateRequest) error {
22+
func (SingleTailnetCoordinateeAuth) Authorize(*proto.CoordinateRequest) error {
2423
return nil
2524
}
2625

@@ -29,7 +28,7 @@ type ClientCoordinateeAuth struct {
2928
AgentID uuid.UUID
3029
}
3130

32-
func (c ClientCoordinateeAuth) Authorize(_ context.Context, req *proto.CoordinateRequest) error {
31+
func (c ClientCoordinateeAuth) Authorize(req *proto.CoordinateRequest) error {
3332
if tun := req.GetAddTunnel(); tun != nil {
3433
uid, err := uuid.FromBytes(tun.Id)
3534
if err != nil {
@@ -66,7 +65,7 @@ type AgentCoordinateeAuth struct {
6665
ID uuid.UUID
6766
}
6867

69-
func (a AgentCoordinateeAuth) Authorize(_ context.Context, req *proto.CoordinateRequest) error {
68+
func (a AgentCoordinateeAuth) Authorize(req *proto.CoordinateRequest) error {
7069
if tun := req.GetAddTunnel(); tun != nil {
7170
return xerrors.New("agents cannot open tunnels")
7271
}
@@ -93,17 +92,17 @@ func (a AgentCoordinateeAuth) Authorize(_ context.Context, req *proto.Coordinate
9392
}
9493

9594
type ClientUserCoordinateeAuth struct {
96-
UserID uuid.UUID
97-
AuthFn func(context.Context, uuid.UUID) error
95+
UserID uuid.UUID
96+
UpdatesProvider WorkspaceUpdatesProvider
9897
}
9998

100-
func (a ClientUserCoordinateeAuth) Authorize(ctx context.Context, req *proto.CoordinateRequest) error {
99+
func (a ClientUserCoordinateeAuth) Authorize(req *proto.CoordinateRequest) error {
101100
if tun := req.GetAddTunnel(); tun != nil {
102101
uid, err := uuid.FromBytes(tun.Id)
103102
if err != nil {
104103
return xerrors.Errorf("parse add tunnel id: %w", err)
105104
}
106-
err = a.AuthFn(ctx, uid)
105+
err = a.UpdatesProvider.IsOwner(a.UserID, uid)
107106
if err != nil {
108107
return xerrors.Errorf("workspace agent not found or you do not have permission: %w", sql.ErrNoRows)
109108
}

0 commit comments

Comments
 (0)