Implement proxy v2 architecture, in Rust#1718
Conversation
|
I accidentally squashed the README commits when migrating and then dropped the CircleCI config since we need to redo it for GHA anyways. |
658fb3d to
1a025c9
Compare
|
I just pushed "WIP: Have the sdk always shell out to the proxy" which is somewhat working, I messed up in negating a boolean somewhere so file downloading is a bit messed up, but the login and other API requests are happening via shelling out to a locally built proxy. Once that's fixed, the next step will be mocking HTTP traffic in tests, maybe with some vcr-lite system. |
|
I'll pick this up from @legoktm after today, starting with the to-do list I've added in #1718 (comment). (Feel free to edit and add to it!) |
|
A bit of an update and what's left:
|
8633321 to
80e7718
Compare
|
Oh also, it might be worth looking at the SDK error handling a bit closer, there's a few raised errors that I left with "FIXME", but also I saw at least one higher-level method that was directly catching a JSON error, which shouldn't be possible since |
|
#1718 (comment) is done as suggested. Stack of work is now: |
Tracking this in #1778. |
|
#1778 is substantively done in f3a5c5a...b82720b and just needs some documenting and Git-polishing. |
eac46df to
b002a5f
Compare
|
#1778 is done and documented in f3a5c5a...b002a5f. @legoktm, let's sync up about this and the remaining checklist here tomorrow. |
b002a5f to
bc30f84
Compare
bc30f84 to
2b95298
Compare
Rewrite the existing tests to be integration tests against a compiled Rust binary. We use the httpbin library to start up a Python webserver and instruct the proxy to connect to it. This allows to test connection properties that aren't recordable in the VCR format, like timeouts or streamed responses. The tests are reorganized to be split into proxy handling and error handling.
The SDK will now unconditionally use proxy v2: in development mode, it'll shell out directly, while production mode will invoke it over qrexec. The dev `./run.sh` script will compile the proxy so it's ready for use before the client starts up. New typed dataclasses represent the two types of responses that can be returned. No user-facting changes are happening at this stage, but this will enable future client features. The union return of send_json_request() means that we need instanceof assertions to make mypy happy. One minor logic bug in API.get_submission() was fixed in the case that no request is made (an undefined exception would've been raised previously).
We can't use VCR.py's "custom_patches" parameter because our API._send_json_request() is RPC- rather than connection-oriented. But we can just instrument _send_json_request() directly, which is what we do here. We subclass vcr.cassette.Cassette to handle identical requests with different responses, which was suggestd by @vickyliin as a workaround for kevin1024/vcrpy#753. Now that the SDK‒proxy connection is itself instrumented, there's only one path to test, with no special error-handling logic required, so merge TestAPIProxy into TestAPI. I considered merging TestAPI and TestShared as well, now that (without TestAPIProxy) TestAPI is the only subclass of TestShared. But reorganizing the alphabetized helpers in TestShared versus the strictly-sequenced TestAPI methods can wait. The tests are also now more patient with slow deletion operations. I'd want to DRY up this logic if this pattern shows up in more places, but it would require adding another level of indirection. A @Retry decorator isn't appropriate at the level of the test method, and a context manager can't loop over its closure. And re-apply the hack from 880635d by renaming test_logout to start with a "z" so it runs last.
Help the developer through regenerating SDK cassettes against a development server, as well as in CI.
* Switch to `Arch: any` because the package contains compiled code and this enables debhelper's automatic shlibs dependency system. * Rename the binary to `securedrop-proxy` because that's the Rust name.
Audits were done by Kunal over the course of a few months.
Same as the SDK, use our custom VCRAPI wrapper instead of pytest-vcr. Because each of the functional tests log in and out, document the server hack needed to run them one after another in the README.
Now replaced by the Rust version.
We are setting the proxy's origin in QubesDB (via dom0 salt), and as discussed/agreed upon in <freedomofpress/securedrop-workstation#1013>, we're reading it directly in the proxy. We link directly to libqubesdb, generating the Rust wrapper with bindgen so it stays in sync (at build time at least). As a result, some unsafe is needed, but it is clearly annotated with relevant SAFETY notes. If we're not building with QubesDB (for development purposes), we'll continue to read from the environment and skip the bindgen and linking steps entirely. Fixes <freedomofpress/securedrop-workstation#853>. Co-authored-by: Kunal Mehta <[email protected]>
libclang is needed by bindgen. The Qubes signing key was fetched from <https://github.com/QubesOS/qubes-builder-debian/blob/64f530a712539829f8f2c1fbb0907d99b562ccdd/keys/qubes-debian-r4.2.asc>.
This does nothing because Rust code is linted through the root Makefile's `rust-lint` target, and Python code linted from the root's ruff targets as well.
Historically production systems used ~/QubesIncoming/sd-proxy as a temporary directory because files were sent over qvm-move. This switches to ~/Downloads as a stop-gap. A better interface would be for the caller to provision a temporary directory, and pass it as the path.
Avoid IndexError in the case that `response.stdout` is empty.
Co-authored-by: Kunal Mehta <[email protected]>
The relevant httpbin issue has been resolved and is compatible with newer werkzeug.
legoktm
left a comment
There was a problem hiding this comment.
I can't approve this since I opened the PR, but this is ready to go and has sign-offs from @cfm and @micahflee on their respective parts.
addressed as per ~/Downloads discussion.
zenmonkeykstop
left a comment
There was a problem hiding this comment.
Stamping given @cfm and @micahflee's reviews, CI passing, and (admittedly) cursory review.
Now that proxy v2 has landed (see <freedomofpress/securedrop-client#1718>), we can remove the qubes.Filecopy RPC rule from sd-proxy to sd-client (it now goes over the securedrop.Proxy RPC) and the sd-proxy.yaml configuration (now read from QubesDB). Fixes #1026.
Now that proxy v2 has landed (see <freedomofpress/securedrop-client#1718>), we can remove the qubes.Filecopy RPC rule from sd-proxy to sd-client (it now goes over the securedrop.Proxy RPC) and the sd-proxy.yaml configuration (now read from QubesDB). Fixes #1026.
This was migrated from freedomofpress/securedrop-proxy#127
Status
Ready for review
Description
Implement the proxy v2 architecture; see individual commits messages for more detail.
vcrpyour proxy v2 request/response semantics #1778requestsandurllib3optional dependencies #1761Test Plan
cd client && ./run.shand test syncing, sending replies and downloading attachments works fineChecklist
If these changes modify code paths involving cryptography, the opening of files in VMs or network (via the RPC service) traffic, Qubes testing in the staging environment is required. For fine tuning of the graphical user interface, testing in any environment in Qubes is required. Please check as applicable:
If these changes add or remove files other than client code, the AppArmor profile may need to be updated. Please check as applicable:
If these changes modify the database schema, you should include a database migration. Please check as applicable:
mainand confirmed that the migration is self-contained and applies cleanlymainand would like the reviewer to do so