Page MenuHomePhabricator

Clarify Terms of Use on whether using Toolforge as a proxy for GitHub Actions is permitted
Closed, ResolvedPublic

Description

GitHub Actions is a great modern tool to automate software development. It could also be a great companion of code-related Wikimedia projects as it allows it integrate GitHub repos and wiki pages containing program code more closely. Unfortunately, there is an obstacle in the way of using GitHub Actions on wikis: the fact that Wikimedia wikis block everything that doesn't look like an end user IP (open proxies, hostings, etc.). Naturally, GitHub Actions' IP ranges are also blocked.

I see two ways out:

  1. Ask for an IP block exemption right.
  2. Use Toolforge as a proxy for GitHub Actions. As requests are routed through a SSH tunnel, any unauthorized use is excluded.

1 would require asking for an exemption on each wiki that has the IPs blocked locally (a global exemption doesn't overwrite that). Not very convenient for a person who seeks to automate. Also, not very necessary as the person's normal IP doesn't need an exemption, only one task he performs does.

2 seems perfect as it is a general solution that many tool creators could use. But there is a problem — https://wikitech.wikimedia.org/wiki/Wikitech:Cloud_Services_Terms_of_use currently states in "Prohibited Uses":

  1. Using Wikimedia Cloud Services as a network proxy: Do not use Wikimedia Cloud Services servers or projects to proxy or relay traffic for other servers. Examples of such activities include running Tor nodes, peer-to-peer network services, or VPNs to other networks. In other words, all network connections must originate from or terminate at Wikimedia Cloud Services.

So, using Toolforge as a proxy for GitHub Actions technically violates this rule, despite the fact that the proxy can be used only by a repository collaborator who is generally the tool maintainer, and what they do is directly wiki-related. All the examples given ("Tor nodes, peer-to-peer network services, or VPNs") seem irrelevant to the mentioned use case, but still "all network connections must originate from or terminate at Wikimedia Cloud Services" — the network connection originates at GitHub and terminates at a wiki. Perhaps a script on Toolforge could perform some additional tasks and not just pass a request from GitHub to a wiki through itself, but in essence it would still be a proxy.

Maybe the rules are not complete and/or I'm misinterpreting the rules, so I ask for an explanation and/or adjustments made to them.

Event Timeline

I think this should be done via an OAuth service in Toolforge.

@Jack_who_built_the_house Just to be clear before we dig into the specifics here, your hoped for end state is using GitHub Actions to edit pages on Wikimedia project wikis correct? Use Toolforge as a SSH proxy is slightly confusing as I think you are really in need of an HTTP reverse proxy to make your Action API calls look to the wikis like they are coming from a trusted network, but if I ignore that bit then I think that's my understanding of your issue.

using GitHub Actions to edit pages on Wikimedia project wikis correct

Right, this is the idea. I could have messed up with the terminology here, so feel free to correct it; @Yurik initially came up with the idea of using SSH for the API requests (made via HTTP, you're right), so maybe he could elaborate more.

@Yurik initially came up with the idea of using SSH for the API requests (made via HTTP, you're right), so maybe he could elaborate more.

My guess is that he mentioned something like "ssh tunnels could be used". In practice, it would be less complicated to run a webservice as a Toolforge tool that did the "proxy" action for you, which is the general idea that @Bugreporter suggested above.

The tricky part would be finding a way to handle authentication between the GitHub Action and the webservice. How tricky this problem is in part depends on if you are hoping to build a one-off tool or something that is reusable by others. You haven't described a full use case, but one that I can imagine is a gadget maintained in a GitHub repo and a GitHub Action based deployment system for that gadget that updates one or more target wikis when new commits are merged to a specific branch or maybe when a tag is made.

Right now I feel like there is an XY problem starting here. As asked, you are looking for a general solution to avoiding IP range blocks on the wikis. And you are very specifically looking for someone to say that using Toolforge as a proxy service to avoid those IP range blocks is ok. The answer to that from me is a very strong no, and I think very few folks would disagree with that. I have a hunch that your real goal is not generically avoiding IP range blocks. Maybe you could back up a step or two and clue us into a specific goal that you have for connecting GitHub and the wikis?

The gadget publication use case I mentioned above, for example, is solvable in several other ways. The most direct being a webservice running on Toolforge which accepts webhook notifications from GitHub and when it sees the right notification takes the action of fetching the gadget source from a trusted origin (the GitHub repo for example) and then publishes the source to the appropriate wiki. None of that requires treating Toolforge as an open proxy, or runs afoul of the Cloud Services or Toolforge terms of use.

My first impression is that I think fixing on-wiki permissions/blocks so whatever account can make edits regardless of the server it's running on.

I do wonder if on-wiki we've been too aggressive in IP blocking all of these CI hosts (or maybe they're too mixed into general cloud providers)? I ran into a similar issue when GitLab CI was blocked.

I should also say I'm skeptical about using Toolforge resources as a proxy for a proprietary/non-free service, getting the advantages of the Toolforge platform but bypassing the open source code requirements.

Goal

To automate the build & publish process, making it as simple and stable as possible. Desired workflow:

  • User publishes a new release of their tool
  • System automatically builds, tests, and publishes the result to the wiki

Possible Solution

GitHub actions offer a super simple way to do the above, allowing user to maintain the needed script inside their repo, and not rely on any additional services. These few lines do everything a user would need - build, test, create an SSH tunnel, and publish to the wiki. Note that there are no other moving parts involved - no services that allow some proxying or listen to webhook events, etc. Moreover, this same code can be copy/pasted to any other similar project, or someone could wrap the last step in a github action -- making it even simpler (there are already thousands of useful actions for all sorts of tasks)

.github/workflows/publish.yml
name: Test and publish

on:
  push:
    branches:
    - master

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v1
        with:
          node-version: 12
      - run: yarn
      - run: yarn build
      - run: yarn test
      - name: publish to wiki
        env:
          SSH_KEY: ${{ secrets.TOOLFORGE_KEY }}
          WIKI_USER: ${{ secrets.WIKI_USER }}
          WIKI_PASSWORD: ${{ secrets.WIKI_PASSWORD }}
        run: |
          mkdir -p ~/.ssh
          echo -e "${SSH_KEY//_/\\n}" > ~/.ssh/id_rsa
          chmod og-rwx ~/.ssh/id_rsa

          # Create a tunnel in the background, delete the key,
          # and set a TRAP to kill the tunnel on exit
          ssh -N -L ... &
          rm ~/.ssh/id_rsa
          trap "kill $!" EXIT

          # run user's own script to login and publish bulid results to a page
          yarn run publish

I am sure there are other ways to skin this cat, but the above seems to be by far the simpler. The only sticky point is the language of the policy -- because technically toolforge is being used as a proxy to access the wiki and bypass the IP block. Without it, the above script would be even simpler.

I should also say I'm skeptical about using Toolforge resources as a proxy for a proprietary/non-free service, getting the advantages of the Toolforge platform but bypassing the open source code requirements.

I'm a bit confused what you are referring to? All the code here is open source, and built specifically for Wikipedia.

You haven't described a full use case, but one that I can imagine is a gadget maintained in a GitHub repo and a GitHub Action based deployment system for that gadget that updates one or more target wikis when new commits are merged to a specific branch or maybe when a tag is made.

Right, my use case is the Convenient-Discussions tool, and this is basically what happens when you try to deploy to Commons (an error is reported in the "Run npm run deploy --dev" section).

The most direct being a webservice running on Toolforge which accepts webhook notifications from GitHub and when it sees the right notification takes the action of fetching the gadget source from a trusted origin (the GitHub repo for example) and then publishes the source to the appropriate wiki.

This is possible, but the whole point of using GitHub Actions is to automate building, testing, and publishing. A GitHub repo in general contains only the source files, not the build files, or you have to specifically clutter the repo with them. Can you suggest a design that would:

  • allow automatically publishing to wiki with appropriate edit summaries (i.e. containing the last commit messages);
  • delegate the building and testing phase to GitHub (or other server, but GitHub Actions has an UI made specifically for that);
  • not clutter the repo with the build files?

Some GitHub Action that would create a release on some trigger and initiate a webhook accepted by Toolforge maybe? Even if possible, wouldn't it be overcomplicated? Currently the script fetches the last edits on the target pages and analyzes the repo's last commits to prepare an appropriate edit summary containing only the relevant commit messages. All that part would have to move to Toolforge where the script doesn't have direct access to the repo.

As asked, you are looking for a general solution to avoiding IP range blocks on the wikis. And you are very specifically looking for someone to say that using Toolforge as a proxy service to avoid those IP range blocks is ok. The answer to that from me is a very strong no, and I think very few folks would disagree with that.

If you put the question this way, what is Toolforge if not a general solution to avoiding IP range blocks on the wikis? I don't see very much difference between proxying a request to Action API as it is and accepting a webhook and then making the same Action API request, except that the second way is a great deal more clumsy. Do you?

Some GitHub Action that would create a release on some trigger and initiate a webhook accepted by Toolforge maybe?

Actually, creating a release with archived build files will clutter the repo the same way, or even worse, as creating a folder with them will. Releases containing files don't make sense for my tool, since the build files are tied to the specific (Wikimedia in our case) environment, and there is no incentive for potential users to download them if they need another environment.

So, the only alternative I see is to make GitHub Actions connect to Toolforge and pass the files and text of edit summaries directly to it, then run a script on Toolforge that will update the wiki pages. Difference with proxying the request? Even less than in the example above.

If that will satisfy the rules and creating a SSH tunnel won't, I'm ready to do it. But that just doesn't make much sense to me, at least until somebody explains the difference to me.

If you will say this will not satisfy the rules, then I would argue that reacting to a webhook won't satisfy either. Because however you wrap it, in essence it would still be the same: publishing files located at GitHub to wikis.

I am sure there are other ways to skin this cat, but the above seems to be by far the simpler. The only sticky point is the language of the policy -- because technically toolforge is being used as a proxy to access the wiki and bypass the IP block. Without it, the above script would be even simpler.

The steps that @Yurik outlined will technical work, but they also bring a large attack surface. Problems that I see with the outlined ssh tunnel approach:

  • A ssh private key connected to a Toolforge maintainer's Developer account needs to be securely managed in a low-trust environment.
  • The ssh public key cannot be limited to a single action on the Toolforge side which means that if the private key leaks it could be used to impersonate the owning Developer account anywhere in Cloud VPS.
  • Activity over the ssh tunnel cannot be limited to a single action or narrow set of actions. The ssh tunnel destinations could be limited, but this is a layer 3 proxy setup, so you will not be able to employ layer 7 protections.

@bd808 I am not sure if the attack surface is significant, and also I don't believe this is relevant

  1. The key is stored in a secure environment (GitHub secrets store) -- relied on by the entire dev world to manage its secrets, including many large corporations (it is a paid service after all). While every system could be compromised (naturally), the chances of the key leaking are relatively low. Note that a similar approach is used by all other public CI systems (TravisCI, etc). Also note that exactly the same limitations are present in WMF Jenkins -- any automation must access secrets somewhere.
  2. See above
  3. Relevancy? If dev act is leaked (via many different routes including dev's machine compromise), they would have access to the limited and well monitored toolsforge. Considering that anyone could get an account with it, and that in the past non-honest parties did get it, I don't believe changing the language of the guidelines related to PROXY is relevant here

The issue here is that a legitimate use case is prevented by the language that was written to prevent other kinds of abuse. This is NOT an abuse scenario, therefor it should be excluded (which it technically already is by looking at the examples given in the rules).

Please remember that these are hoops that every non-paid contributor has to jump through. Are you sure you want to make it more difficult without offering anything as simple due to mitigating a single attack vector? Sounds a bit like building a wall that can be climbed over, dug under, or walked around :)

The most direct being a webservice running on Toolforge which accepts webhook notifications from GitHub and when it sees the right notification takes the action of fetching the gadget source from a trusted origin (the GitHub repo for example) and then publishes the source to the appropriate wiki.

This is possible, but the whole point of using GitHub Actions is to automate building, testing, and publishing. A GitHub repo in general contains only the source files, not the build files, or you have to specifically clutter the repo with them. Can you suggest a design that would:

  • allow automatically publishing to wiki with appropriate edit summaries (i.e. containing the last commit messages);
  • delegate the building and testing phase to GitHub (or other server, but GitHub Actions has an UI made specifically for that);
  • not clutter the repo with the build files?

I think I did that in T260746#6395254? I may have not gone into enough detail for you to follow my arguments however, so I'll try to provide more details to help you see what I think is a less risky solution.

Some GitHub Action that would create a release on some trigger and initiate a webhook accepted by Toolforge maybe? Even if possible, wouldn't it be overcomplicated? Currently the script fetches the last edits on the target pages and analyzes the repo's last commits to prepare an appropriate edit summary containing only the relevant commit messages. All that part would have to move to Toolforge where the script doesn't have direct access to the repo.

Your .github/workflows/deploy.yml file includes uses: actions/checkout@v2. This is functionally a call to git clone https://github.com/jwbth/convenient-discussions. This is exactly what you would do on the Toolforge side in response to a notification that a change had happened in the GitHub repo that should trigger a new build and deploy.

This shell script is the functional equivalent of your current deploy.yml:

$HOME/deploy.sh
#!/bin/bash
set -Eeuo pipefail

# Load environment variables from secrets file
SECRETS=$HOME/.secrets
set -o allexport
[[ -r $SECRETS ]] && source $SECRETS
set +o allexport

# Prepare working dir
WORK_DIR=$HOME/convenient-discussions
[[ ! -d $WORK_DIR ]] || git clone https://github.com/jwbth/convenient-discussions $WORK_DIR

cd $WORK_DIR
git fetch origin
git reset --hard origin/master

npm ci
npm run build --dev
npm run deploy --dev
$HOME/.secrets
USERNAME=MyWikiUserName
PASSWORD=this is ideally a bot password (https://www.mediawiki.org/wiki/Special:BotPasswords)

The only part of your current deploy workflow that can't be matched 1-to-1 on Toolforge is using Node.js v14. The newest version we currently support is Node.js v10.15.2. That version is only available on the Kubernetes cluster as well, so your webservice handling the webhook notification would currently need to be written in node.js as well so that it would have the ability to shell out to node.js v10. There does seem to be a nice library for writing these sorts of webservices: https://github.com/rvagg/github-webhook-handler

@bd808 thanks for the good example. It does provide a workaround, but it also highlights some issues:

  • The code that sets up/listens/triggers webhooks lives outside of the repo, and has to be essentially a separately maintained service, and has to be set up by hand. In github actions, it is part of the master branch, so it evolves as needs change, and changing part of the build process is as simple as another commit. This puts an undue burden on the contributor in the name of security, which is not really mitigated - see below.
  • When built, the code is actually as insecure as having an SSH key at github because it runs any code as given by github's master under user's credentials.
  • When built, the code is not built in a sandbox, thus the incorrect user settings affect user's toolforge account. Github actions actually run in a sandbox, so it would be more difficult to mess up user's account (has to be more deliberate)
  • The build process is severely limited only by tools available at toolforge, and it does not allow arbitrary docker images to be used for building or testing (e.g. I cannot easily spin-up a test wikipedia server and run tests against it - whereas I can totally do that on github actions with just one more line in the yaml)

The above shows that without a significant gain we put extra burden on the non-paid contributors who just want to get something working using very simple off-the-shelf well documented and understood tooling, with a huge community support behind that tooling (stackoverflow, etc)

@bd808 Thanks for the demonstration, but if having to choose between:

  • building and testing at Toolforge and
  • say, setting up GitHub Actions to upload the content that needs to be posted to wiki to some place X (perhaps some third-party server or the artifacts feature in GitHub Actions if it's possible to reveal the artifacts to the outside world) and send a webhook that Toolforge will accept, pick up the content at the place X and then post it to wiki,

given my current knowledge, I would choose the latter. This is perhaps an extreme example, but if you say that even GitHub can't be trusted with the SSH credentials, then we shouldn't even try to connect to Toolforge from GitHub Actions except via webhook (even though I didn't see where Cloud Services or Toolforge terms of use forbid transferring credentials to entities like GitHub).

When built, the code is actually as insecure as having an SSH key at github because it runs any code as given by github's master under user's credentials.

Exactly. So no matter via a SSH tunnel or via the repo content—we gave the code full freedom to send any requests it wants using "a general solution to avoiding IP range blocks on the wikis" that is Toolforge 😉 the second when we allowed any connection between GitHub and Toolforge. The method using a SSH tunnel is even less risky as it only allows to make requests, but not run arbitrary code, if I'm getting it right.

@Jack_who_built_the_house well, if GitHub has your ssh key, in theory any software that can get access to it could establish any kind of connection, including a regular session to run arbitrary commands. But if you establish a tunnel and unset the key right away, any subsequently ran software can only create socket connections, not run commands (at least that's my understanding).

Thank you @Jack_who_built_the_house for the original question and your responses to my clarifying questions and strawdog proposals.

I personally do not like the ssh tunnel idea, and I hope I have outlined why in a way that is understandable even to those who disagree with my evaluation. The root question posed by this task however is not a security review of the idea, but a ruling on the "legality" of using ssh tunnels to Toolforge to work around IP range blocks on-wiki. I have asked the rest of the cloud-services-team to read over this task and think about this topic. We will have a discussion as a group at our next scheduled team meeting (2020-08-26) and update this task with the result of that discussion.

@Jack_who_built_the_house I want to thank you for the question and your patience in allowing us time to think about your proposal. As mentioned, the cloud-services-team discussed this yesterday and we are happy to support this request. In return however, we would ask a couple things of you.

  1. Please use a dedicated developer account for this. This will slightly minimize risks for yourself and others should the key be compromised in some way, and make it easier for all of us to monitor.
  2. We also would love to see a better workflow for what's being asked here, and would love to support a build service for toolforge! @Yurik outlined it well above I think. Push a new release of a tool, and toolforge then handles the build, test, and release. When we are ready to take on this work, our ask is that you help us design and provide feedback on what you would desire of such a service.

Thanks again for the discussion, and I trust you will be mindful when implementing this. Please do update us once you've got it setup.

nskaggs claimed this task.

I'm closing this as resolved as we've clarified usage in this case. However, we would still love to hear implementation details! If there's other loose ends, please feel free to notify and re-open.

Should the terms page be updated, possibly with a link to this discussion?

@Yurik thank you. I've updated the terms page noting the exception and linked to this discussion.

@Jack_who_built_the_house We are testing out buildpacks on Toolforge. Can you take a look to see if it can help you migrate using a custom image?
There's a short quickstart here.
Kindly take a look and let us know.

I'm out of free time unfortunately. Maybe @SD0001 is interested to take a look?

@Jack_who_built_the_house We are testing out buildpacks on Toolforge. Can you take a look to see if it can help you migrate using a custom image?
There's a short quickstart here.
Kindly take a look and let us know.

(I might take a look too if there is a more detailed guide; currently, Convenient Discussions' GitHub Actions is quite elaborated, and it would require some effort to migrate it anywhere.)

I think buildpacks are a great tool for building custom images capable of running webservices that toolforge doesn't otherwise support, but not so much for a use case like this. Reviewing the conversation above, it appears the concern with Convenient Discussions' current deployment setup is the need to store a secret (SSH key) in GitHub secrets which cannot be narrowly scoped to perform a specific function (T260746#6398423). Now, there does exist https://github.com/wikimedia-gadgets/deploy-action that eliminates this concern (instead of storing an SSH key, you store an OAuth2 token or botpassword instead - which can indeed be scoped to specific actions). The action internally calls an API hosted on Toolforge.

Broadly, it's better to have build and tests run on GitHub itself, as many of the concerns with running them on Toolforge outlined in T260746#6398619 still hold even with buildpacks (build process would not be sandboxed, spinning up a MediaWiki instance for integration tests wouldn't be possible, etc).

(I might take a look too if there is a more detailed guide; currently, Convenient Discussions' GitHub Actions is quite elaborated, and it would require some effort to migrate it anywhere.)

We continue to add more details to the quickstart guide. Still a WIP.
We are also tracking known limitations and common issues with current implementation.