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

Eco 4788 investigate proxy browser failures #1774

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6bb2f82
Remove Rest#setLog method
lawrence-forooghian May 16, 2024
eab98b9
Remove getRealtimeHost from CompleteDefaults
lawrence-forooghian May 16, 2024
f5361d2
Remove global effects of setting logHandler and logLevel
lawrence-forooghian May 16, 2024
725cb50
Document private API usage in tests
lawrence-forooghian May 2, 2024
a58a6c5
Better handling of failure to start Mocha server
lawrence-forooghian Apr 18, 2024
08ec417
Add Python stuff to gitignore
lawrence-forooghian Apr 17, 2024
ff30f2c
Add abandoned initial attempt at interception proxy
lawrence-forooghian Apr 17, 2024
1ee20a2
Revert "Add abandoned initial attempt at interception proxy"
lawrence-forooghian Apr 17, 2024
4dadcee
ECO-14: Document internal API usage in tests, add interception proxy …
lawrence-forooghian Apr 17, 2024
dd94f9d
isolate failing test
lawrence-forooghian May 15, 2024
243730d
exploring presence test
lawrence-forooghian May 16, 2024
6b0c500
add logs
lawrence-forooghian May 16, 2024
632ece0
turn off log stripping for now
lawrence-forooghian May 17, 2024
43dba0c
further
lawrence-forooghian May 17, 2024
550c7c5
furthe rlogging
lawrence-forooghian May 17, 2024
43913ed
logs
lawrence-forooghian May 17, 2024
952915d
improve logging in proxy
lawrence-forooghian May 17, 2024
5a3d65a
logs
lawrence-forooghian May 17, 2024
ee1fd26
add timestamps to logs
lawrence-forooghian May 17, 2024
5d52c19
add with packet capture
lawrence-forooghian May 17, 2024
4f63a15
restore modular tests
lawrence-forooghian May 20, 2024
50f0899
Make sure we tear down connections in modular.test.js
lawrence-forooghian May 20, 2024
80b6506
Make FetchRequest not try and parse a 204 response body
lawrence-forooghian May 21, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion .github/workflows/test-browser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,69 @@ jobs:
with:
node-version: 20.x
- run: npm ci

# Set up Python (for pipx)
- uses: actions/setup-python@v5
with:
python-version: '3.12'

# Install pipx (for mitmproxy)
# See https://pipx.pypa.io/stable/installation/
- name: Install pipx
run: |
python3 -m pip install --user pipx
sudo pipx --global ensurepath

# https://docs.mitmproxy.org/stable/overview-installation/#installation-from-the-python-package-index-pypi
- name: Install mitmproxy
run: |
pipx install mitmproxy
# We use this library in our addon
pipx inject mitmproxy websockets

- name: Generate mitmproxy SSL certs
run: mitmdump -s test/mitmproxy_addon_generate_certs_and_exit.py

- name: Start interception proxy server
run: ./start-interception-proxy

- name: Install Playwright browsers and dependencies
run: npx playwright install --with-deps
- env:

# For certutil
- name: Install NSS tools
run: sudo apt install libnss3-tools

# This is for Chromium (see https://chromium.googlesource.com/chromium/src/+/master/docs/linux/cert_management.md)
# Note this is the same command that we use for adding it to the Firefox profile (see playwrightHelpers.js)
- name: Install mitmproxy root CA in NSS shared DB
run: |
mkdir -p ~/.pki/nssdb
certutil -A -d sql:$HOME/.pki/nssdb -t "C" -n "Mitmproxy Root Cert" -i ~/.mitmproxy/mitmproxy-ca-cert.pem
certutil -L -d sql:$HOME/.pki/nssdb

# This is for WebKit (I think because it uses OpenSSL)
- name: Install mitmproxy root CA in /usr/local/share/ca-certificates
run: |
sudo cp ~/.mitmproxy/mitmproxy-ca-cert.cer /usr/local/share/ca-certificates/mitmproxy-ca-cert.crt
sudo update-ca-certificates

- name: Run the tests
env:
PLAYWRIGHT_BROWSER: ${{ matrix.browser }}
run: npm run test:playwright

- name: Save interception proxy server logs
if: always()
run: sudo journalctl -u ably-sdk-test-proxy.service > interception-proxy-logs.txt

- name: Upload interception proxy server logs
if: always()
uses: actions/upload-artifact@v4
with:
name: interception-proxy-logs-${{ matrix.browser }}
path: interception-proxy-logs.txt

- name: Upload test results
if: always()
uses: ably/test-observability-action@v1
Expand Down
113 changes: 112 additions & 1 deletion .github/workflows/test-node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,120 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run test:node

# Set up Python (for pipx)
- uses: actions/setup-python@v5
with:
python-version: '3.12'

# Install pipx (for mitmproxy)
# See https://pipx.pypa.io/stable/installation/
- name: Install pipx
run: |
python3 -m pip install --user pipx
sudo pipx --global ensurepath

# https://docs.mitmproxy.org/stable/overview-installation/#installation-from-the-python-package-index-pypi
- name: Install mitmproxy
run: |
pipx install mitmproxy
# We use this library in our addon
pipx inject mitmproxy websockets

- name: Create a user to run the tests
run: sudo useradd --create-home ably-test-user

- name: Create a group for sharing the working directory
run: |
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 ably-test-user
# 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 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.)
chmod o+x ~

# TODO set umask appropriately, so that new files created are readable/writable by the group

- name: Generate mitmproxy SSL certs
run: mitmdump -s test/mitmproxy_addon_generate_certs_and_exit.py

- name: Set up iptables rules
run: |
# 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)
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)

- name: Start interception proxy server
run: ./start-interception-proxy

- name: Run the tests
run: sudo -u ably-test-user NODE_EXTRA_CA_CERTS=~/.mitmproxy/mitmproxy-ca-cert.pem npm run test:node
env:
CI: true

- name: Save interception proxy server logs
if: always()
run: sudo journalctl -u ably-sdk-test-proxy.service > interception-proxy-logs.txt

- name: Upload interception proxy server logs
if: always()
uses: actions/upload-artifact@v4
with:
name: interception-proxy-logs-${{ matrix.node-version }}
path: interception-proxy-logs.txt

- name: Upload test results
if: always()
uses: ably/test-observability-action@v1
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ react/
typedoc/generated/
junit/
test/support/mocha_junit_reporter/build/
tmp/

# Python stuff (for interception proxy)
__pycache__
Binary file added capture-5.pcapng
Binary file not shown.
Loading
Loading