odoo / odoo/runbot

Mergebot as a Github application

Open
#669 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

mergebot
Dominant language
Python
Stars
159
Forks
156
Avg merge
1d 13h
Merged PRs (30d)
16

Description

Motivation

PATs are a problem, along multiple axis:

  1. they are a 1:1 representation of a user account's accesses (with some
    restrictions but e.g. repo is necessary to do anything with private
    repositories and it's basically full unfettered access), across all
    organizations and repositories, this makes them extremely dangerous
  2. they can not be audited or managed by organizations beyond a completely
    binary "allow all" / "deny all"
  3. as a consequence of (2), it is not possible for Odoo to deny PATs
    (and require e.g. FGTs) as long as infrastructure tools like the mergebot
    need PATs
FGTs are not a solution (for the mergebot)

FGTs exist as a finer-grained and more auditable alternative to PATs, however
they do not work for the mergebot: an FGT is strongly bound to one owner.

This means cross-owner operations can only be performed between public
repositories: given two organizations A and B, and the repositories A.r and
B.r (a fork of A.r), creating a PR from B.r to A.r requires content (ref)
read access to B.r and PR write access to A.r. If both repositories are
private, this is infeasible, because an FGT can only get access to the private
repositories of the owner they are bound to.

Apps do work

By default ("installation access") github apps behave like FGTs, except in
the access token can be even more restricted than the application, and the
access token only has a 1h lifetime.

However apps also have user access tokens, so if they are installed
in a user's account and multiple organizations (/ repositories?) using that
mode they can interact across organizations. This is a more PAT-like usage
but still requires opt-in (installing the app) and respects the application's
restrictions. UATs also have an 8h lifetime after which they need to be
refreshed so the UAT is a bit less of a security risk than an FGT let alone
PAT. The refresh token is a lot more valuable, however because it's consumed
on refresh if the refresh token is leaked you should know within 8h (however if
someone has continuous access to the instance they could just read the UAT every
time it's refreshed...).

Changing the permission requirements of an app will also require that the
owner applies the upgrade to their installation(s).

As such apps can do the job for better security, but at the cost of a
complexity increase:

  • needs to store multiple "key to the kingdom" secrets (private key, client secret
    uat/refresh token pair)

  • needs to track installations in order to generate IATs

  • needs to track an (UAT, refresh token) pair per "actor"

    this is mitigation by only needing one such, in order to create PRs...

TODO

  • check if scoped tokens can be derived from IATs
    they can't, only UATs
    • check if a user with read access to A and write to B can install the
      application and grant it the ability to push branches to B and create
      PRs in A
      • yes, being a read collaborator on A is sufficient to create a PR
        there, don't even need to be associated with the org
  • check if an app owned by one org can be installed by an other org, or a
    user (purpose: have odoo own the mergebot app, with odoo-dev/robodoo/...
    installing it from there, rather than have robodoo own the app)
    • yes
  • use X-Accepted-GitHub-Permissions to figure the exact ACL
    • POST /orgs/{org}/repos administration=write
    • GET /repos/{owner}/{repo} metadata=read
    • POST /repos/{owner}/{repo}/forks None
    • GET /repos/{owner}/{repo}/branches/dsfasfsfsaf contents=read
    • POST /repos/{owner}/{repo}/git/trees contents=write
    • POST /repos/{owner}/{repo}/git/refs contents=write; contents=write,workflows=write
    • GET /repos/{owner}/{repo}/issues/{number} issues=read (even if it's a PR)
    • POST /repos/{owner}/{repo}/pulls pull_requests=write
      This one is a bit weird because the app needs pull_requests=write but the
      user only needs read on the repository.
    • GET /repos/{owner}/{repo}/pulls/{number}/commits pull_requests=read
    • POST /repos/{owner}/{repo}/issues/{number}/comments issues=write; pull_requests=write
    • PUT /repos/{owner}/{repo}/issues/{number}/labels issues=write; pull_requests=write
    • PATCH /repos/{owner}/{repo}/pulls/{number} pull_requests=write
    • POST /repos/{owner}/{repo}/issues/comments/{id}/reactions issues=write; pull_requests=write
    • GET /repos/{owner}/{repo}/commits/{ref}/status ?commit_statuses=read
  • add apps support to DC (ugh...)
  • split API config into a separate object, with abstract token retrieval
  • add scope information to token retrieval
    • ?
  • user access flow requires a bunch of endpoints of its own
  • use /user/installations to check that cross-org interactions can work
  • allow supporting app webhooks somehow (probably have the webhooks
    controller feed into the API config object)
  • provide an app manifest so third parties can deploy their own
    mergebot-as-app
  • apps can be configured to bypass rulesets protection
Critical interactions
  • "App" endpoints (app-level operations) require JWT signed with (one
    of) the app's private keys.
  • Refreshing UATs require the client secret and refresh token. There is no
    way (that I found) to check the validity of a UAT aside from trying to use it,
    so might be nice to associate a pessimistic lifetime to UATs on retrieval,
    also maybe only store UAT and IAT in memory? Unlogged table?
    • IATs come with an expires_at
    • if the app is configured with non-expiring tokens, the response of the
      auth flow will contain just the access_token
  • "Installation" endpoints require IATs.
  • General API access can use either IAT or UAT depending on their action role
    (and requirements, mostly, for us), the main effect is the actor the action is
    associated with: with an IAT, it's the application's [Bot] user whereas with
    a UAT it's the user whose account granted the authorization
  • git network operations (push/pull) require using https and the
    x-access-token user

Limitations

  • issues:write / pull_requests:write is necessary to leave comments on issues
    and PRs (respectively) for private repositories,
  • scoped tokens have to be bound to a specific owner (target/target_id)
    so they create the same restriction as FGT/IAT when it comes to the one
    thing I need them for: trying to create a cross-org PR fails with "not all
    refs are readable" (same as FGT).
  • github apps can't fork private repositories cross-orgs, which means workarounds
    are required for testing interactions between private forks

Additional features

  • IAT means apps interact as apps, rather than impersonating a user

  • automatic webhooks setup

  • IP whitelisting

  • webhooks for installation, repository addition (/ creation)

  • checks API

  • multiple private keys and client secrets can be active at the same time, making those easy to rotate

    UAT/refresh tokens, not so much [nb: to test but IIRC if a new UAT is created, even through a full auth flow, the previous one is invalidated]

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the TODO and the critical interactions sections, then trace the existing API config and webhook controller entry points mentioned there. Map app, installation, and user-access-token flows, including scope and token storage requirements. Done means Mergebot can use GitHub Apps for its required operations, webhooks and configuration are supported, and the listed permission constraints are handled.

Written by the indexing model from the issue text.

Assessment

Tech stack
github, python
Domain
authentication, authorization, backend-api-design, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.