How to set up the trezor bridge in 4.1

Trezor’s instructions didn’t work for me on 4.1. The main problem was that the qubes rpc script needs to be executable. The documentation here Qubes OS - Trezor Wiki is also using the pre-4.1 policy format, and I ran into some weird stuff getting the bridge software itself, so I made this guide, which I hope makes it to the trezor wiki somehow.

First you need the bridge software.

if you want to use their rpm

You can go to Trezor Suite for the prompt to download the latest bridge, or you can wget https://wallet.trezor.io/data/bridge/2.0.27/trezor-bridge-2.0.27-1.x86_64.rpm This is not the latest version of trezord, it is a few years old and signed with Pavol’s key that I managed to track down (shame on you satoshilabs!), but it looks like not much has changed in trezord since, and the old bridge does work with the current suite. Get Pavol’s key wget rusnak.io/public/pgp.txt and verify the rpm

sudo rpm --import pgp.txt
rpm --checksig trezor-bridge-2.0.27-1.x86_64.rpm
> trezor-bridge-2.0.27-1.x86_64.rpm: digests signatures OK

I didn’t want to install the rpm in a template, rather just in sys-usb. I made this persistent by replicating what the installation does in rc.local

# in a trusted vm you use to get and verify the rpm
sudo dnf install trezor-bridge-2.0.27-1.x86_64.rpm
rpm -qlp trezor-bridge-2.0.27-1.x86_64.rpm
> /lib/udev/rules.d/50-trezor.rules
> /usr/bin/trezord
> /usr/lib/systemd/system/trezord.service
qvm-copy /lib/udev/rules.d/50-trezor.rules /usr/bin/trezord /usr/lib/systemd/system/trezord.service #to sys-usb
# now in sys-usb
sudo mkdir /rw/config/trezor
sudo mv QubesIncoming/trusted/* /rw/config/trezor

Then add the following to /rw/config/rc.local to run on startup

# /rw/config/rc.local 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

and finally create the executable for the qubes rpc calls by writing the following into /rw/config/trezor/qubes.Trezor

#!/bin/sh
socat - TCP:localhost:21325

and make it executable sudo chmod +x /rw/config/trezor/qubes.Trezor

if you want to build a recent version of trezord

either to have the latest version, to have built the binary from source, or both, then do the following in a fresh vm with network

# install go from the link here https://go.dev/doc/install currently:
wget https://go.dev/dl/go1.18.1.linux-amd64.tar.gz
tar -xzf go1.18.1.linux-amd64.tar.gz
mv go ~/.local
echo 'export PATH="$PATH:/home/user/.local/go"' >> ~/.bashrc
source ~/.bashrc
mkdir buildtrezord
cd buildtrezord
# follow instructions here https://github.com/trezor/trezord-go
GO111MODULE=auto go get github.com/trezor/trezord-go
GO111MODULE=auto go build github.com/trezor/trezord-go
qvm-copy trezord-go

Easy, copy that binary to sys-usb. You still need the udev rules and the systemd service, which you can get from the rpm and verify that they look like this

# 50-trezor.rules
# Trezor: The Original Hardware Wallet
# https://trezor.io/
# Put this file into /usr/lib/udev/rules.d

# note - hidraw* lines are not necessary for trezord-go, as we don't use hidraw
# however, it is still necessary for Chrome support of u2f

# Trezor
SUBSYSTEM=="usb", ATTR{idVendor}=="534c", ATTR{idProduct}=="0001", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="trezor%n"
KERNEL=="hidraw*", ATTRS{idVendor}=="534c", ATTRS{idProduct}=="0001",  MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl"

# Trezor v2
SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="53c0", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="trezor%n"
SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="53c1", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="trezor%n"
KERNEL=="hidraw*", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="53c1",  MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl"
# trezord.service
[Unit]
Description=Trezor Bridge
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/trezord
User=trezord

[Install]
WantedBy=multi-user.target

Then just like in the instructions based on the rpm, you need to add the lines to rw/config/rc.local, put 50-trezor.rules trezord.service and the binary you compiled (trezord-go which you should rename to trezord) in /rw/config/trezor, and create qubes.Trezor.

Make the qubes rpc policy

in dom0, add the following line to /etc/qubes/policy.d/30-user.policy (create the file if it doesn’t exist yet)

qubes.Trezor * CLIENTVM sys-usb allow

where CLIENTVM is the vm that you want to have access to the bridge (like to use the trezor suite). You can check the policy documentation if you want to allow more vms or have other rules.

Make a socket in the client vm

This part is just like in trezor’s documentation. Add the following line to /rw/config/rc.local in the client vm to create a socket that listens on the trezord port and forwards standard input/output to the qubes rpc call

# /rw/config/rc.local
socat TCP-LISTEN:21325,fork EXEC:"qrexec-client-vm sys-usb qubes.Trezor" &

And that’s it! At least this works on my machine. When either the trezor suite or the cli tool trezorctl makes a request to localhost:21325, it goes through the qrexec to sys-usb, where qubes.Trezor forwards it again with socat.

You can also do this with a disposable sys-usb in case you connect other devices at some point. In that case, you’ll want to install the bridge software in the disposable template, and then point the client vm’s rpc calls (and the rpc policy) to the disposable vm, which I found intuitive enough but feel free to ask if it’s not.

3 Likes

Trying this right meow. Looks like I wasn’t too far off. Will report back soon.

:EDIT:

MIssing udev rules :confused:

user@sys-usb ~$ ls -al /usr/bin/trezord 
lrwxrwxrwx 1 root root 25 Apr 26 21:02 /usr/bin/trezord -> /rw/config/trezor/trezord
user@sys-usb ~$ ls -al /lib/udev/rules.d/50-trezor.rules 
lrwxrwxrwx 1 root root 33 Apr 26 21:02 /lib/udev/rules.d/50-trezor.rules -> /rw/config/trezor/50-trezor.rules
user@sys-usb ~$ ls -al /usr/lib/systemd/system/trezord.service 
lrwxrwxrwx 1 root root 33 Apr 26 21:02 /usr/lib/systemd/system/trezord.service -> /rw/config/trezor/trezord.service
user@sys-usb ~$ ls -al /etc/qubes-rpc/qubes.Trezor 
lrwxrwxrwx 1 root root 30 Apr 26 21:02 /etc/qubes-rpc/qubes.Trezor -> /rw/config/trezor/qubes.Trezor
user@sys-usb ~$ id -u trezord
1001
user@sys-usb ~$ systemctl status trezord
● trezord.service - Trezor Bridge
     Loaded: loaded (/usr/lib/systemd/system/trezord.service; disabled; vendor preset: disabled)
     Active: active (running) since Tue 2022-04-26 21:02:44 EDT; 15min ago
   Main PID: 549 (trezord)
      Tasks: 10 (limit: 273)
     Memory: 10.2M
        CPU: 2.669s
     CGroup: /system.slice/trezord.service
             └─549 /usr/bin/trezord

Warning: some journal files were not opened due to insufficient permissions.

Gonna try reinstalling the udev rules again…

:EDIT 2:
No joy :frowning:

user@sys-usb ~$ ll /lib/udev/rules.d/50-trezor.rules 
lrwxrwxrwx 1 root root 33 Apr 26 21:36 /lib/udev/rules.d/50-trezor.rules -> /rw/config/trezor/50-trezor.rules
user@sys-usb ~$ ll /lib/udev/rules.d/51-trezor.rules 
-rw-r--r-- 1 root root 982 Apr 26 21:41 /lib/udev/rules.d/51-trezor.rule

I installed the bridge and udev rules to fedora-34-dvm (TemplateVM for sys-usb dispVM). I wonder what I’m missing. Got much farther than before. It can detect if the Trezor is connected at least.

My sys-usb is using a minimal-fedora-34 template, but Its also disposable.

I dont mind installing the rpm in sys-usb, but as my sys-usb is based on a disposible template i’ve installed them there. is that the correct thing to do?

After installing

trezor-bridge-2.0.27-1.x86_64.rpm
&
trezor-udev-2-1.noarch.rpm
into the minimal-fedora-34 template is see the following directories exist in sys-usb

> /lib/udev/rules.d/50-trezor.rules
> /usr/bin/trezord
> /usr/lib/systemd/system/trezord.service

I have this in sudo vim /usr/local/etc/qubes-rpc/trezord-service as per the wiki

#!/bin/sh
socat - TCP:localhost:21325

EDIT: ok this post solved the problem, it has to be made executible

Since I used the “disposable sys-usb” during the OS install, do I need to do something differently?

I am experiencing something that shouldnt happen.
WITHOUT attaching the trezor to my trezor-app-vm via sys-usb, the trezor suit can access the trezor ??!! Surely my trezor-app-vm shouldnt be able to directly read USB devices without them attached?
Is there a log i can provide to figure out what is going on?

̶Y̶o̶u̶r̶ ̶c̶h̶a̶n̶g̶e̶s̶ ̶h̶a̶v̶e̶ ̶t̶o̶ ̶b̶e̶ ̶i̶n̶ ̶t̶h̶e̶ ̶d̶i̶s̶p̶o̶s̶a̶b̶l̶e̶ ̶t̶e̶m̶p̶l̶a̶t̶e̶ ̶s̶y̶s̶-̶u̶s̶b̶ ̶u̶s̶e̶s̶,̶ ̶n̶o̶t̶ ̶t̶h̶e̶ ̶a̶c̶t̶i̶v̶e̶ ̶s̶y̶s̶-̶u̶s̶b̶ ̶(̶b̶e̶c̶a̶u̶s̶e̶ ̶t̶h̶a̶t̶ ̶l̶o̶s̶e̶s̶ ̶a̶l̶l̶ ̶c̶h̶a̶n̶g̶e̶s̶ ̶e̶a̶c̶h̶ ̶r̶e̶s̶t̶a̶r̶t̶)̶
scratch that. To try fix the above problem (trazor attaching to trezor-app-vm without sys-usb, i just uninstalled the .rpms from the fedora-34-minimal template the disposible one is based on, restarted them, and went back the “bridge not running” error.

EDIT: in an attempt to stop trezor connecting to trezor-app-vm without sys-usb being connected, i’ve created an entirely new minimal template, and disposible templete based on that, for sys-usb. That way the minimal template used for sys-firewall and sys-net didnt have any trezor.rpm packages installed.
BUT this didnt work. trezor can still directly talk to app-vm without being attached via sys-usb
Any ideas on why this is?

This is the expected behavior. You shouldn’t attach the trezor device to the app VM like you would a drive - for reasons I don’t understand, this doesn’t work well. The point of setting things up this way is that trezord (the trezor bridge) runs inside sys-usb to communicate with the device, and then you can run the trezor suite or electrum or whatever in an app vm.

When the trezor suite checks to see whether the bridge is running, it sends a TCP packet to localhost:21325. Normally (like not in qubes) trezord is listening at localhost:21325 and handles the request. Now, socat is listening on the app vm’s localhost:21325 and passes the data along through a qubes-rpc request to the qubes.Trezor executable in sys-usb. The qubes.Trezor executable takes the standard input from the qubes-rpc call and uses socat to forward it along to localhost:21325 in sys-usb where trezord is listening.

I’m sure there are logs somewhere about the qubes-rpc requests, but I’m not so familiar and don’t see anything in a quick search of the docs or in the obvious places in /var/log. What you can do to get some piece of mind is change the policy in dom0 (/etc/qubes/policy.d/30-user.policy by the new format and in the first post of the thread, or in /etc/qubes-rpc/policy by the wiki) so it says “ask” instead of “allow”. Then when the app vm makes the request, you’ll get a pop up asking for permission, though that will get annoying. This policy file is how you determine which vms can access the trezor.

1 Like

It looks like the udev rules are installed correctly. For me, it’s enough that 50-trezor.rules is put their as a symlink when rc.local runs. If you see those files in the disposable usb VM as it’s running when you try to use the trezor, I think it should work.

You say it can detect if the trezor is connected, but you’re seeing an error somewhere about missing udev rules? What do you do that produces an error? For me, when I plug the trezor in, I get a popup that says “Device available, Device, SatoshiLabs_TREZOR_… is available”, and then in a different app vm, I can run trezorctl btc get-address -n 1 and I get a prompt to enter my pin. Let me know where you’re getting stuck along the way.

One last thing I can think of is I have a trezor one. I’m very confident the rest of the steps of getting trezord and setting up the qubes-rpc stuff are the same, but maybe the udev rules are different? I doubt it but worth checking if all else fails.

Yes, my Trezor asks for my PIN, after that, both the desktop AppImage and the WebUI both go from “Connect your Trezor” to “Missing udev rules”.

That’s a lot confusing. I haven’t tried NOT attaching it to my AppVM… then again, I don’t plug things into my Qubes box unless mandatory.

Hmm, well if it’s asking for your PIN then somehow the data is getting from the trezor suite to the device, so that’s a good sign.

If wonder if trezord is running in the app vm, and the udev rules are only installed in sys-usb or something like that? Consider whether they use the same template. The setup you want is like:

App VM:
- has socat listening on localhost:21325 to forward to qubes-rpc
- has trezorctl, trezor suite, or other UI tools

Dom0:
- has policy to allow trezor qubes-rpc from App VM to sys-usb

Sys-usb:
- has udev rules
- has trezord running
- has executable called by qubes-rpc that forwards along to localhost:21325
- has the trezor plugged in

I think that even if you install the udev rules and trezord in the app VM, it won’t work because of how the hardware is “passed” from sys-usb. It’s some black magic that is not just like having it plugged in in a bare metal OS. That’s why we need the qubes-rpc setup.

Try it without attaching to the app VM and see if it works. If it doesn’t ask for the pin, then somehow the connection isn’t getting through, and you should check with sudo netstat -tlp on the app VM and see that socat is listening on 21325. If it is, try to run wireshark on sys-usb and see if some packet appear on its network interface when you try to interact with the trezor in the app VM. You can also try to change the policy in dom0 to “ask” instead of “allow” and then a popup should ask for permission when you try to interact with the trezor. Hope that helps.

Yep. I did something similar. New to this still, so I may have missed a step.

fedora-34-dvm  (TemplateVM for sys-usb DispVM):
Installed bridge + udev here

user@sys-usb ~$ netstat | grep 21325
tcp        0      0 localhost:21325         localhost:36066         ESTABLISHED
tcp        0      0 localhost:36066         localhost:21325         ESTABLISHED
user@sys-usb ~$ ps aux | grep trezord
trezord     1773  0.0  6.5 704092 15372 ?        Ssl  16:11   0:04 /usr/bin/trezord

TemplateVM: whonix-16-coinpress-dvm (clone of whonix-ws-16 dvm)
Copied Trezor Suite to here.
Installed other currency UIs here.

DispVM: coinpress-<currency_goes_here>

user@host:~$ netstat -an | grep "21325"
tcp        0      0 0.0.0.0:21325           0.0.0.0:*               LISTEN

That’s about as far as I can get. Doesn’t matter if I attach it to the dispVM or not. Policy is allowing $anyvm for testing purposes right now.

:EDIT: Here some of the Trezor Suite AppImage logs:

  {
    "type": "@suite/online-status",
    "time": 1651360032407
  },
  {
    "type": "@suite/app-changed",
    "time": 1651360032408,
    "action": {
      "type": "@suite/app-changed",
      "payload": "dashboard"
    }
  },
  {
    "type": "@router/location-change",
    "time": 1651360032408
  },
  {
    "type": "@suite/init",
    "time": 1651360032409,
    "action": {
      "type": "@suite/init",
      "payload": {
        "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) TrezorSuite/22.3.2 Chrome/94.0.4606.81 Electron/15.3.0 Safari/537.36",
        "commitHash": <SNIP SNIP>,
        "suiteType": "desktop"
      }
    }
  },
  {
    "type": "@message-system/save-valid-messages",
    "time": 1651360032427
  },
  {
    "type": "@suite/tor-status",
    "time": 1651360032427
  },
  {
    "type": "@desktop-update/allow-prerelease",
    "time": 1651360032433
  },
  {
    "type": "@desktop-update/enable",
    "time": 1651360032435
  },
  {
    "type": "@desktop-update/checking",
    "time": 1651360032465
  },
  {
    "type": "@message-system/save-valid-messages",
    "time": 1651360032552
  },
  {
    "type": "@suite/set-language",
    "time": 1651360032553,
    "action": {
      "type": "@suite/set-language",
      "payload": {
        "locale": "en"
      }
    }
  },
  {
    "type": "@recovery/reset-reducer",
    "time": 1651360032556
  },
  {
    "type": "@onboarding/enable-onboarding-reducer",
    "time": 1651360032557
  },
  {
    "type": "@suite/app-changed",
    "time": 1651360032558,
    "action": {
      "type": "@suite/app-changed",
      "payload": "onboarding"
    }
  },
  {
    "type": "@router/location-change",
    "time": 1651360032599
  },
  {
    "type": "@suite/lock-router",
    "time": 1651360032605
  },
  {
    "type": "@storage/loaded",
    "time": 1651360032607
  },
  {
    "type": "@resize/update-window-size",
    "time": 1651360032727
  },
  {
    "type": "@message-system/save-valid-messages",
    "time": 1651360034688
  },
  {
    "type": "@message-system/fetch-config-success-update",
    "time": 1651360034691
  },
  {
    "type": "@analytics/init",
    "time": 1651360034700
  },
  {
    "type": "@suite/trezor-connect-initialized",
    "time": 1651360034726
  },
  {
    "type": "@blockchain/update-fee",
    "time": 1651360034753
  },
  {
    "type": "@suite/ready",
    "time": 1651360034772
  },
  {
    "type": "@blockchain/ready",
    "time": 1651360034774
  },
  {
    "type": "@notification/event",
    "time": 1651360035598
  },
  {
    "type": "@account/dispose",
    "time": 1651360035620
  },
  {
    "type": "@send/dispose",
    "time": 1651360035625
  },
  {
    "type": "@receive/dispose",
    "time": 1651360035627
  },
  {
    "type": "@coinmarket-buy/dispose",
    "time": 1651360035628
  },
  {
    "type": "@message-system/save-valid-messages",
    "time": 1651360035632
  },
  {
    "type": "@suite/select-device",
    "time": 1651360035644
  },
  {
    "type": "device-connect_unacquired",
    "time": 1651360035647
  },
  {
    "type": "@message-system/save-valid-messages",
    "time": 1651360035669
  },
  {
    "type": "transport-start",
    "time": 1651360035673,
    "action": {
      "type": "transport-start",
      "payload": {
        "type": "bridge",
        "version": "2.0.27"
      }
    }
  },
  {
    "type": "@message-system/dismiss-message",
    "time": 1651360038567
  },
  {
    "type": "@suite/lock-router",
    "time": 1651360039295
  },
  {
    "type": "@backup/reset-reducer",
    "time": 1651360039295
  },
  {
    "type": "@firmware/reset-reducer",
    "time": 1651360039297
  },
  {
    "type": "@suite/set-flag",
    "time": 1651360039301
  },
  {
    "type": "@onboarding/reset-onboarding",
    "time": 1651360039302
  },
  {
    "type": "@suite/app-changed",
    "time": 1651360039302,
    "action": {
      "type": "@suite/app-changed",
      "payload": "settings"
    }
  },
  {
    "type": "@router/location-change",
    "time": 1651360039304
  },
  {
    "type": "@desktop-update/available",
    "time": 1651360039763
  },
  {
    "type": "@desktop-update/window",
    "time": 1651359860732
  },
  {
    "type": "@modal/open-user-context",
    "time": 1651359864288
  }

Hmmmm, that is unexpected, and I didn’t figure anything clever out from reading the logs. I can suggest two things to try next.

First, try (temporarily) giving the sys-usb dvm network and install trezorctl with pip pip install --user trezor, or the trezor suite, and see if it works that way. On my machine, I couldn’t get the trezor suite gui to behave on a VM in HVM mode, which is why I suggest the simpler trezorctl cli tool. If that doesn’t work, then it’s definitely not about the communication between the VMs.

Second, in the normal setup with e.g. coinpress-btc running the suite and sys-usb-dvm running trezord, try sudo wireshark on sys-usb-dvm, listen on all interfaces, and see if TCP packets appear to/from port 21325 when you open the trezor suite.

This should determine whether it’s about the inter-vm communication or about something wrong between sys-usb-dvm and the trezor itself.

Oh, one other thing is to try the trezor on another machine, or running tails on your qubes device, since it could be the trezor itself.

The items that stuck out to me were these:

{
    "type": "@suite/trezor-connect-initialized",
    "time": 1651360034726
},
<..... stuff ..... >
{
    "type": "device-connect_unacquired",
    "time": 1651360035647
}

These logs aren’t documented on Trezor’s site, so I can only take an edumacated guesstimation that the USB protocol is functioning as expected. It seems the Trezor API is timing out. So yeah, I will try sys-usb install test when I have some cycles and report back.

P.S. My apologies on the edits. Bad habits from another platform. I’ll try to keep that to typo corrections.

Yeah, it’s just not so informative from the logs what it was that went wrong. Good luck, I hope we figure it out :slight_smile: and no worries

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.