발단: GitHub Actions 분수를 다 썼습니다
운영하는 GitHub Organization에서 이번 달 GitHub Actions 무료 사용량을 다 써버렸습니다. 결제 수단을 등록해야 했는데, 평소 해외 결제용으로 쓰던 트래블월렛 카드로는 결제가 되지 않았습니다. 그래서 하나카드로 다시 시도했습니다.
하나카드 결제 인증 단계에서 익숙한 화면이 나왔습니다. "보안 프로그램을 설치해 주세요." 받은 파일은 NWS_IPinsideAgent.dmg였습니다. 결제는 해야 하니 일단 설치했습니다.
결제는 끝났습니다. 그런데 macOS 시스템 설정의 로그인 항목에 이런 줄이 새로 생겼습니다.
interezen co., Ltd.
1개의 항목: 1개의 항목이 모든 사용자에게 영향을 줌
"모든 사용자에게 영향을 줌"은 사용자 계정 단위가 아니라 시스템 전체에서 root로 도는 데몬이라는 뜻입니다. 결제 한 번 하자고 이걸 상주시킬 이유는 없습니다. 다만 앱만 휴지통에 버리면 무엇이 남는지 알 수 없었습니다. 그래서 Claude Code에게 "이거 설치된 거 완전히 다 삭제해줘"라고 맡겼습니다.
분석: 실제로 무엇이 설치됐나
1. 패키지 영수증부터 확인
macOS .pkg 설치기는 어떤 파일을 깔았는지 영수증(receipt)을 남깁니다. 삭제할 대상을 추측하지 않고 이 목록에서 시작하는 게 가장 확실합니다.
$ pkgutil --pkgs | grep -i interezen
com.interezen.IPinside
$ pkgutil --files com.interezen.IPinside
Applications/IPinside.app
Applications/IPinside.app/Contents/MacOS/IPinside
Applications/IPinside.app/Contents/MacOS/NWSDaemon
Applications/IPinside.app/Contents/Resources/Interezen.tgz
Applications/IPinside.app/Contents/Resources/InterezenSSLServerCert.ext
Applications/IPinside.app/Contents/Resources/openssl
Applications/IPinside.app/Contents/Resources/openssl.cnf
...
Library/LaunchDaemons/com.interezen.nwsdaemon.plist
파일 경로는 두 군데뿐입니다. 하지만 눈에 띄는 게 있습니다. 앱 안에 **openssl 바이너리와 SSL 서버 인증서 확장 설정 파일(InterezenSSLServerCert.ext)**이 들어 있습니다. 설치 과정에서 인증서를 직접 만들어 어딘가에 넣는다는 신호입니다. 그리고 이 작업은 영수증의 파일 목록에 나타나지 않습니다.
2. 데몬 설정
$ cat /Library/LaunchDaemons/com.interezen.nwsdaemon.plist
<key>Label</key>
<string>com.interezen.nwsdaemon</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/IPinside.app/Contents/MacOS/NWSDaemon</string>
</array>
<key>KeepAlive</key>
<true/>
KeepAlive가 true입니다. 활성 상태 보기에서 프로세스를 강제 종료해도 launchd가 바로 다시 띄웁니다. 참고로 이 plist의 수정일은 2018년 11월 24일이었습니다. 이 데몬 설정은 적어도 8년 가까이 그대로라는 얘기입니다.
3. 키체인에 숨어 있던 루트 인증서
영수증에 없는 흔적을 찾으려고 System 키체인과 로그인 키체인의 인증서를 전부 꺼냈습니다(총 16개). 그리고 주체(subject)를 하나씩 확인했습니다.
[email protected], CN=INTEREZEN CA, OU=INTEREZEN,
O=INTEREZEN, L=SEOUL, ST=SEOUL, C=KR
issuer [email protected], CN=INTEREZEN CA, ...
INTEREZEN CA 자체 서명 루트 인증서가 System 키체인에 들어 있었습니다. subject와 issuer가 같으니 루트 CA입니다. 로컬 데몬이 https://127.0.0.1로 브라우저와 통신하려고 만든 것으로 보입니다(추정).
이 인증서가 이 글의 핵심입니다. 시스템이 신뢰하는 루트 CA가 하나 늘었다는 건, 그 CA의 개인키를 가진 쪽이 어떤 도메인의 인증서든 발급할 수 있고 내 Mac이 그걸 믿을 수 있다는 뜻입니다. 앱을 지워도 키체인 항목은 그대로 남습니다. 그래서 "앱만 드래그해서 삭제"로는 정리가 끝나지 않습니다.
홈 폴더의 Preferences, Caches, Application Support도 뒤져 봤습니다. interezen, ipinside 관련 파일은 없었습니다.
정리하면 설치 흔적은 네 가지입니다.
| 항목 | 위치 |
|---|---|
| 앱 본체 | /Applications/IPinside.app |
| root 데몬 (로그인 항목에 보이던 것) | /Library/LaunchDaemons/com.interezen.nwsdaemon.plist |
루트 인증서 INTEREZEN CA |
/Library/Keychains/System.keychain |
| 설치 영수증 | com.interezen.IPinside |
삭제: 함정 하나
삭제 스크립트를 만들고 Claude Code 프롬프트에서 ! 접두사로 실행했더니 바로 막혔습니다.
$ ! sudo bash uninstall_ipinside.sh
sudo: a terminal is required to read the password; either use the -S option
to read from standard input or configure an askpass helper
sudo: a password is required
Claude Code의 ! 명령은 TTY가 없는 환경에서 실행됩니다. 그래서 sudo가 비밀번호를 물어볼 곳이 없습니다. 해결책은 macOS의 GUI 비밀번호 창을 쓰는 것이었습니다.
osascript -e 'do shell script "bash /path/to/uninstall-ipinside-mac.sh --apply" with administrator privileges'
이렇게 실행하면 익숙한 macOS 관리자 비밀번호 창이 뜨고, 입력하면 root 권한으로 실행됩니다. 결과는 다음과 같았습니다.
[1] stop daemon
[2] remove files
/Library/LaunchDaemons/com.interezen.nwsdaemon.plist
removed /Applications/IPinside.app
[3] remove INTEREZEN CA root certificate
[4] forget pkg receipt
Forgot package 'com.interezen.IPinside' on '/'.
done
삭제 후 다시 확인했습니다.
gone /Applications/IPinside.app
gone /Library/LaunchDaemons/com.interezen.nwsdaemon.plist
receipts gone
--- pkg: none
--- cert: security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.
--- proc: none
남은 흔적은 없었습니다. 로그인 항목의 "interezen co., Ltd." 줄은 설정 화면의 캐시라서 재로그인이나 재부팅 후 사라집니다.
가져다 쓰는 스크립트
제 환경에서 쓴 스크립트는 인증서 SHA-1 해시를 직접 박아 두었습니다. 하지만 IPinside가 앱 안의 openssl로 인증서를 만드는 구조라면 해시가 PC마다 다를 수 있습니다. 그래서 공개용 스크립트는 인증서를 이름으로 찾은 뒤 해시를 뽑아 지우게 바꿨습니다.
또 하나, 기본 동작은 점검만 하도록 했습니다. 무엇이 지워질지 먼저 확인한 다음 --apply로 실행하는 방식입니다.
#!/bin/bash
# IPinside (interezen) 완전 삭제 스크립트 for macOS
# 점검만: bash uninstall-ipinside-mac.sh
# 삭제: sudo bash uninstall-ipinside-mac.sh --apply
set -u
APPLY=0
[ "${1:-}" = "--apply" ] && APPLY=1
APP="/Applications/IPinside.app"
DAEMON_LABEL="com.interezen.nwsdaemon"
DAEMON_PLIST="/Library/LaunchDaemons/${DAEMON_LABEL}.plist"
PKG_ID="com.interezen.IPinside"
KEYCHAIN="/Library/Keychains/System.keychain"
CERT_NAME="INTEREZEN CA"
if [ "$APPLY" -eq 1 ] && [ "$(id -u)" -ne 0 ]; then
echo "삭제 모드는 sudo가 필요합니다: sudo bash $0 --apply"
exit 1
fi
# 이름으로 찾은 인증서들의 SHA-1 해시 (설치마다 다를 수 있어 하드코딩하지 않음)
cert_hashes() {
security find-certificate -a -c "$CERT_NAME" -Z "$KEYCHAIN" 2>/dev/null \
| awk '/^SHA-1 hash:/ {print $3}'
}
echo "== 점검 결과 =="
[ -e "$APP" ] && echo "[발견] $APP" || echo "[없음] $APP"
[ -e "$DAEMON_PLIST" ] && echo "[발견] $DAEMON_PLIST" || echo "[없음] $DAEMON_PLIST"
pkgutil --pkg-info "$PKG_ID" >/dev/null 2>&1 && echo "[발견] 설치 기록 $PKG_ID" || echo "[없음] 설치 기록 $PKG_ID"
HASHES=$(cert_hashes)
if [ -n "$HASHES" ]; then
for h in $HASHES; do echo "[발견] 루트 인증서 '$CERT_NAME' (SHA-1 $h)"; done
else
echo "[없음] 루트 인증서 '$CERT_NAME'"
fi
if [ "$APPLY" -eq 0 ]; then
echo
echo "점검만 했습니다. 지우려면: sudo bash $0 --apply"
exit 0
fi
echo
echo "== 삭제 =="
# KeepAlive=true 데몬이라 프로세스만 죽이면 launchd가 되살린다 → 등록부터 해제
launchctl bootout "system/$DAEMON_LABEL" 2>/dev/null && echo "데몬 등록 해제"
pkill -f "$APP" 2>/dev/null
rm -f "$DAEMON_PLIST" && echo "삭제: $DAEMON_PLIST"
rm -rf "$APP" && echo "삭제: $APP"
for h in $HASHES; do
security delete-certificate -Z "$h" "$KEYCHAIN" && echo "삭제: 인증서 SHA-1 $h"
done
pkgutil --forget "$PKG_ID" >/dev/null 2>&1 && echo "삭제: 설치 기록 $PKG_ID"
echo
echo "완료. 로그인 항목의 'interezen' 표시는 재로그인/재부팅 후 사라집니다."
사용법
# 1) 파일로 저장한 뒤 점검 (root 권한 불필요)
bash uninstall-ipinside-mac.sh
# 2) 삭제 — 터미널 앱에서
sudo bash uninstall-ipinside-mac.sh --apply
# 2') 또는 TTY가 없는 환경(Claude Code의 ! 명령 등)에서
osascript -e 'do shell script "bash ~/uninstall-ipinside-mac.sh --apply" with administrator privileges'
이미 지운 제 Mac에서 점검 모드를 돌리면 이렇게 나옵니다.
== 점검 결과 ==
[없음] /Applications/IPinside.app
[없음] /Library/LaunchDaemons/com.interezen.nwsdaemon.plist
[없음] 설치 기록 com.interezen.IPinside
[없음] 루트 인증서 'INTEREZEN CA'
점검만 했습니다. 지우려면: sudo bash uninstall-ipinside-mac.sh --apply
스크립트 설계에서 신경 쓴 부분
- 삭제 순서: 데몬 등록 해제(
launchctl bootout)를 먼저 하고 그다음에 파일을 지웁니다. 순서가 반대면KeepAlive가 걸린 데몬이 지워진 바이너리를 계속 다시 띄우려고 시도합니다. - 인증서는 해시로 지정해서 삭제:
security delete-certificate -c "이름"도 쓸 수 있습니다. 하지만 이름이 겹치는 인증서가 여러 개면 어떤 게 지워질지 애매합니다. 그래서 이름으로 찾되, 실제 삭제는 SHA-1 해시(-Z)로 하나씩 정확히 합니다. sfltool resetbtm은 넣지 않았습니다: 로그인 항목 표시를 바로 없애는 명령이지만, IPinside뿐 아니라 모든 앱의 백그라운드 항목 승인을 초기화합니다. 다른 앱이 다시 허용을 요청하게 되므로, 재로그인으로 해결되는 표시 하나 때문에 쓸 이유가 없습니다.
덧붙임
- 하나카드처럼 IPinside를 요구하는 사이트에 다시 들어가면 재설치하라고 나올 수 있습니다. 필요할 때 깔고, 쓰고, 이 스크립트로 지우는 흐름으로 쓰면 됩니다.
- 다른 국내 보안 프로그램도 같은 방식으로 확인할 수 있습니다. 순서는
pkgutil --pkgs로 영수증 확인 →/Library/LaunchDaemons·LaunchAgents확인 → 키체인 루트 인증서 확인입니다. 마지막 단계가 가장 자주 빠집니다. - 분석과 스크립트 작성은 Claude Code에게 맡겼습니다. 설치 흔적을 추측하지 않고 영수증 → plist → 키체인 순서로 증거를 모은 다음, 사람이 비밀번호만 넣게 한 분업이 꽤 괜찮았습니다. 여러 GitHub 계정과 Organization을 오가며 작업하는 환경은 폴더별 GitHub 계정 분리 글에, Claude Code 사용량 관리는 Claude Code·Codex·Gemini 사용량 대시보드 글에 정리해 두었습니다.