서비스 · 프로세스 관리 14 / 50 · Part 2. 시그널·자원·세션
실습 환경: Rocky Linux 9.8 · Ubuntu 24.04.5 (systemd로 부팅한 Docker 격리 컨테이너, 테스트 계정 analyst)

1. 들어가며

점검 스크립트나 백업 스크립트는 임시 파일과 락 파일을 만든다. 스크립트가 끝까지 잘 돌면 마지막 줄에서 지우면 되지만, 누군가 Ctrl+C를 누르거나 systemctl stop으로 종료하면 정리 코드가 실행되지 않고 파일이 남는다. 남은 임시 파일에는 수집한 계정 목록이나 설정 값 같은 민감한 정보가 들어 있을 수도 있다.

이번 글에서는 셸 스크립트의 시그널 핸들러인 trap으로 어떤 경로로 끝나든 정리되는 스크립트 를 만들고, trap이 동작하지 않는 예외를 확인한다.


2. 핵심 개념

2-1. trap 문법

형태의미
trap '명령' SIGNAL...시그널 도착 시 명령 실행 (핸들러 등록)
trap '' SIGNAL시그널 무시 (SIG_IGN)
trap - SIGNAL기본 동작으로 복원
trap -p현재 설정 출력
trap -l시그널 목록

2-2. bash 전용 의사 시그널

이름실행 시점용도
EXIT셸이 종료될 때 (정상·exit·시그널 종료 모두)정리 작업의 핵심
ERR명령이 0이 아닌 종료 코드를 낼 때실패 위치 기록
DEBUG각 명령 실행 전디버깅·감사
RETURN함수·source 종료 시—

2-3. trap이 동작하지 않는 경우

경우이유
SIGKILL, SIGSTOP커널이 핸들러 등록을 허용하지 않는다
시작할 때 이미 무시된 시그널비대화형 bash는 진입 시 무시된 시그널을 trap하거나 복원할 수 없다
전원 차단·커널 패닉프로세스가 실행될 기회가 없다
포그라운드 명령 실행 중bash는 실행 중인 명령이 끝난 뒤에 trap을 실행한다. 긴 명령은 cmd & wait $! 패턴으로

3. 동작 원리

trap 으로 만드는 "반드시 정리되는" 스크립트

job.sh 시작
  TMP 생성 → trap 등록 (EXIT, INT/TERM, HUP 무시)
  sleep 60 & wait $!           ← wait 는 시그널이 오면 즉시 반환된다

kill -TERM 1005
  → wait 중단 → TERM 트랩: "[trap] INT/TERM 수신"; exit 130
  → exit 가 EXIT 트랩 호출 → cleanup: rm -f $TMP
  → 종료 코드 130

sleep 60을 포그라운드로 실행하면 bash는 sleep이 끝날 때까지(최대 60초) TERM 트랩을 미룬다. & wait $! 패턴 을 쓰면 wait가 시그널에 즉시 반응해 트랩이 바로 실행된다. systemd가 스크립트 서비스를 90초 기다렸다가 KILL하는 흔한 원인이 바로 이것이다.


4. 명령어 실습

# 1) 정리 트랩이 있는 스크립트
cat > ~/job.sh <<'EOF'
#!/bin/bash
TMP=$(mktemp /tmp/job.XXXXXX)
cleanup() { rm -f "$TMP"; echo "[trap] 임시파일 $TMP 삭제 (신호: $1)"; }
trap 'cleanup EXIT' EXIT
trap 'echo "[trap] INT/TERM 수신"; exit 130' INT TERM
trap '' HUP
echo "PID=$$ TMP=$TMP"; trap -p
sleep 60 & wait $!
EOF
chmod +x ~/job.sh
~/job.sh & sleep 0.5; J=$!
kill -HUP $J; ps -o pid,stat,cmd -p $J       # 무시되어 계속 실행
kill -TERM $J; wait $J; echo "종료 코드=$?"; ls /tmp/job.*

# 2) (Ubuntu) trap 이 없으면 임시 파일이 남는다
bash -c 'T=$(mktemp); echo "TMP=$T"; sleep 30' & sleep 0.5; kill $!
ls -l /tmp/tmp.* | tail -2

# 3) ERR 트랩으로 실패 위치 기록
bash -c 'set -E; trap '\''echo "[ERR] 줄 $LINENO 실패: $BASH_COMMAND (코드 $?)"'\'' ERR; ls /nonexistent; echo "계속 진행"'

5. 실행 결과

실제 실행 결과 — Rocky Linux 9.8 · analyst@rocky9-lab — trap 으로 종료 시 정리 작업 보장

실제 실행 결과 — Ubuntu 24.04.5 · analyst@ubuntu-lab — trap 이 없을 때와 ERR 트랩

텍스트 원본(실제 출력):

[analyst@rocky9-lab ~]$ cat > ~/job.sh <<'EOF'
> #!/bin/bash
> TMP=$(mktemp /tmp/job.XXXXXX)
> cleanup() { rm -f "$TMP"; echo "[trap] 임시파일 $TMP 삭제 (신호: $1)"; }
> trap 'cleanup EXIT' EXIT
> trap 'echo "[trap] INT/TERM 수신"; exit 130' INT TERM
> trap '' HUP
> echo "PID=$$ TMP=$TMP"; trap -p
> sleep 60 & wait $!
> EOF
[analyst@rocky9-lab ~]$ chmod +x ~/job.sh
[analyst@rocky9-lab ~]$ ~/job.sh & sleep 0.5; J=$!
PID=1005 TMP=/tmp/job.jEtypS
trap -- 'cleanup EXIT' EXIT
trap -- '' SIGHUP
trap -- '' SIGINT
trap -- '' SIGQUIT
trap -- 'echo "[trap] INT/TERM 수신"; exit 130' SIGTERM
[analyst@rocky9-lab ~]$ kill -HUP $J; sleep 0.3; ps -o pid,stat,cmd -p $J
    PID STAT CMD
   1005 S+   /bin/bash /home/analyst/job.sh
[analyst@rocky9-lab ~]$ kill -TERM $J; wait $J; echo "종료 코드=$?"; ls /tmp/job.* 2>&1
[trap] INT/TERM 수신
[trap] 임시파일 /tmp/job.jEtypS 삭제 (신호: EXIT)
종료 코드=130
ls: cannot access '/tmp/job.*': No such file or directory
analyst@ubuntu-lab:~$ bash -c 'T=$(mktemp); echo "TMP=$T"; sleep 30' & sleep 0.5; kill $!; wait $! 2>/dev/null; ls -l /tmp/tmp.* | tail -2
TMP=/tmp/tmp.HgfdCpDfLx
-rw------- 1 analyst analyst 0 Sep 24 11:50 /tmp/tmp.HgfdCpDfLx
analyst@ubuntu-lab:~$ bash -c 'set -E; trap '\''echo "[ERR] 줄 $LINENO 실패: $BASH_COMMAND (코드 $?)"'\'' ERR; ls /nonexistent; echo "계속 진행"'
ls: cannot access '/nonexistent': No such file or directory
[ERR] 줄 1 실패: ls /nonexistent (코드 2)
계속 진행

6. 결과 해석

관찰의미
trap -p에 EXIT, HUP(''), TERM 트랩 표시등록한 트랩이 적용되었다
trap -- '' SIGINT, trap -- '' SIGQUIT코드에서 INT 트랩을 걸었는데 무시('')로 표시 된다. 백그라운드로 실행된 비대화형 스크립트는 INT·QUIT이 시작부터 무시 상태라 trap이 적용되지 않았다
HUP 후에도 S+ 유지trap '' HUP으로 무시되었다
TERM → "[trap] INT/TERM 수신" → "[trap] 임시파일 ... 삭제"TERM 트랩이 exit 130을 호출했고, exit가 EXIT 트랩을 실행해 정리했다
종료 코드 130TERM 트랩에서 지정한 값. EXIT 트랩은 종료 코드를 바꾸지 않는다
ls /tmp/job.* → 없음임시 파일이 확실히 정리되었다
Ubuntu: trap 없는 스크립트를 kill → /tmp/tmp.HgfdCpDfLx 남음정리 코드가 없으면 시그널 종료 시 파일이 그대로 남는다
[ERR] 줄 1 실패: ls /nonexistent (코드 2) 후 "계속 진행"ERR 트랩은 실패를 기록 할 뿐 스크립트를 멈추지 않는다. 멈추려면 set -e 또는 트랩 안에서 exit

실무 스크립트를 Ctrl+C로 멈출 수 있어야 한다면 포그라운드로 실행 하거나, 스크립트 첫 부분에서 INT를 명시적으로 다루는 대신 TERM 중심으로 종료를 설계한다. systemd·cron은 종료 시 TERM을 보낸다.


7. 보안 관점

주제내용
민감한 임시 파일점검 결과·비밀번호 해시·설정 사본이 /tmp에 남으면 다른 사용자가 읽을 수 있다. mktemp(권한 600) + EXIT 트랩으로 반드시 정리한다
예측 가능한 임시 파일명/tmp/backup.tmp처럼 고정된 이름은 공격자가 심볼릭 링크를 미리 만들어 다른 파일을 덮어쓰게 할 수 있다. 항상 mktemp 사용
트랩 악용공격 스크립트도 trap '' HUP INT TERM으로 종료 시그널을 무시해 버틴다. 이런 프로세스의 SigIgn에는 HUP·INT·TERM 비트가 모두 켜져 있다 (11편)
DEBUG 트랩trap '...' DEBUG는 실행되는 모든 명령을 가로챌 수 있다. 공유 프로필(/etc/profile.d/)에 심어진 DEBUG 트랩은 명령 탈취·키로깅 에 악용될 수 있다

8. 보안관제 관점

[Detection]  /etc/profile.d/ 에 새 파일, 내용에 "trap ... DEBUG"
     ↓
[확인]       grep -rn "trap" /etc/profile /etc/profile.d/ /etc/bash.bashrc ~/.bashrc 2>/dev/null
             stat /etc/profile.d/<file>        ← 생성·변경 시각
     ↓
[판단]       모든 로그인 셸에서 입력 명령을 외부로 보내는 코드인가?
     ↓
[Response]   파일 격리·삭제, 변경 시각 전후 로그인 기록 조사, 영향 계정 비밀번호 변경
점검 항목명령
셸 초기화 파일의 trapgrep -rn 'trap' /etc/profile* /etc/bash* /home/*/.bash*
TERM을 무시하는 프로세스/proc/*/status의 SigIgn 비트 14(SIGTERM)를 해석 — 11편 방식, 49편에서 스크립트로 자동화
남은 임시 파일find /tmp /var/tmp -user root -mmin +60 -type f

9. 실무에서 자주 발생하는 실수

실수결과예방
정리 코드를 스크립트 마지막 줄에만 둠중간 종료 시 실행 안 됨trap cleanup EXIT
긴 명령을 포그라운드로 실행트랩이 명령 종료까지 지연 → systemd 타임아웃cmd & wait $!
trap 문자열을 큰따옴표로 작성변수가 등록 시점 에 확장됨작은따옴표로 작성
INT/TERM 트랩에서 exit 누락트랩 실행 후 스크립트가 계속 진행트랩 끝에 exit N
백그라운드 실행 스크립트에 INT 트랩적용되지 않음 (실측)TERM 중심 설계, trap -p로 확인

10. 실습 체크리스트

[ ] trap EXIT 로 임시 파일을 정리하는 스크립트를 작성했다
[ ] trap '' HUP 으로 HUP 이 무시되는 것을 확인했다
[ ] TERM 트랩 → exit → EXIT 트랩 순서를 확인했다
[ ] trap -p 로 백그라운드 스크립트의 INT 가 무시 상태인 것을 확인했다
[ ] trap 이 없는 스크립트는 임시 파일이 남는 것을 확인했다
[ ] ERR 트랩으로 실패 줄과 명령을 기록했다

11. 핵심 정리

  • trap '명령' SIGNAL은 셸 스크립트의 시그널 핸들러다. ''는 무시, -는 복원이다.
  • EXIT 트랩 은 정상 종료·exit·시그널 종료 모두에서 실행되므로 정리 작업에 쓴다.
  • SIGKILL과 시작 시 이미 무시된 시그널은 trap할 수 없다.
  • 긴 명령은 & wait $! 패턴으로 실행해야 트랩이 즉시 동작한다.
  • 셸 초기화 파일의 DEBUG 트랩은 명령 탈취에 악용될 수 있어 점검 대상이다.

12. 다음 편 예고

다음 글 「15. 프로세스와 파일 디스크립터 (lsof)」 에서는 프로세스가 연 파일을 확인하는 lsof를 다룬다. 특히 삭제했는데 디스크 용량이 줄지 않는 장애를 재현하고, 삭제된 파일을 /proc/PID/fd로 찾아 비우거나 복구하는 방법을 실습한다.


참고 자료


시리즈 이동

profile
코드에 숨겨진 위협을 읽고 AI로 보안의 미래를 설계합니다. 프론트엔드 개발 경험을 자산 삼아 더 견고하고 지능적인 보안 운영 시스템을 구축해 나가는 과정을 기록합니다

0개의 댓글