Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[server] Restrict snapshot access based on repository access #8306

Merged
merged 1 commit into from
Feb 21, 2022

Conversation

jankeromnes
Copy link
Contributor

@jankeromnes jankeromnes commented Feb 18, 2022

Description

Restrict snapshot access based on repository access.

Also refactors:

  • GuardedSnapshot.workspace (simplified)
  • WorkspaceLogAccessGuardRepositoryResourceGuard
  • RepositoryService.canAccessHeadlessLogsRepositoryProvider.hasReadAccess

Related Issue(s)

Fixes #8257

How to test

  1. Create a snapshot for a private repo
  2. Verify that you can open the snapshot
  3. Verify that other members of the private repo can open the snapshot
  4. Verify that non-members of the private repo can not open the snapshot

Also:

  1. Create a snapshot for a public repo
  2. Verify that anyone can open the snapshot (even if they haven't connected the repo's Git provider)

Release Notes

Restrict snapshot access based on repository access

Documentation

@jankeromnes jankeromnes requested a review from a team February 18, 2022 11:29
@jankeromnes jankeromnes marked this pull request as draft February 18, 2022 11:29
@github-actions github-actions bot added the team: webapp Issue belongs to the WebApp team label Feb 18, 2022
@codecov
Copy link

codecov bot commented Feb 18, 2022

Codecov Report

Merging #8306 (0f83683) into main (1e5962d) will increase coverage by 2.75%.
The diff coverage is n/a.

❗ Current head 0f83683 differs from pull request most recent head 42f1a54. Consider uploading reports for the commit 42f1a54 to get more accurate results

Impacted file tree graph

@@            Coverage Diff            @@
##            main    #8306      +/-   ##
=========================================
+ Coverage   8.42%   11.17%   +2.75%     
=========================================
  Files         33       18      -15     
  Lines       2339      993    -1346     
=========================================
- Hits         197      111      -86     
+ Misses      2137      880    -1257     
+ Partials       5        2       -3     
Flag Coverage Δ
components-gitpod-cli-app 11.17% <ø> (ø)
components-installer-raw-app ?
components-local-app-app-darwin-amd64 ?
components-local-app-app-darwin-arm64 ?
components-local-app-app-linux-amd64 ?
components-local-app-app-linux-arm64 ?
components-local-app-app-windows-386 ?
components-local-app-app-windows-amd64 ?
components-local-app-app-windows-arm64 ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
components/local-app/pkg/auth/pkce.go
...components/ws-manager/unpriviledged-rolebinding.go
components/installer/pkg/common/ca.go
...onents/installer/pkg/components/ws-manager/role.go
components/installer/pkg/common/display.go
...s/installer/pkg/components/ws-manager/configmap.go
components/local-app/pkg/auth/auth.go
...installer/pkg/components/ws-manager/rolebinding.go
.../installer/pkg/components/ws-manager/deployment.go
components/installer/pkg/common/storage.go
... and 5 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1e5962d...42f1a54. Read the comment docs.

@jankeromnes jankeromnes marked this pull request as ready for review February 18, 2022 11:51
@jankeromnes
Copy link
Contributor Author

If you try to open a snapshot of a private repository you don't have access to, it now fails with:

Screenshot 2022-02-18 at 12 50 45

(We could also make this error screen nicer.)

@JanKoehnlein
Copy link
Contributor

LGTM codewise, haven't tested

@jankeromnes
Copy link
Contributor Author

Many thanks!

Holding because I'd like to see if I can detect the missing integration, and at least show a "Connect" button on the error screen in that case.

/hold

@jldec
Copy link
Contributor

jldec commented Feb 18, 2022

@jankeromnes I think the error screen needs a little love (per issue)

Showing the provider 403 error string is ok'ish, but because of the change relative to existing snapshot behavior, we also need to show a message like:

Snapshot URLs require read access to the underlying repository.
Please request access from the repository owner.

Ideally we would also include a link to the repo.
Screenshot 2022-02-18 at 13 18 53

geropl
geropl previously approved these changes Feb 18, 2022
Copy link
Member

@geropl geropl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code LGTM as well

I like the WorkspaceLogAccessGuard → RepositoryResourceGuard refactoring 👍

@jankeromnes
Copy link
Contributor Author

jankeromnes commented Feb 18, 2022

Okay, checking for a connected provider, and throwing a proper UnauthorizedError when absent, seems to do the trick (see the new Authorize button):

Screenshot 2022-02-18 at 17 10 49

However, after authorizing, starting my own snapshot for a public repo fails 🙄

Screenshot 2022-02-18 at 17 12 39

I'm not sure whether that's a bug in my changes, or a bug somewhere else in Gitpod / core-dev.

Checking the logs, I see a few worrying messages:

{
    "message": "unable to parse URL from normalized contextURL: 'snapshot/bb410d69-12cf-45f2-be53-ee7c030cf335'",
    "error": `TypeError [ERR_INVALID_URL]: Invalid URL
        at new NodeError (node:internal/errors:371:5)
        at onParseError (node:internal/url:552:9)
        at new URL (node:internal/url:628:5)
        at Object.getNormalizedURL (/app/node_modules/@gitpod/gitpod-protocol/lib/context-url.js:38:20)
        at RepositoryResourceGuard.<anonymous> (/app/node_modules/@gitpod/server/dist/src/auth/resource-access.js:379:61)
        at Generator.next (<anonymous>)
        at /app/node_modules/@gitpod/server/dist/src/auth/resource-access.js:13:71
        at new Promise (<anonymous>)
        at __awaiter (/app/node_modules/@gitpod/server/dist/src/auth/resource-access.js:9:12)
        at RepositoryResourceGuard.canAccess (/app/node_modules/@gitpod/server/dist/src/auth/resource-access.js:369:16)
        at /app/node_modules/@gitpod/server/dist/src/auth/resource-access.js:48:64
        at Array.map (<anonymous>)
        at CompositeResourceAccessGuard.<anonymous> (/app/node_modules/@gitpod/server/dist/src/auth/resource-access.js:48:53)
        at Generator.next (<anonymous>)
        at /app/node_modules/@gitpod/server/dist/src/auth/resource-access.js:13:71
        at new Promise (<anonymous>)`
}
{
    "message": "Request createWorkspace unsuccessful: 460/\"unable to parse ContextURL: undefined\"",
    "payload": {
        "method": "createWorkspace",
        "args": [
            {
                "contextUrl": "snapshot/f012085f-5c9e-4f1b-8d44-c6cac2d5fa17",
                "mode": "select-if-running",
                "forceDefaultConfig": false
            }, { "_isCancelled":false }
        ]
    }
}

and:

{
    "message": "cannot watch imagebuild logs for workspaceId",
    "error": `Error: upstream ended with status code: 2
        at /app/node_modules/@gitpod/server/dist/src/workspace/headless-log-service.js:135:37
        at /app/node_modules/@gitpod/supervisor-api-grpcweb/lib/status_pb_service.js:259:9
        at Array.forEach (<anonymous>)
        at onEnd (/app/node_modules/@gitpod/supervisor-api-grpcweb/lib/status_pb_service.js:258:21)
        at /app/node_modules/@improbable-eng/grpc-web/dist/grpc-web-client.js:1:11490
        at Array.forEach (<anonymous>)
        at e.rawOnError (/app/node_modules/@improbable-eng/grpc-web/dist/grpc-web-client.js:1:11452)
        at e.onTransportEnd (/app/node_modules/@improbable-eng/grpc-web/dist/grpc-web-client.js:1:10318)
        at WebSocket.ws.onclose (/app/node_modules/@gitpod/server/dist/src/util/grpc-web-ws-transport.js:86:25)
        at WebSocket.onClose (/app/node_modules/ws/lib/event-target.js:136:16)
        at WebSocket.emit (node:events:390:28)
        at WebSocket.emit (node:domain:475:12)
        at WebSocket.emitClose (/app/node_modules/ws/lib/websocket.js:236:12)
        at Object.onceWrapper (node:events:509:28)
        at ClientRequest.emit (node:events:390:28)
        at ClientRequest.emit (node:domain:475:12)`
}
{
    "message": "Request watchWorkspaceImageBuildLogs unsuccessful: 640/\"cannot watch imagebuild logs for workspaceId\"",
     "payload": {
        "method": "watchWorkspaceImageBuildLogs",
        "args": [ "bronze-goat-sbkglq6tewo", {"_isCancelled":false} ]
    }
}

Questions:

  • How does the RepositoryResourceGuard ever come across a snapshot contextURL? (It's supposed to only get the contextURL from the original workspace from which the snapshot was taken)
  • Why does opening my snapshot try to watch image build logs? (This should never happen for a snapshot, since it already has a fully-built-and-resolved image, right?)

@jankeromnes
Copy link
Contributor Author

jankeromnes commented Feb 18, 2022

Left to do:

  • Understand and maybe resolve the snapshot bugs mentioned above
  • Surface the message Snapshot URLs require read access to the underlying repository. Please request access from the repository owner. in the UI when snapshot access fails

@jankeromnes
Copy link
Contributor Author

jankeromnes commented Feb 19, 2022

Hmm, I've added some debug logs, but could not reproduce the errors above. I guess this works as expected now. ✅

I've also added a more specific error message when you don't have repo access:

Integration connected, repo access

Opening the snapshot works ✅

Integration connected, no repo access

Screenshot 2022-02-20 at 11 34 27

Integration not connected (can't verify access)

Screenshot 2022-02-19 at 14 37 11

I feel like we could still make these error pages look nicer somehow, but this could be a follow-up issue. 💭

Ready for final approval. 🚢

@jankeromnes jankeromnes force-pushed the jx/snapshot-access branch 2 times, most recently from 2cf8ab5 to a989331 Compare February 20, 2022 14:23
@@ -14,4 +14,5 @@ export interface RepositoryProvider {
getBranches(user: User, owner: string, repo: string): Promise<Branch[]>;
getCommitInfo(user: User, owner: string, repo: string, ref: string): Promise<CommitInfo | undefined>;
getUserRepos(user: User): Promise<string[]>;
hasReadAccess(user: User, owner: string, repo: string): Promise<boolean>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏻

Also refactor:
- Simplify GuardedSnapshot.workspace
- WorkspaceLogAccessGuard → RepositoryResourceGuard
- RepositoryService.canAccessHeadlessLogs → RepositoryProvider.hasReadAccess
Copy link
Member

@AlexTugarev AlexTugarev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jldec
Copy link
Contributor

jldec commented Feb 21, 2022

This looks good now @jankeromnes - thanks for improving the error message.

Copy link
Contributor

@jldec jldec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merci bien

@jankeromnes
Copy link
Contributor Author

jankeromnes commented Feb 21, 2022

Many thanks for the reviews! 🙏

Releasing the hold-down clamps, go for launch 🚀

/unhold

@roboquat roboquat merged commit 2d44392 into main Feb 21, 2022
@roboquat roboquat deleted the jx/snapshot-access branch February 21, 2022 10:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release-note size/L team: webapp Issue belongs to the WebApp team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Epic: Restrict access to snapshot URLs
6 participants