-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b7adcde
commit 59878bc
Showing
7 changed files
with
139 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import { promisify } from 'node:util'; | ||
import { exec } from 'node:child_process'; | ||
|
||
// Note: this script is adapted from an earlier shell script, hence its use of shell scripting commands where there are perhaps Node APIs that might do the job in a clearer fashion | ||
|
||
async function runShellCommands(commands: string[]) { | ||
for (const command of commands) { | ||
console.log('Running command:', command); | ||
await promisify(exec)(command); | ||
} | ||
} | ||
|
||
async function createMitmproxyUserLinux() { | ||
// This user comes from when originally following the mitmproxy documentation (i.e. intercepting _all_ traffic except that from this user). We no longer do this, but I guess there’s no harm in having a separate user instead of running as root. | ||
await runShellCommands(['sudo useradd --create-home mitmproxyuser']); | ||
} | ||
|
||
async function createTestsUserLinux() { | ||
// Create a user to run the tests | ||
await runShellCommands(['sudo useradd --create-home ably-test-user']); | ||
} | ||
|
||
async function createGroupLinux() { | ||
await runShellCommands([ | ||
'sudo groupadd ably-test-users', | ||
// Add relevant users to the group | ||
'sudo usermod --append --groups ably-test-users $USER', | ||
'sudo usermod --append --groups ably-test-users mitmproxyuser', | ||
'sudo usermod --append --groups ably-test-users ably-test-user', | ||
]); | ||
} | ||
|
||
async function sortOutPermissions() { | ||
await runShellCommands([ | ||
// Give the group ownership of the working directory and everything under it... | ||
'sudo chown -R :ably-test-users .', | ||
// ...and give group members full read/write access to its contents (i.e. rw access to files, rwx access to directories) | ||
// (We use xargs because `find` does not fail if an `exec` command fails; see https://serverfault.com/a/905039) | ||
'find . -type f -print0 | xargs -n1 -0 chmod g+rw', | ||
'find . -type d -print0 | xargs -n1 -0 chmod g+rwx', | ||
]); | ||
} | ||
|
||
// TODO set umask appropriately, so that new files created are readable/writable by the group | ||
|
||
async function addExecutePermissionsToHomeDirectoryCI() { | ||
/* | ||
TODO understand better | ||
This is to make `npm run` work when run as ably-test-user; else it fails because of a `statx()` call on package.json: | ||
> 2024-04-17T13:08:09.1302251Z [pid 2051] statx(AT_FDCWD, `"/home/runner/work/ably-js/ably-js/package.json"`, AT_STATX_SYNC_AS_STAT, STATX_ALL, 0x7f4875ffcb40) = -1 EACCES (Permission denied) | ||
statx documentation says: | ||
> in the case of **statx**() with a pathname, execute (search) permission is required on all of the directories in _pathname_ that lead to the file. | ||
The fact that I’m having to do this probably means that I’m doing something inappropriate elsewhere. (And I don’t know what the other consequences of doing this might be.) | ||
*/ | ||
await runShellCommands(['chmod o+x ~']); | ||
} | ||
|
||
async function generateMitmproxySSLCertsLinux() { | ||
await runShellCommands([ | ||
'sudo -u mitmproxyuser /opt/pipx_bin/mitmdump -s test/mitmproxy_addon_generate_certs_and_exit.py', | ||
]); | ||
} | ||
|
||
async function copyMitmproxySSLCertsLocally() { | ||
// Copy mitmproxy SSL certs locally so we can use them | ||
await runShellCommands(['sudo cp ~mitmproxyuser/.mitmproxy/mitmproxy-ca-cert.pem .']); | ||
} | ||
|
||
async function createFilewallRulesLinux() { | ||
/* | ||
The rules suggested by mitmproxy etc are aimed at intercepting _all_ the outgoing traffic on a machine. I don’t want that, given that we want to be able to run this test suite on developers’ machines in a non-invasive manner. Instead we just want to target traffic generated by the process that contains the Ably SDK, which we’ll make identifable by iptables by running that process as a specific user created for that purpose (ably-test-user). | ||
Relevant parts of iptables documentation: | ||
nat: | ||
> This table is consulted when a packet that creates a new connection is encountered. It consists of three built-ins: PREROUTING (for altering packets as soon as they come in), OUTPUT (for altering locally-generated packets before routing), and POSTROUTING (for altering packets as they are about to go out). | ||
owner: | ||
> This module attempts to match various characteristics of the packet creator, for locally-generated packets. It is only valid in the OUTPUT chain, and even this some packets (such as ICMP ping responses) may have no owner, and hence never match. | ||
REDIRECT: | ||
> This target is only valid in the nat table, in the PREROUTING and OUTPUT chains, and user-defined chains which are only called from those chains. It redirects the packet to the machine itself by changing the destination IP to the primary address of the incoming interface (locally-generated packets are mapped to the 127.0.0.1 address). It takes one option: | ||
> | ||
> --to-ports port[-port] | ||
> This specifies a destination port or range of ports to use: without this, the destination port is never altered. This is only valid if the rule also specifies -p tcp or -p udp. | ||
I don’t exactly understand what the nat table means; I assume its rules apply to all _subsequent_ packets in the connection, too? | ||
So, what I expect to happen: | ||
1. iptables rule causes default-port HTTP(S) datagram from test process to get its destination IP rewritten to 127.0.0.1, and rewrites the TCP header’s destination port to 8080 | ||
2. 127.0.0.1 destination causes OS’s routing to send this datagram on the loopback interface | ||
3. nature of the loopback interface means that this datagram is then received on the loopback interface | ||
4. mitmproxy, listening on port 8080 (not sure how or why it uses a single port for both non-TLS and TLS traffic) receives these datagrams, and uses Host header or SNI to figure out where they were originally destined. | ||
TODO (how) do we achieve the below on macOS? I have a feeling that it’s currently just working by accident; e.g. it's because the TCP connection to the control server exists before we start mitmproxy and hence the connection doesn’t get passed to its NETransparentProxyProvider or something. To be on the safe side, though, I’ve added a check in the mitmproxy addon so that we only mess with stuff for ports 80 or 443 | ||
Note that in the current setup with ably-js, the test suite and the Ably SDK run in the same process. We want to make sure that we don’t intercept the test suite’s WebSocket communications with the interception proxy’s control API (which it serves at 127.0.0.1:8001), hence only targeting the default HTTP(S) ports. (TODO consider that Realtime team also run a Realtime on non-default ports when testing locally) | ||
*/ | ||
await runShellCommands([ | ||
'sudo iptables --table nat --append OUTPUT --match owner --uid-owner ably-test-user --protocol tcp --destination-port 80 --jump REDIRECT --to-ports 8080', | ||
'sudo iptables --table nat --append OUTPUT --match owner --uid-owner ably-test-user --protocol tcp --destination-port 443 --jump REDIRECT --to-ports 8080', | ||
'sudo ip6tables --table nat --append OUTPUT --match owner --uid-owner ably-test-user --protocol tcp --destination-port 80 --jump REDIRECT --to-ports 8080', | ||
'sudo ip6tables --table nat --append OUTPUT --match owner --uid-owner ably-test-user --protocol tcp --destination-port 443 --jump REDIRECT --to-ports 8080', | ||
]); | ||
} | ||
|
||
/* | ||
TODO how will this behave with: | ||
1. the WebSocket connection from test suite to control API (see above note; not a problem in this CI setup, think about it on macOS) | ||
2. the WebSocket connection from mitmproxy to control API (not an issue on Linux or macOS with our current setup since we don’t intercept any traffic from mitmproxy) | ||
3. the WebSocket connections that mitmproxy proxies to the interception proxy (which it sends to localhost:8002) (ditto 2) | ||
4. the WebSocket connections for which interception proxy is a client (not an issue for Linux or macOS with our current setup since we don’t intercept any traffic from interception proxy) | ||
*/ | ||
|
||
(async function run() { | ||
await createMitmproxyUserLinux(); | ||
await createTestsUserLinux(); | ||
await createGroupLinux(); | ||
await sortOutPermissions(); | ||
await addExecutePermissionsToHomeDirectoryCI(); | ||
await generateMitmproxySSLCertsLinux(); | ||
await copyMitmproxySSLCertsLocally(); | ||
await createFilewallRulesLinux(); | ||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters