리눅스 시스템 기초 · 입문편 — 본문의 "N편"은 입문 과정 번호다. 번호별 글과 전체 250편 구성은 통합 로드맵에서 확인할 수 있다.

리눅스 시스템 기초 36 / 50 · Part 4. 프로세스·서비스·리소스
실습 환경: Rocky Linux 9 (10.0.0.200) / Ubuntu 22.04 (10.0.0.210)
이전 글: 35. Parent / Child Process

1. 들어가며

8편에서 부팅 과정을 다루며 커널이 첫 번째 사용자 프로세스로 systemd(PID 1) 를 실행한다고 했다. 35편에서는 모든 프로세스 트리의 뿌리가 systemd라는 것을 확인했다.

이번 글은 systemd가 무엇을 어떤 구조로 관리하는지 를 정리한다. 관제 관점에서 systemd가 중요한 이유는 두 가지다.

  1. 서버의 거의 모든 서비스(sshd, httpd, auditd …)가 systemd를 통해 시작·재시작된다. 서비스 장애와 이상 종료의 기록이 여기에 남는다.
  2. 공격자는 재부팅 후에도 살아남기 위해 systemd 서비스나 타이머를 몰래 등록 한다(MITRE ATT&CK T1543.002). 어디를 봐야 하는지 알아야 찾을 수 있다.

명령어 사용법(systemctl)은 37편에서 다루고, 이번 글은 구조 에 집중한다.


2. 핵심 개념

2-1. Unit

systemd가 관리하는 모든 대상을 Unit 이라 부른다. 확장자가 종류를 나타낸다.

Unit 종류관리 대상예
.service데몬, 일회성 작업sshd.service
.timer예약 실행 (cron 대체, 38편)logrotate.timer
.target여러 Unit의 묶음 (옛 runlevel)multi-user.target
.socket소켓에 연결이 오면 서비스 기동sshd.socket
.mount마운트 지점tmp.mount
.path파일·디렉터리 변화 감시 후 기동cups.path

2-2. Unit 파일 위치와 우선순위

같은 이름의 Unit이 여러 곳에 있으면 위쪽이 이긴다.

우선순위경로용도
1/etc/systemd/system/관리자 설정, 드롭인(X.service.d/*.conf)
2/run/systemd/system/런타임 생성, 재부팅 시 사라짐
3/usr/lib/systemd/system/ (RHEL) · /lib/systemd/system/ (Ubuntu)패키지 원본
사용자~/.config/systemd/user/, /etc/systemd/user/사용자 단위 서비스

패키지 원본(3)을 직접 수정하지 말고 /etc 쪽에 덮어쓰는 것이 원칙이다. 공격자도 같은 원리로 /etc/systemd/system/에 새 파일이나 드롭인을 만든다.

2-3. Unit 파일 구조

[Unit]
Description=OpenSSH server daemon
After=network.target            # 순서: network.target 다음에 시작
Wants=sshd-keygen.target        # 약한 의존: 함께 시작 시도

[Service]
Type=notify
ExecStart=/usr/sbin/sshd -D $OPTIONS    # 실제 실행 명령 ← 가장 중요
ExecReload=/bin/kill -HUP $MAINPID      # reload = SIGHUP (34편)
Restart=on-failure                      # 비정상 종료 시 자동 재시작
User=                                   # 비어 있으면 root

[Install]
WantedBy=multi-user.target      # enable 하면 이 target에 연결
지시어의미
After= / Before=순서 만 정함
Wants= / Requires=의존 — 함께 시작. Requires는 실패하면 같이 실패
ExecStart=실행할 명령 (보안 점검 핵심)
Restart=죽으면 다시 살림 — 악성 서비스가 kill 되어도 되살아나는 이유
User=실행 계정. 없으면 root
WantedBy=enable 시 어느 target에 연결할지

3. 동작 원리

systemd 구조 — Unit 파일 위치, 부팅 순서, 지속성 지점

① enable의 실체는 심볼릭 링크다. systemctl enable sshd를 실행하면 [Install]의 WantedBy=multi-user.target을 보고 다음 링크를 만든다.

/etc/systemd/system/multi-user.target.wants/sshd.service
    -> /usr/lib/systemd/system/sshd.service

부팅 시 systemd는 multi-user.target을 목표로 삼고, .wants/ 디렉터리 안의 링크를 따라 서비스를 시작한다. 그래서 *.wants/ 디렉터리를 보면 부팅 시 자동 실행되는 목록 을 알 수 있다.

② 의존성 그래프로 병렬 시작한다. 예전 SysV init은 스크립트를 번호 순서대로 하나씩 실행했지만, systemd는 After·Requires 관계만 지키면서 가능한 한 동시에 시작한다. 부팅이 빠른 이유다.

③ cgroup으로 서비스를 묶는다. 서비스가 시작되면 systemd는 그 프로세스와 모든 자손을 /sys/fs/cgroup/system.slice/<이름>.service/에 넣는다. 그래서:

  • systemctl stop은 해당 cgroup의 모든 프로세스 를 종료할 수 있다(서비스가 fork한 자식까지).
  • 35편에서 본 것처럼 이중 fork로 부모를 끊어도 cgroup 소속은 남는다.
  • 서비스별 CPU·메모리 제한도 cgroup으로 건다.

④ 로그는 journald로 모인다. 서비스의 표준 출력·오류는 systemd-journald가 받아 저장한다(48편 journalctl).


4. 실습

# 1) PID 1 확인
ps -p 1 -o pid,comm,args

# 2) Unit 목록
systemctl list-units --type=service --state=running
systemctl list-unit-files --type=service --state=enabled

# 3) Unit 파일 내용과 실제 경로 (드롭인 포함)
systemctl cat sshd
systemctl show sshd -p FragmentPath,DropInPaths,ExecStart,User

# 4) 부팅 시 자동 실행 링크
ls -l /etc/systemd/system/multi-user.target.wants/

# 5) 의존성
systemctl list-dependencies multi-user.target | head -30

# 6) 부팅 시간 분석 (8편)
systemd-analyze blame | head

# 7) 프로세스가 속한 서비스
systemctl status <PID>
cat /proc/<PID>/cgroup
systemd-cgls --no-pager | head -40

# 8) 사용자 유닛과 linger
ls -la ~/.config/systemd/user/ 2>/dev/null
loginctl show-user <user> -p Linger

5. 결과 분석

아래 출력은 형식 설명용 예시다.

① systemctl show dbus-helper -p FragmentPath,ExecStart,User

FragmentPath=/etc/systemd/system/dbus-helper.service
ExecStart={ path=/var/tmp/.cache/dbus-helper ; argv[]=/var/tmp/.cache/dbus-helper ; ... }
User=
단서해석
FragmentPath가 /etc/systemd/system/패키지가 아니라 누군가 직접 만든 Unit
이름 dbus-helper정상 dbus 서비스처럼 보이게 한 위장
ExecStart가 /var/tmp/.cache/숨김 디렉터리의 실행 파일 (22편)
User= 비어 있음root 권한 실행

② 패키지 소속 확인

rpm -qf /etc/systemd/system/dbus-helper.service   # RHEL
dpkg -S /etc/systemd/system/dbus-helper.service   # Ubuntu
file /etc/systemd/system/dbus-helper.service is not owned by any package

어떤 패키지에도 속하지 않는 서비스 파일이다. 관리자가 직접 만든 정상 서비스일 수도 있으므로 변경 관리 기록과 대조한다.


6. 보안 관점

지속성 기법위치점검
새 서비스 등록 (T1543.002)/etc/systemd/system/*.service + .wants/ 링크패키지 소속, ExecStart 경로
기존 서비스 드롭인 변조/etc/systemd/system/sshd.service.d/override.confsystemctl cat으로 드롭인 표시 확인
타이머 (T1053.006)*.timer + 같은 이름의 .servicesystemctl list-timers --all (38편)
사용자 유닛~/.config/systemd/user/ + lingerroot 권한 없이 가능
원본 Unit 변조/usr/lib/systemd/system/rpm -V systemd, rpm -Va \| grep systemd
보안 서비스 비활성화systemctl disable --now auditd / maskenable 상태 변화 감시 (37편)

Restart=always가 설정된 악성 서비스는 프로세스를 kill 해도 systemd가 몇 초 뒤 다시 살린다. 반드시 systemctl stop → disable → Unit 파일 격리 순서로 처리해야 한다.


7. SOC / 보안관제 활용

7-1. 비패키지 Unit 헌팅

# RHEL: 어떤 패키지에도 속하지 않는 unit 파일
find /etc/systemd/system /usr/lib/systemd/system -name '*.service' -o -name '*.timer' |
  while read -r f; do rpm -qf "$f" >/dev/null 2>&1 || echo "NOPKG $f"; done

# 최근 7일 내 변경된 unit·드롭인 (22편)
find /etc/systemd /usr/lib/systemd /run/systemd ~/.config/systemd -newermt '-7 days' -type f 2>/dev/null

# ExecStart 가 임시 경로를 가리키는 서비스
grep -rEl 'ExecStart=.*(/tmp|/var/tmp|/dev/shm|/\.)' /etc/systemd /usr/lib/systemd 2>/dev/null

7-2. 감사 규칙

# /etc/audit/rules.d/systemd.rules
-w /etc/systemd/system/ -p wa -k systemd_persist
-w /usr/lib/systemd/system/ -p wa -k systemd_persist

7-3. 분석 흐름

[Alert]   auditd: /etc/systemd/system/dbus-helper.service 생성 (key=systemd_persist)
   ↓
[확인]    systemctl cat → ExecStart=/var/tmp/.cache/dbus-helper, User 없음(root)
   ↓
[판단]    패키지 미소속 + 숨김 경로 + 위장 이름 → 지속성 설치
   ↓
[범위]    생성 시각 전후 secure 로그의 sudo/su 기록 → root 획득 경로 (21·49편)
   ↓
[Response] stop → disable → unit·실행 파일 격리(해시 보관) → daemon-reload → 다른 서버 동일 파일 탐색

8. 핵심 정리

  • systemd는 PID 1로서 모든 서비스를 Unit 단위로 관리한다. .service, .timer, .target, .socket …
  • Unit 파일 우선순위: /etc/systemd/system > /run > /usr/lib(Ubuntu /lib). 사용자 유닛은 ~/.config/systemd/user.
  • 보안 점검의 핵심 지시어는 ExecStart, User, Restart, WantedBy 다.
  • enable은 *.target.wants/에 심볼릭 링크를 만드는 것이다.
  • systemd는 서비스를 cgroup 으로 묶어 자손까지 관리하고, 출력은 journald로 보낸다.
  • 지속성 헌팅: 패키지 미소속 Unit, 최근 변경, 임시 경로 ExecStart, 드롭인, 타이머, 사용자 유닛.

9. 다음 글

다음 글 「37. systemctl 서비스 관리」 에서는 이번 구조를 조작하는 도구 systemctl을 다룬다. start·stop·restart·reload의 차이, enable과 mask, status 출력(Active·Main PID·exit code)을 읽는 방법, 그리고 서비스 이상 종료를 관제에서 판단하는 방법을 정리한다.


참고 자료

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

0개의 댓글