finish wip: whitelist & readme

This commit is contained in:
Lamp 2022-12-23 02:19:20 -06:00
parent caa44a4c19
commit 6f3dacb565
2 changed files with 38 additions and 41 deletions

@ -1,41 +1,44 @@
# ActivityPub Proxy
A simple proxy for ActivityPub that lets you circumvent blocks by masquerading as another domain name. All it does is replace all hostnames in the text proxied through, and for signed POST requests, it swaps the public keys and re-signs the requests.
A **simple** proxy for ActivityPub that lets you circumvent blocks by masquerading as another domain name. All it does is replace all hostnames in the text proxied through, and for signed POST requests, it swaps the public keys and re-signs the requests.
To use it, type a user's handle, replace the dots in their domain with hyphens, and add `.proxydomain` to the end. (If the domain already has hyphens, replace them with double hypens.) So for example, say you want to follow @kingu_platypus_gidora@octodon.social but the woke administrator has blocked you (or your instance blocked them wtf mastodon.social??), and you have a proxy at *.activitypub-proxy.cf: that would make @kingu_platypus_gidora@octodon-social.activitypub-proxy.cf which you can theoretically follow and fully interact with just like the real user. The person on the other side will see your tag proxied the same way, `@yourname@your-domain.your.proxy`, and they can follow and interact with you back.
The ideal application is to set up your own proxy with a whitelist to prevent abuse, as otherwise anyone would be able to proxy anything.
## Environment Variables
### `TARGET_WHITELIST`
Comma-separated list of target domains to allow, that is the servers the proxy will send GET or POST requests to.
### `CLIENT_WHITELIST`
Comma-separated list of client domains to allow, that is the servers that the proxy will accept requests from.
### `DOMAIN_WHITELIST`
Combination of the previous two for simpler configuration. If set, will override.
### `USER_WHITELIST`
Comma-separated list of user names to allow.
### `PORT`
The port to listen to for HTTP. Default: 80
### `BIND_IP`
The IP address for the HTTP server to listen on. Default: all
To use it, type a user's handle, replace the dots in their domain with hyphens, and add `.yourproxydomain` to the end. (If the domain already has hyphens, replace them with double hypens.) So for example, say you want to follow @kingu_platypus_gidora@octodon.social but the woke administrator has blocked you (or your instance blocked them wtf mastodon.social??), and you have a proxy at *.activitypub-proxy.cf: that would make @kingu_platypus_gidora@octodon-social.activitypub-proxy.cf which you can theoretically follow and fully interact with just like the real user. The person on the other side will see your tag proxied the same way, `@yourname@your-domain.your.proxy`, and they can follow and interact with you back.
## Installation
You will need a wildcard domain with HTTP and a host. The easiest way is to use Cloudflare.
You will need a host with Node.js 15 or newer, and a wildcard domain with HTTPS pointed to your server. Cloudflare may be easiest, as you can bind the app to an extra IP address and connect Cloudflare directly to it.
Download the repository and `npm i`. Then you can run it with the following environment variables.
- `PORT`: The port to listen for HTTP, default: 80.
- `BIND_IP`: The IP address to bind to. Default: all.
- `DOMAIN_WHITELIST`: Comma-separated list of domains that can use the proxy. Recommended to use this to prevent abuse as otherwise anyone can proxy anything. Remember to include the domains you want to follow from as well as the domains you want to follow, as it needs to work both ways.
- `USER_WHITELIST`: Comma-separated list of names that can be looked up. Maybe useful if you don't want other users messing with other users... 🤷 Note that this only restricts the webfinger. (todo could be circumvented? 🤔) Default: any
- `NODE_ENV`: Set to `development` to see debug logs.
Install by copying and pasting this example systemd file to `/etc/systemd/system/ap-proxy.service` (or similar), and editing as needed:
```systemd
[Unit]
Description=Simple ActivityPub Proxy
Documentation=https://gitea.moe/lamp/activitypub-proxy
After=network.target
[Service]
Environment= PORT=80 BIND_IP=0.0.0.0 DOMAIN_WHITELIST=mastodon.social,mstdn.social
WorkingDirectory=/path/to/activitypub-proxy/
ExecStart=/usr/bin/node .
[Install]
WantedBy=multi-user.target
```
`systemctl enable --now ap-proxy` and Bob's your uncle.
## Known issues
- Sending AP messages to Pleroma makes it 500 Internal Server Error for no obvious reason
- Since it simply replaces all instances of the domain names in the raw JSON text, mentions of those names in post content will be replaced as well. Although this makes it more likely to work with _anything_ with less code, a stricter version that parses deeper into the protocol might be of better quality.
If any issues please submit!

@ -3,13 +3,7 @@ if (process.env.NODE_ENV != "development") {
console.debug = () => {};
}
if (process.env.DOMAIN_WHITELIST) {
let DOMAIN_WHITELIST = process.env.DOMAIN_WHITELIST.split(',').map(x=>x.trim().toLowerCase()).filter(x=>x);
var TARGET_WHITELIST = DOMAIN_WHITELIST, CLIENT_WHITELIST = DOMAIN_WHITELIST;
} else {
if (process.env.TARGET_WHITELIST) var TARGET_WHITELIST = process.env.TARGET_WHITELIST.split(',').map(x=>x.trim().toLowerCase()).filter(x=>x);
if (process.env.CLIENT_WHITELIST) var CLIENT_WHITELIST = process.env.CLIENT_WHITELIST.split(',').map(x=>x.trim().toLowerCase()).filter(x=>x);
}
if (process.env.DOMAIN_WHITELIST) var DOMAIN_WHITELIST = process.env.DOMAIN_WHITELIST.split(',').map(x=>x.trim().toLowerCase()).filter(x=>x);
if (process.env.USER_WHITELIST) var USER_WHITELIST = process.env.USER_WHITELIST.split(',').map(x=>x.trim().toLowerCase()).filter(x=>x);
var express = require("express");
@ -47,12 +41,12 @@ app.use(async (req, res, next) => {
if (!req.subdomains[0]) return next();
var TARGET_NODE = req.subdomains[0].replaceAll(/(?<!-)-(?!-)/g, '.').replaceAll('--','-');
if (TARGET_WHITELIST && !TARGET_WHITELIST.includes(TARGET_NODE)) return res.status(403).send(`target ${TARGET_NODE} is not whitelisted`);
if (DOMAIN_WHITELIST && !DOMAIN_WHITELIST.includes(TARGET_NODE)) return res.status(403).send(`target ${TARGET_NODE} is not whitelisted`);
var TARGET_MASQUERADE = req.hostname;
var TARGET_REGEXP = new RegExp(`(?<!\\.)${TARGET_NODE.replaceAll('.','\\.')}`, 'gi');
var CLIENT_NODE = req.get("User-Agent").match(/(?<=https:\/\/)[a-z0-9-\.]+/i)?.[0];
if (CLIENT_WHITELIST && !CLIENT_WHITELIST.includes(CLIENT_NODE)) return res.status(403).send(`client ${CLIENT_NODE} is not whitelisted`);
if (DOMAIN_WHITELIST && !DOMAIN_WHITELIST.includes(CLIENT_NODE)) return res.status(403).send(`client ${CLIENT_NODE} is not whitelisted`);
if (CLIENT_NODE) var CLIENT_MASQUERADE = [CLIENT_NODE.replaceAll('-','--').replaceAll('.','-'), ...req.hostname.split('.').slice(-2)].join('.');
if (USER_WHITELIST && req.url.startsWith("/.well-known/webfinger") && !USER_WHITELIST.includes(req.query.resource?.replace('acct:','').split('@')[0])) return res.status(403).send("user not whitelisted");