상황
라즈베리파이에서 자판기와 비슷한 것을 구현하기 위해 Electron을 프론트엔드로 사용하고 있다.
간편한 유지보수를 위해 Electron 프론트엔드에 자동 업데이트를 구현하려고, electron-builder
에서 제공하는 업데이트 기능인 electron-updater
를 도입했다.
우선 나는 라즈베리파이OS (데비안 12 Bookworm), 라즈베리파이4B 기준으로 테스트했다.
문제점
다만 예상하지 못한 문제가 생겼다면, 업데이트를 받아오고 설치를 시도할 때마다 다음과 같은 창이 뜬다는 것이다.

조금 찾아보니 이건 pkexec
라는 도구인데, 명령어 실행 권한을 가지고 있는 사용자로 명령어를 실행할 수 있도록 도와주는 도구라고 한다.
해결
시도 #1 - sudoer에 사용자 NOPASSWD 지정 (실패)
sudoer 파일에서 sudo 권한이 있는 사용자를 지정해줄 수 있는데, 원래 파일은 /etc/sudoers
에 위치한다. 이 파일을 보다 안전하게 수정하고 싶다면 visudo
명령을 이용해주면 된다.
# User privilege specificationroot ALL=(ALL:ALL) ALL
# Allow members of group sudo to execute any command%sudo ALL=(ALL:ALL) ALL
# See sudoers(5) for more information on "@include" directives:@includedir /etc/sudoers.d
기본 설정 이외에는 @includedir를 이용해 설정 파일을 나누어 관리하고 있는데, 이 디렉터리를 알아보자.
ls -l /etc/sudoers.d
명령을 실행했다.
-r--r----- 1 root root 36 Apr 29 2019 010_at-export-r--r----- 1 root root 44 Jun 19 21:48 010_dpkg-threads-r--r----- 1 root root 31 Jul 26 2023 010_global-tty-r--r----- 1 root root 28 Jul 4 09:16 010_pi-nopasswd-r--r----- 1 root root 211 Jun 7 23:10 010_proxy-r--r----- 1 root root 1096 Jun 27 2023 README
여기에 010_pi-nopasswd
라는 파일이 있어 해당 파일을 열었더니
username ALL=(ALL) NOPASSWD: ALL
이처럼 이미 라즈베리파이 기본 유저에 대한 NOPASSWD 설정이 되어 있었다. 어쩐지 sudo에서 비밀번호를 입력한 적이 없더라니…
시도 #2 - pkexec를 비밀번호 없이 실행? (실패)
이 질문 글에서 동일한 문제를 확인했다. /usr/share/polkit-1/rules.d
폴더에 들어가니 dpkg 관련된 파일 org.dpkg.pkexec.update-alternatives.policy
가 있어 해당 파일을 수정했다.
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd"><policyconfig> <vendor>The Dpkg Project</vendor> <vendor_url>https://wiki.debian.org/Teams/Dpkg</vendor_url> <icon_name>update-alternatives</icon_name> <action id="org.dpkg.pkexec.update-alternatives"> <description>Run update-alternatives to modify system alternative selections</description> <defaults> <allow_any>auth_admin_keep</allow_any> <allow_inactive>auth_admin_keep</allow_inactive> <allow_active>auth_admin_keep</allow_active> </defaults> <annotate key="org.freedesktop.policykit.exec.path">/usr/bin/update-alternatives</annotate> </action></policyconfig>
여기 auth_admin_keep
을 yes
로 바꿔주었으나 변화는 없었다.
해결 - electron-updater 내부 sudo 명령어 수정
electron-updater 패키지의 BaseUpdater
파일을 보면 wrapSudo
라는 명령어를 sudo 커맨드로 말아주는 메소드가 있다.
이 메소드 중 다음과 같이 어떤 sudo wrapper(?)를 사용해야 하는지 구하는 코드가 있다.
const sudo = this.spawnSyncLog("which gksudo || which kdesudo || which pkexec || which beesu")
나는 어짜피 사용자에게 sudo 권한이 무조건 있고, sudo를 입력했을 때 터미널에서 별도의 비밀번호를 입력하는 인풋이 나오지 않는 환경에서만 업데이트가 이루어진다.
따라서 위 코드를 다음과 같이 바꿔준다.
const sudo = this.spawnSyncLog("which sudo")
라즈베리파이는 데비안 기반이라 electron-updater중에서도 DebUpdater를 사용한다. 해당 파일을 둘러보자.
보면 실제 설치가 일어나는 doInstall
이라는 메소드가 있다. 이걸 자세히 보자.
protected doInstall(options: InstallOptions): boolean { const sudo = this.wrapSudo() // pkexec doesn't want the command to be wrapped in " quotes const wrapper = /pkexec/i.test(sudo) ? "" : `"` const cmd = ["dpkg", "-i", options.installerPath, "||", "apt-get", "install", "-f", "-y"] this.spawnSyncLog(sudo, [`${wrapper}/bin/bash`, "-c", `'${cmd.join(" ")}'${wrapper}`]) if (options.isForceRunAfter) { this.app.relaunch() } return true}
위 BaseUpdater에서 수정했던 wrapSudo를 이용하여 sudo를 이용해 명령을 실행하는데, sudo wrapper를 기준으로 구성되어 sudo를 사용하면 dpkg가 올바르게 동작하지 않는다.
파일 메소드를 조금 수정하여 간단하게 sudo dpkg -i <파일> || apt-get install -f -y
만 실행할 수 있도록 수정한다:
protected doInstall(options: InstallOptions): boolean { const sudo = this.wrapSudo() // pkexec doesn't want the command to be wrapped in " quotes const wrapper = /pkexec/i.test(sudo) ? "" : `"` const cmd = ["dpkg", "-i", options.installerPath, "||", "apt-get", "install", "-f", "-y"] this.spawnSyncLog(sudo, cmd.join(" ")) if (options.isForceRunAfter) { this.app.relaunch() } return true}
이렇게 수정한 부분을 pnpm patch를 이용하여 패키지를 일일히 수정하지 않아도 모든 환경에서 반영될 수 있도록 했다.