How to set up the trezor bridge in 4.1

Verified Trezor works. Updated firmware. Fun fact, Asus EeePC netbooks run Linux just fine. Sadly, it had to be “recycled” in the name of security.

Wireshark/sys-usb testing updates to come.

Booted Trezor Suite 22.4.3 AppImage verified from Trezor.io

|1|0.000000000|127.0.0.1|127.0.0.1|TCP|68|40748 → 21325 [SYN] Seq=0 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|---|---|---|---|---|---|---|
|2|0.000011238|127.0.0.1|127.0.0.1|TCP|68|21325 → 40748 [SYN, ACK] Seq=0 Ack=1 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|3|0.000018374|127.0.0.1|127.0.0.1|TCP|56|40748 → 21325 [ACK] Seq=1 Ack=1 Win=65504 Len=0|
|4|0.000063506|127.0.0.1|127.0.0.1|HTTP|284|POST / HTTP/1.1 |
|5|0.000066298|127.0.0.1|127.0.0.1|TCP|56|21325 → 40748 [ACK] Seq=1 Ack=229 Win=65280 Len=0|
|6|0.210132458|127.0.0.1|127.0.0.1|HTTP|270|HTTP/1.1 200 OK  (text/plain)|
|7|0.210151375|127.0.0.1|127.0.0.1|TCP|56|40748 → 21325 [ACK] Seq=229 Ack=215 Win=65312 Len=0|
|8|0.210378831|127.0.0.1|127.0.0.1|TCP|56|21325 → 40748 [FIN, ACK] Seq=215 Ack=229 Win=65504 Len=0|
|9|0.230287457|127.0.0.1|127.0.0.1|TCP|56|40748 → 21325 [FIN, ACK] Seq=229 Ack=216 Win=65504 Len=0|
|10|0.230300375|127.0.0.1|127.0.0.1|TCP|56|21325 → 40748 [ACK] Seq=216 Ack=230 Win=65504 Len=0|

Connected the Trezor and unlocked it:

|11|2.259594409|127.0.0.1|127.0.0.1|TCP|68|40750 → 21325 [SYN] Seq=0 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|12|2.259609059|127.0.0.1|127.0.0.1|TCP|68|21325 → 40750 [SYN, ACK] Seq=0 Ack=1 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|13|2.259617931|127.0.0.1|127.0.0.1|TCP|56|40750 → 21325 [ACK] Seq=1 Ack=1 Win=65504 Len=0|
|14|2.259656294|127.0.0.1|127.0.0.1|HTTP|306|POST / HTTP/1.1 |
|15|2.259660818|127.0.0.1|127.0.0.1|TCP|56|21325 → 40750 [ACK] Seq=1 Ack=251 Win=65248 Len=0|
|16|2.555827517|127.0.0.1|127.0.0.1|HTTP|266|HTTP/1.1 200 OK  (text/plain)|
|17|2.555848635|127.0.0.1|127.0.0.1|TCP|56|40750 → 21325 [ACK] Seq=251 Ack=211 Win=65312 Len=0|
|18|2.577063817|127.0.0.1|127.0.0.1|TCP|56|21325 → 40750 [FIN, ACK] Seq=211 Ack=251 Win=65504 Len=0|
|19|2.617283440|127.0.0.1|127.0.0.1|TCP|56|40750 → 21325 [ACK] Seq=251 Ack=212 Win=65504 Len=0|
|20|2.622583079|127.0.0.1|127.0.0.1|TCP|56|40750 → 21325 [FIN, ACK] Seq=251 Ack=212 Win=65504 Len=0|
|21|2.622593306|127.0.0.1|127.0.0.1|TCP|56|21325 → 40750 [ACK] Seq=212 Ack=252 Win=65504 Len=0|
|22|3.655034959|127.0.0.1|127.0.0.1|TCP|68|40752 → 21325 [SYN] Seq=0 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|23|3.655046697|127.0.0.1|127.0.0.1|TCP|68|21325 → 40752 [SYN, ACK] Seq=0 Ack=1 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|24|3.655053340|127.0.0.1|127.0.0.1|TCP|56|40752 → 21325 [ACK] Seq=1 Ack=1 Win=65504 Len=0|
|25|3.655078071|127.0.0.1|127.0.0.1|HTTP|289|POST /enumerate HTTP/1.1 |
|26|3.655081143|127.0.0.1|127.0.0.1|TCP|56|21325 → 40752 [ACK] Seq=1 Ack=234 Win=65280 Len=0|
|27|3.972579720|127.0.0.1|127.0.0.1|HTTP|339|HTTP/1.1 200 OK  (text/plain)|
|28|3.972595492|127.0.0.1|127.0.0.1|TCP|56|40752 → 21325 [ACK] Seq=234 Ack=284 Win=65248 Len=0|
|29|3.972649401|127.0.0.1|127.0.0.1|TCP|56|21325 → 40752 [FIN, ACK] Seq=284 Ack=234 Win=65504 Len=0|
|30|3.988845367|127.0.0.1|127.0.0.1|TCP|56|40752 → 21325 [FIN, ACK] Seq=234 Ack=285 Win=65504 Len=0|
|31|3.988856962|127.0.0.1|127.0.0.1|TCP|56|21325 → 40752 [ACK] Seq=285 Ack=235 Win=65504 Len=0|
|32|5.850036321|127.0.0.1|127.0.0.1|TCP|68|40754 → 21325 [SYN] Seq=0 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|33|5.850036365|127.0.0.1|127.0.0.1|TCP|68|40756 → 21325 [SYN] Seq=0 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|34|5.850049392|127.0.0.1|127.0.0.1|TCP|68|21325 → 40756 [SYN, ACK] Seq=0 Ack=1 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|35|5.850052362|127.0.0.1|127.0.0.1|TCP|68|21325 → 40754 [SYN, ACK] Seq=0 Ack=1 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|36|5.850057398|127.0.0.1|127.0.0.1|TCP|56|40756 → 21325 [ACK] Seq=1 Ack=1 Win=65504 Len=0|
|37|5.850059584|127.0.0.1|127.0.0.1|TCP|56|40754 → 21325 [ACK] Seq=1 Ack=1 Win=65504 Len=0|
|38|5.850087454|127.0.0.1|127.0.0.1|HTTP|294|POST /acquire/2/null HTTP/1.1 |
|39|5.850090680|127.0.0.1|127.0.0.1|TCP|56|21325 → 40756 [ACK] Seq=1 Ack=239 Win=65280 Len=0|
|40|5.850117106|127.0.0.1|127.0.0.1|HTTP|420|POST /listen HTTP/1.1  (text/plain)|
|41|5.850120295|127.0.0.1|127.0.0.1|TCP|56|21325 → 40754 [ACK] Seq=1 Ack=365 Win=65152 Len=0|
|42|6.682372299|127.0.0.1|127.0.0.1|HTTP|286|HTTP/1.1 400 Bad Request  (text/plain)|
|43|6.682391287|127.0.0.1|127.0.0.1|TCP|56|40756 → 21325 [ACK] Seq=239 Ack=231 Win=65280 Len=0|
|44|6.683426146|127.0.0.1|127.0.0.1|TCP|56|21325 → 40756 [FIN, ACK] Seq=231 Ack=239 Win=65504 Len=0|
|45|6.692003596|127.0.0.1|127.0.0.1|TCP|56|40756 → 21325 [FIN, ACK] Seq=239 Ack=232 Win=65504 Len=0|
|46|6.692015324|127.0.0.1|127.0.0.1|TCP|56|21325 → 40756 [ACK] Seq=232 Ack=240 Win=65504 Len=0|

Disconnected Trezor and reconnected it

|47|79.007137681|127.0.0.1|127.0.0.1|HTTP|247|HTTP/1.1 200 OK  (text/plain)|
|48|79.007152142|127.0.0.1|127.0.0.1|TCP|56|40754 → 21325 [ACK] Seq=365 Ack=192 Win=65344 Len=0|
|49|79.020737842|127.0.0.1|127.0.0.1|TCP|56|21325 → 40754 [FIN, ACK] Seq=192 Ack=365 Win=65504 Len=0|
|50|79.026075995|127.0.0.1|127.0.0.1|TCP|56|40754 → 21325 [FIN, ACK] Seq=365 Ack=193 Win=65504 Len=0|
|51|79.026086437|127.0.0.1|127.0.0.1|TCP|56|21325 → 40754 [ACK] Seq=193 Ack=366 Win=65504 Len=0|
|52|80.581035825|127.0.0.1|127.0.0.1|TCP|68|40760 → 21325 [SYN] Seq=0 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|53|80.581049715|127.0.0.1|127.0.0.1|TCP|68|21325 → 40760 [SYN, ACK] Seq=0 Ack=1 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|54|80.581059134|127.0.0.1|127.0.0.1|TCP|56|40760 → 21325 [ACK] Seq=1 Ack=1 Win=65504 Len=0|
|55|80.581124286|127.0.0.1|127.0.0.1|HTTP|328|POST /listen HTTP/1.1  (text/plain)|
|56|80.581129446|127.0.0.1|127.0.0.1|TCP|56|21325 → 40760 [ACK] Seq=1 Ack=273 Win=65248 Len=0|
|57|117.113919129|127.0.0.1|127.0.0.1|HTTP|339|HTTP/1.1 200 OK  (text/plain)|
|58|117.113934277|127.0.0.1|127.0.0.1|TCP|56|40760 → 21325 [ACK] Seq=273 Ack=284 Win=65248 Len=0|
|59|117.116183419|127.0.0.1|127.0.0.1|TCP|56|21325 → 40760 [FIN, ACK] Seq=284 Ack=273 Win=65504 Len=0|
|60|117.121133034|127.0.0.1|127.0.0.1|TCP|56|40760 → 21325 [FIN, ACK] Seq=273 Ack=285 Win=65504 Len=0|
|61|117.121142801|127.0.0.1|127.0.0.1|TCP|56|21325 → 40760 [ACK] Seq=285 Ack=274 Win=65504 Len=0|
|62|118.114651140|127.0.0.1|127.0.0.1|TCP|68|40762 → 21325 [SYN] Seq=0 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|63|118.114664009|127.0.0.1|127.0.0.1|TCP|68|21325 → 40762 [SYN, ACK] Seq=0 Ack=1 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|64|118.114672002|127.0.0.1|127.0.0.1|TCP|56|40762 → 21325 [ACK] Seq=1 Ack=1 Win=65504 Len=0|
|65|118.114701804|127.0.0.1|127.0.0.1|HTTP|420|POST /listen HTTP/1.1  (text/plain)|
|66|118.114705256|127.0.0.1|127.0.0.1|TCP|56|21325 → 40762 [ACK] Seq=1 Ack=365 Win=65152 Len=0|
|67|118.114754232|127.0.0.1|127.0.0.1|TCP|68|40764 → 21325 [SYN] Seq=0 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|68|118.114757673|127.0.0.1|127.0.0.1|TCP|68|21325 → 40764 [SYN, ACK] Seq=0 Ack=1 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|69|118.114760445|127.0.0.1|127.0.0.1|TCP|56|40764 → 21325 [ACK] Seq=1 Ack=1 Win=65504 Len=0|
|70|118.114778043|127.0.0.1|127.0.0.1|HTTP|294|POST /acquire/3/null HTTP/1.1 |
|71|118.114785801|127.0.0.1|127.0.0.1|TCP|56|21325 → 40764 [ACK] Seq=1 Ack=239 Win=65280 Len=0|
|72|118.734310725|127.0.0.1|127.0.0.1|HTTP|286|HTTP/1.1 400 Bad Request  (text/plain)|
|73|118.734326597|127.0.0.1|127.0.0.1|TCP|56|40764 → 21325 [ACK] Seq=239 Ack=231 Win=65280 Len=0|
|74|118.736301362|127.0.0.1|127.0.0.1|TCP|56|21325 → 40764 [FIN, ACK] Seq=231 Ack=239 Win=65504 Len=0|
|75|118.777294049|127.0.0.1|127.0.0.1|TCP|56|40764 → 21325 [ACK] Seq=239 Ack=232 Win=65504 Len=0|
|76|118.813632983|127.0.0.1|127.0.0.1|TCP|56|40764 → 21325 [FIN, ACK] Seq=239 Ack=232 Win=65504 Len=0|
|77|118.813648222|127.0.0.1|127.0.0.1|TCP|56|21325 → 40764 [ACK] Seq=232 Ack=240 Win=65504 Len=0|
|78|125.537519783|127.0.0.1|127.0.0.1|HTTP|247|HTTP/1.1 200 OK  (text/plain)|
|79|125.537558229|127.0.0.1|127.0.0.1|TCP|56|40762 → 21325 [ACK] Seq=365 Ack=192 Win=65344 Len=0|
|80|125.539875584|127.0.0.1|127.0.0.1|TCP|56|21325 → 40762 [FIN, ACK] Seq=192 Ack=365 Win=65504 Len=0|
|81|125.547129558|127.0.0.1|127.0.0.1|TCP|56|40762 → 21325 [FIN, ACK] Seq=365 Ack=193 Win=65504 Len=0|
|82|125.547141611|127.0.0.1|127.0.0.1|TCP|56|21325 → 40762 [ACK] Seq=193 Ack=366 Win=65504 Len=0|
|83|126.334841846|127.0.0.1|127.0.0.1|TCP|68|40766 → 21325 [SYN] Seq=0 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|84|126.334852806|127.0.0.1|127.0.0.1|TCP|68|21325 → 40766 [SYN, ACK] Seq=0 Ack=1 Win=65495 Len=0 MSS=65495 SACK_PERM=1 WS=32|
|85|126.334859498|127.0.0.1|127.0.0.1|TCP|56|40766 → 21325 [ACK] Seq=1 Ack=1 Win=65504 Len=0|
|86|126.334884034|127.0.0.1|127.0.0.1|HTTP|328|POST /listen HTTP/1.1  (text/plain)|
|87|126.334886541|127.0.0.1|127.0.0.1|TCP|56|21325 → 40766 [ACK] Seq=1 Ack=273 Win=65248 Len=0|
|88|130.736536048|127.0.0.1|127.0.0.1|TCP|56|40766 → 21325 [FIN, ACK] Seq=273 Ack=1 Win=65504 Len=0|
|89|130.777300926|127.0.0.1|127.0.0.1|TCP|56|21325 → 40766 [ACK] Seq=1 Ack=274 Win=65504 Len=0|
|90|131.203526784|127.0.0.1|127.0.0.1|HTTP|249|HTTP/1.1 200 OK  (text/plain)|
|91|131.203549836|127.0.0.1|127.0.0.1|TCP|56|40766 → 21325 [ACK] Seq=274 Ack=194 Win=65312 Len=0|
|92|131.203688177|127.0.0.1|127.0.0.1|TCP|56|21325 → 40766 [FIN, ACK] Seq=194 Ack=274 Win=65504 Len=0|
|93|131.203742390|127.0.0.1|127.0.0.1|TCP|56|40766 → 21325 [ACK] Seq=274 Ack=195 Win=65312 Len=0|

Then disconnected it. Same result if I attach to AppVM.

P.S. Is there a roll-up HTML code for stuff like this?

Yeah that traffic looks like it’s working. So you can see a request originates from some random high port (40748) to 21325 with the [SYN] tag, which is like “hey, anyone there?” and then trezord which is listening on 21325 writes back an ACKnowledgement to 40748 [SYN, ACK] that it heard. And then after establishing the channel for TCP, they’re able to trade some HTTP packets. So the suite can talk to trezord!

So if we understood each other correctly that you took this wireshark data on sys-usb while running the trezor suite in an app vm, then the qubes rpc calls are working, and that’s not the source of the problem.

If you’re still having a bug where the device is in the incorrect state on qubes, and that doesn’t happen on another OS, then I’m afraid I have no idea. I guess you can double check that the content of your udev rules file matches what I wrote in the first post. You can maybe also try deleting the 51-trezor.rules file, since I know it works in my setup with only 50-trezor.rules present. Another last-ditch effort, try backing up the trezor and use a different PC to do a factory reset? If none of that helps, then maybe writing to trezor is the only hope.

Already did. I even dropped a link to this thread. They didn’t offer anything beyond the excellent help you all have. Factory reset did not help. I’ll try yet another fresh install of Qubes. Maybe there’s some bits floating around in bad places from all my trial and error. To be continued…

It occurred to me you could also try debugging trezord. You can for example

#sys-usb
sudo systemctl stop trezord
trezord -v -l /home/user/mylogfile

Then while it’s running, try to interact with it to reproduce the bug.

The -v flag makes the log very verbose, so you might try without it first and see if you get anything useful explaining the problem. I’m not that optimistic, but it might help anyway to try compiling the latest version of trezord for some reason.

There’s something in the source code about resetting the connection to put the device in the right state, and having some timing issues on some browsers that they fixed by giving it a few tries before giving up. The -r flag seems related to this, and it says it’s enabled by default, but give it a try with -r specifically included anyway. gl

Not a bad idea. Just came across “Networking broken in 4.1 default templates” and related threads. Gonna give those a whirl as well after the re-install.

Fixed. Installed bridge + udev rules on default TemplateVM ‘fedora-34’. For some reason, I had it stuck in my head that fedora-34-dvm was a TemplateVM.

This command returns: No such file or directory
And these return with PERMISSION DENIED on sys-usb

ln -s /rw/config/trezor/trezord /usr/bin/trezord
ln -s /rw/config/trezor/50-trezor.rules /lib/udev/rules.d/50-trezor.rules
ln -s /rw/config/trezor/trezord.service /usr/lib/systemd/system/trezord.service
ln -s /rw/config/trezor/qubes.Trezor /etc/qubes-rpc/qubes.Trezor
# add user that systemctl service is configured to use
useradd trezord
systemctl start trezord

The issue im having is that i get to he web wallet and it even shows my device name but nothing loads, cannot open any wallets its just stuck on loading forever. Any ideas??

Also, question. I do it in an APPVM is that an issue? And then I pass Trezor from sys-usb to the APPVM

This really is getting on my nerves it has been 5 days and i have not been able to use my trezor with qubes.

This command returns: No such file or directory
And these return with PERMISSION DENIED on sys-usb

These should be pasted into /rw/config/rc.local, which means they will run as root when the VM starts up (so shouldn’t have permission denied), and you should put the files into /rw/config/trezor/ that it makes links to.

Also, question. I do it in an APPVM is that an issue? And then I pass Trezor from sys-usb to the APPVM

Yes, this is an issue. The whole point of this setup is that the usb device passthrough is complicated and doesn’t work reliably for many kinds of devices. The trezor is one like device that cannot be passed through. Because the client (trezor suite, trezorctl, electrum, etc.) communicates with the bridge (trezord) via TCP, we don’t need to use a usb passthrough. Instead we run the bridge in the usb vm, run the client in an app vm, and use TCP sockets to send the client<->bridge communication through qubes-rpc.

I would update the instructions in the original post to say “First you need the bridge software in sys-usb.” to make that part more clear. I’m new to the forums, and it looks like I can’t edit the original post.

1 Like

I get it but how do I install bridge in sys-usb then? Could you try explaining it to my further or in private? If it works well I’m willing to pay u for your time.

The steps are in the original post. To summarize, you need to

  • get the RPM
  • install it
  • copy its files (50-trezor.rules, trezord, trezord.service) into /rw/config/trezor
  • create the qubes.Trezor rpc script
  • add the lines to /rw/config/rc.local to make the symbolic links and start the trezord service
    With that done, trezord should be running in sys-usb whenever you start it up.

Then in dom0, you need to make the rpc policy, so that requests from the client will be allowed.

And in the client you need to add the line to /rw/config/rc.local that listens on port 21325 and forwards TCP packets in the rpc request.

Is it clear?

I didn’t do exactly what you did.

I setup a minimum Qube with Trezor Suite, and everything it requires to connect. I can manually attach the device to Trezor Suite, and see accounts. However, I cannot upgrade firmware when connected through bootloader mode. Have any idea how I can get firmware updates to work?

Sorry to write a bit late. Hmm, I never got it to work in the first place by just attaching the device. So you have trezord running in the appVM where you run the trezor suite, and the trezor is attached through sys-usb? I don’t have a great understanding of what Qubes does to pass usb devices through, but it does seem to mangle some things. The advantage with the setup as I described in this post is that trezord runs in the VM that has direct access to the hardware, and then it talks to the trezor suite via TCP packets that Qubes can pass along without any problems, so you could try it that way. I have done a firmware update on mine and it’s worked.

If you don’t want to though, you could try running the trezor suite app VM in HVM and giving it direct access to the usb hardware (so you’d have to shutdown sys-usb and add the usb controller in the hardware settings for the app VM). On my computer, for some reason trezor suite was too resource hungry or something, and I just couldn’t get it to work in HVM, hence my solution, but maybe it’d work for you.

OK dumb but related question. I feel like I did verify the bridge in the past but now the same file, I cannot get to pgp verify. Either

wget rusnak.io/public/pgp.txt
http://rusnak.io/public/pgp.txt
Resolving rusnak.io (rusnak.io)… failed: Temporary failure in name resolution.
wget: unable to resolve host address ‘rusnak.io

or if I manually get the txt file and try and import I get

sudo rpm --import pgp.txt
error: pgp.txt: key 1 import failed.

If anyone is still having issues with this, I’ve published a definitive fix here. Let me know if you have any issues and I’ll be happy to help.

Hello! Did you ever get this problem solved? I’m getting this same error every time I try to verify:

sudo rpm --import pgp.txt
error: pgp.txt: key 1 import failed.

Please let me know if you found a fix!

Hello! Thanks for your awesome guide on using the Trezor Bridge.
Quick one for you - are there any particular settings you recommend for the VM to use to verify the pgp key? (first step)

I keep getting this error - I’m using a VM with a fedora-37 template and sys-whonix networking.

sudo rpm --import pgp.txt
error: pgp.txt: key 1 import failed.

Thanks for all your help in this thread - much appreciated.

I’ve tried it in qube based on fedora-39-xfce and it worked for me:

curl -O https://rusnak.io/public/pgp.txt
sudo rpm --import pgp.txt