From 51f6874dc157d8e41c86b78fd587dd2d96ed77b8 Mon Sep 17 00:00:00 2001 From: Peter Sanderson Date: Wed, 18 Dec 2024 12:39:29 +0100 Subject: [PATCH] fix: remove auto-install of udev-rules for Linux that requires running SH scipt with sudo. Its spooky. --- .../src/__tests__/api.test.ts | 17 --- packages/suite-desktop-api/src/api.ts | 2 - packages/suite-desktop-api/src/factory.ts | 3 - .../suite-desktop-core/src/modules/index.ts | 3 +- .../src/modules/udev-install.ts | 73 ------------- .../PrerequisitesGuide/DeviceUnreadable.tsx | 103 ++---------------- packages/suite/src/support/messages.ts | 8 -- 7 files changed, 9 insertions(+), 200 deletions(-) delete mode 100644 packages/suite-desktop-core/src/modules/udev-install.ts diff --git a/packages/suite-desktop-api/src/__tests__/api.test.ts b/packages/suite-desktop-api/src/__tests__/api.test.ts index 5327d2269a5..02253ac43f8 100644 --- a/packages/suite-desktop-api/src/__tests__/api.test.ts +++ b/packages/suite-desktop-api/src/__tests__/api.test.ts @@ -268,23 +268,6 @@ describe('DesktopApi', () => { expect(spy).toHaveBeenCalledWith('user-data/open', existingDirectory); }); - it('DesktopApi.installUdevRules', async () => { - const spy = jest - .spyOn(ipcRenderer, 'invoke') - .mockImplementation(() => Promise.resolve({ success: true })); - const result = await api.installUdevRules(); - expect(spy).toHaveBeenCalledWith('udev/install'); - expect(result.success).toBe(true); - if (result.success) { - expect(result.payload).toBe(undefined); - } else { - expect(result.error).toBe('should not happen'); - } - - // @ts-expect-error no expected params - api.installUdevRules(true); - }); - it('DesktopApi.handshake', async () => { const spy = jest .spyOn(ipcRenderer, 'invoke') diff --git a/packages/suite-desktop-api/src/api.ts b/packages/suite-desktop-api/src/api.ts index 6c810a72b39..8f897b8d176 100644 --- a/packages/suite-desktop-api/src/api.ts +++ b/packages/suite-desktop-api/src/api.ts @@ -159,8 +159,6 @@ export interface DesktopApi { clearStore: DesktopApiSend<'store/clear'>; clearUserData: DesktopApiInvoke<'user-data/clear'>; openUserDataDirectory: DesktopApiInvoke<'user-data/open'>; - // Udev rules - installUdevRules: DesktopApiInvoke<'udev/install'>; // Logger configLogger: DesktopApiSend<'logger/config'>; // Bridge diff --git a/packages/suite-desktop-api/src/factory.ts b/packages/suite-desktop-api/src/factory.ts index baa14bfd23f..93fc7e28c7d 100644 --- a/packages/suite-desktop-api/src/factory.ts +++ b/packages/suite-desktop-api/src/factory.ts @@ -145,9 +145,6 @@ export const factory = >( return Promise.resolve({ success: false, error: 'invalid params' }); }, - // Udev rules - installUdevRules: () => ipcRenderer.invoke('udev/install'), - // Logger configLogger: config => { ipcRenderer.send('logger/config', config); diff --git a/packages/suite-desktop-core/src/modules/index.ts b/packages/suite-desktop-core/src/modules/index.ts index c643741dc19..d5b2ea7d065 100644 --- a/packages/suite-desktop-core/src/modules/index.ts +++ b/packages/suite-desktop-core/src/modules/index.ts @@ -23,7 +23,6 @@ import * as metadata from './metadata'; import * as customProtocols from './custom-protocols'; import * as autoUpdater from './auto-updater'; import * as store from './store'; -import * as udevInstall from './udev-install'; import * as userData from './user-data'; import * as trezorConnect from './trezor-connect'; import * as devTools from './dev-tools'; @@ -56,7 +55,6 @@ const MODULES: Module[] = [ customProtocols, autoUpdater, store, - udevInstall, userData, trezorConnect, devTools, @@ -85,6 +83,7 @@ interface MainThreadMessages { 'app/fully-quit': void; 'app/show': void; } + export const mainThreadEmitter = new TypedEmitter(); export type MainThreadEmitter = typeof mainThreadEmitter; diff --git a/packages/suite-desktop-core/src/modules/udev-install.ts b/packages/suite-desktop-core/src/modules/udev-install.ts deleted file mode 100644 index c4435fbfc9e..00000000000 --- a/packages/suite-desktop-core/src/modules/udev-install.ts +++ /dev/null @@ -1,73 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { spawn } from 'child_process'; - -import { validateIpcMessage } from '@trezor/ipc-proxy'; -import { checkFileExists } from '@trezor/node-utils'; - -import { app, ipcMain } from '../typed-electron'; - -import type { ModuleInit } from './index'; - -const FILE_NAME = '51-trezor.rules'; - -export const SERVICE_NAME = 'udev'; - -export const init: ModuleInit = () => { - ipcMain.handle('udev/install', async ipcEvent => { - validateIpcMessage(ipcEvent); - - const { logger, resourcesPath } = global; - const resourceRules = path.join(resourcesPath, `bin/udev/${FILE_NAME}`); - const userRules = path.join(app.getPath('userData'), FILE_NAME); - const distRules = path.join('/etc/udev/rules.d/', FILE_NAME); - - logger.info(SERVICE_NAME, `Installing ${resourceRules} > ${userRules} > ${distRules}`); - - if (await checkFileExists(distRules)) { - logger.error(SERVICE_NAME, `/etc/udev rules already installed: ${distRules}`); - - // /etc/udev already exists, break here. - // TODO: should override anyway? - return { success: false, error: `File ${distRules} already exists` }; - } - - if (!(await checkFileExists(userRules))) { - try { - logger.info(SERVICE_NAME, `Create user data rules: ${userRules}`); - // copy rules from app resources (/tmp/...) to user data files (~/.cache/...) - // this step is required. `pkexec` returns "Permission denied" error when copying directly from app resources. - await fs.promises.copyFile(resourceRules, userRules); - // chmod to read-only - await fs.promises.chmod(userRules, 0o444); - } catch (error) { - logger.error(SERVICE_NAME, `User data rules error ${error}`); - - return { success: false, error: `${error}` }; - } - } - - return new Promise(resolve => { - logger.info(SERVICE_NAME, `Copy rules from ${userRules} to ${distRules}`); - // request superuser permissions and copy from user data files to /etc/udev - // NOTE: https://github.blog/2021-06-10-privilege-escalation-polkit-root-on-linux-with-bug/ - const pkexec = spawn('pkexec', ['cp', userRules, distRules]); - - pkexec.on('error', error => { - logger.error(SERVICE_NAME, `pkexec error ${error}`); - resolve({ success: false, error: `pkexec error ${error.message}` }); - }); - - pkexec.on('exit', code => { - logger.debug(SERVICE_NAME, `pkexec exit with code ${code}`); - if (code === 0) { - resolve({ success: true }); - } else if (code === 126) { - resolve({ success: false, error: `pkexec authentication dialog dismissed` }); - } else { - resolve({ success: false, error: `pkexec exit with code ${code}` }); - } - }); - }); - }); -}; diff --git a/packages/suite/src/components/suite/PrerequisitesGuide/DeviceUnreadable.tsx b/packages/suite/src/components/suite/PrerequisitesGuide/DeviceUnreadable.tsx index 22c100029dd..a47887f70d4 100644 --- a/packages/suite/src/components/suite/PrerequisitesGuide/DeviceUnreadable.tsx +++ b/packages/suite/src/components/suite/PrerequisitesGuide/DeviceUnreadable.tsx @@ -1,12 +1,7 @@ -import { useState, MouseEvent } from 'react'; - -import { Button } from '@trezor/components'; -import { desktopApi } from '@trezor/suite-desktop-api'; -import { isDesktop, isLinux } from '@trezor/env-utils'; -import { notificationsActions } from '@suite-common/toast-notifications'; import { selectSelectedDevice } from '@suite-common/wallet-core'; +import { isLinux } from '@trezor/env-utils'; -import { Translation, TroubleshootingTips, UdevDownload } from 'src/components/suite'; +import { Translation, TroubleshootingTips } from 'src/components/suite'; import { TROUBLESHOOTING_TIP_SUITE_DESKTOP, TROUBLESHOOTING_TIP_DIFFERENT_COMPUTER, @@ -14,93 +9,11 @@ import { TROUBLESHOOTING_TIP_SUITE_DESKTOP_TOGGLE_BRIDGE, TROUBLESHOOTING_TIP_RECONNECT, TROUBLESHOOTING_TIP_CLOSE_ALL_TABS, + TROUBLESHOOTING_TIP_UDEV, } from 'src/components/suite/troubleshooting/tips'; -import { useSelector, useDispatch } from 'src/hooks/suite'; +import { useSelector } from 'src/hooks/suite'; import type { TrezorDevice } from 'src/types/suite'; -// linux web -const UdevWeb = () => ( - } - items={[ - { - key: 'udev-about', - noBullet: true, - description: , - }, - { - key: 'udev-download', - noBullet: true, - description: , - }, - ]} - data-testid="@connect-device-prompt/unreadable-udev" - /> -); - -// linux desktop -const UdevDesktop = () => { - const [response, setResponse] = useState(-1); - - const dispatch = useDispatch(); - - const handleCtaClick = async (event: MouseEvent) => { - event.preventDefault(); - event.stopPropagation(); - - const resp = await desktopApi.installUdevRules(); - - if (resp?.success) { - setResponse(1); - } else { - dispatch( - notificationsActions.addToast({ - type: 'error', - error: resp?.error || 'desktopApi not available', - }), - ); - - setResponse(0); - } - }; - - if (response === 1) { - return ( - } - items={[]} - data-testid="@connect-device-prompt/unreadable-udev" - /> - ); - } - - return ( - } - cta={ - - } - items={[ - { - key: 'udev-about', - description: , - noBullet: true, - }, - { - key: 'udev-download', - description: , - noBullet: true, - }, - ]} - data-testid="@connect-device-prompt/unreadable-udev" - /> - ); -}; - interface DeviceUnreadableProps { device?: TrezorDevice; // this should be actually UnreadableDevice, but it is not worth type casting } @@ -114,14 +27,14 @@ interface DeviceUnreadableProps { export const DeviceUnreadable = ({ device }: DeviceUnreadableProps) => { const selectedDevice = useSelector(selectSelectedDevice); + // generic troubleshooting tips + const items = []; + // this error is dispatched by trezord when udev rules are missing if (isLinux() && device?.error === 'LIBUSB_ERROR_ACCESS') { - return <> {isDesktop() ? : }; + items.push(TROUBLESHOOTING_TIP_UDEV); } - // generic troubleshooting tips - const items = []; - // only for unreadable HID devices if ( // model 1 hid normal mode diff --git a/packages/suite/src/support/messages.ts b/packages/suite/src/support/messages.ts index 6126ce18a92..096e7b330ea 100644 --- a/packages/suite/src/support/messages.ts +++ b/packages/suite/src/support/messages.ts @@ -7305,18 +7305,10 @@ export default defineMessages({ defaultMessage: 'Restarting your computer may fix the communication issue between your browser and device.', }, - TR_TROUBLESHOOTING_UNREADABLE_UDEV: { - id: 'TR_TROUBLESHOOTING_UNREADABLE_UDEV', - defaultMessage: 'Missing udev rules', - }, TR_TROUBLESHOOTING_UNREADABLE_UNKNOWN: { id: 'TR_TROUBLESHOOTING_UNREADABLE_UNKNOWN', defaultMessage: 'Unexpected state: {error}', }, - TR_TROUBLESHOOTING_UDEV_INSTALL_TITLE: { - id: 'TR_TROUBLESHOOTING_UDEV_INSTALL_TITLE', - defaultMessage: 'Install rules automatically', - }, TR_SEEDLESS_SETUP_IS_NOT_SUPPORTED_TITLE: { id: 'TR_SEEDLESS_SETUP_IS_NOT_SUPPORTED_TITLE', defaultMessage: "Seedless setup isn't supported in Trezor Suite",