서비스 · 프로세스 관리 32 / 50 · Part 4. 로그·스케줄링·운영
실습 환경: Rocky Linux 9.8 · Ubuntu 24.04.5 (systemd로 부팅한 Docker 격리 컨테이너, 테스트 계정 analyst)

1. 들어가며

침해 사고 조사에서 가장 허무한 상황은 "로그가 없다"는 것이다. 공격자가 지웠을 수도 있지만, 의외로 흔한 원인은 처음부터 로그가 디스크에 저장되지 않았던 경우 다. journald는 설정과 디렉터리 유무에 따라 로그를 메모리(/run)에만 두기도 하는데, 이 경우 재부팅 한 번에 모든 기록이 사라진다.

이번 글에서는 실습 서버의 journal이 실제로 어디에 저장되고 있었는지 확인하고, 영구 저장·용량 제한·보존 기간을 drop-in으로 설정한다. 그리고 무결성 검증과 원격 전송까지, 관제에 필요한 로그 보존 기준을 정리한다.


2. 핵심 개념

2-1. journald.conf 주요 설정 ([Journal])

설정의미기본값
Storage=auto / persistent / volatile / noneauto
SystemMaxUse=영구 저장 최대 용량파일 시스템의 10% (최대 4G)
SystemKeepFree=남겨 둘 여유 공간15%
RuntimeMaxUse=휘발 저장 최대 용량/run의 10%
MaxRetentionSec=최대 보존 기간0 (무제한, 용량으로만 제한)
MaxFileSec=파일 하나를 쓰는 최대 기간 (로테이션)1month
Compress=큰 항목 압축yes
Seal=FSS 봉인 (키 설정 시)yes
ForwardToSyslog=syslog 데몬으로 전달no (배포판마다 다름)
RateLimitIntervalSec= / RateLimitBurst=서비스별 로그 폭주 제한30s / 10000

설정은 /etc/systemd/journald.conf를 직접 고치지 말고 /etc/systemd/journald.conf.d/*.conf drop-in 으로 추가한다(28편과 같은 원칙).

2-2. 관리 명령

명령용도
journalctl --disk-usage사용 용량
journalctl --list-boots부팅별 기록 범위
journalctl --flush/run의 로그를 /var로 옮김
journalctl --rotate현재 파일을 닫고 새 파일 시작
journalctl --vacuum-size= / --vacuum-time=오래된 보관 파일 정리
journalctl --verify파일 무결성(구조·봉인) 검사
journalctl --setup-keysFSS 봉인 키 생성

3. 동작 원리

journal 은 어디에 저장되고 언제 사라지나

부팅 초기 : journald 는 항상 /run/log/journal 에 먼저 기록 (디스크가 아직 준비 전)
          ↓ systemd-journal-flush.service (Storage 가 persistent 또는 auto+디렉터리 존재)
정상 운영 : /var/log/journal 로 옮기고 계속 기록
          ↓ SystemMaxUse / MaxRetentionSec 도달
정리      : 가장 오래된 "보관(archived)" 파일부터 삭제

Storage=auto인데 /var/log/journal 디렉터리가 없으면 flush가 일어나지 않고 계속 /run에만 기록된다. 이것이 이번 실습의 첫 번째 발견이다.


4. 명령어 실습

# 1) 현재 저장 위치 확인
grep -E '^#?(Storage|SystemMaxUse|MaxRetentionSec|ForwardToSyslog|Compress|Seal)=' /etc/systemd/journald.conf
ls -ld /run/log/journal /var/log/journal
journalctl --disk-usage
journalctl --header --no-pager | grep -E 'File path|State' | head -4

# 2) drop-in 으로 영구 저장 · 200MB · 90일
mkdir -p /etc/systemd/journald.conf.d
printf '[Journal]\nStorage=persistent\nSystemMaxUse=200M\nMaxRetentionSec=90day\nCompress=yes\n' \
  > /etc/systemd/journald.conf.d/60-soc.conf
systemctl restart systemd-journald; journalctl --flush
ls -ld /var/log/journal; ls /var/log/journal/*/
journalctl --disk-usage
journalctl -u systemd-journald -n 3 --no-pager -o cat

# 3) 보존·무결성 관리
journalctl --list-boots --no-pager
journalctl --verify 2>&1 | tail -2
journalctl --rotate; journalctl --vacuum-size=150M | tail -1

# 4) (Ubuntu) 기본값 비교
ls -ld /var/log/journal; journalctl --disk-usage
systemctl is-active rsyslog; ls /var/log/*.log | head -3

5. 실행 결과

실제 실행 결과 — Rocky Linux 9.8 · root@rocky9-lab — 지금 로그는 어디에 저장되나

실제 실행 결과 — Rocky Linux 9.8 · root@rocky9-lab — 영구 저장 · 용량 제한 설정 (drop-in)

실제 실행 결과 — Rocky Linux 9.8 · root@rocky9-lab — 보존·무결성 관리

실제 실행 결과 — Ubuntu 24.04.5 · root@ubuntu-lab — Ubuntu 기본값 비교

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

[root@rocky9-lab ~]# grep -E '^#?(Storage|SystemMaxUse|MaxRetentionSec|ForwardToSyslog|Compress|Seal)=' /etc/systemd/journald.conf
#Storage=auto
#Compress=yes
#Seal=yes
#SystemMaxUse=
#MaxRetentionSec=
#ForwardToSyslog=no
[root@rocky9-lab ~]# ls -ld /run/log/journal /var/log/journal 2>&1
ls: cannot access '/var/log/journal': No such file or directory
drwxr-sr-x+ 3 root systemd-journal 60 Sep 24 11:48 /run/log/journal
[root@rocky9-lab ~]# journalctl --disk-usage
Archived and active journals take up 8.0M in the file system.
[root@rocky9-lab ~]# journalctl --header --no-pager | grep -E 'File path|State|Head timestamp|Tail timestamp' | head -4
File path: /run/log/journal/eefc7636505d4b099e14a89497222b58/system.journal
State: ONLINE
[root@rocky9-lab ~]# mkdir -p /etc/systemd/journald.conf.d
[root@rocky9-lab ~]# printf '[Journal]\nStorage=persistent\nSystemMaxUse=200M\nMaxRetentionSec=90day\nCompress=yes\n' > /etc/systemd/journald.conf.d/60-soc.conf; cat /etc/systemd/journald.conf.d/60-soc.conf
[Journal]
Storage=persistent
SystemMaxUse=200M
MaxRetentionSec=90day
Compress=yes
[root@rocky9-lab ~]# systemctl restart systemd-journald; sleep 1; journalctl --flush
[root@rocky9-lab ~]# ls -ld /var/log/journal; ls /var/log/journal/*/ | head -3
drwxr-xr-x 3 root root 4096 Sep 24 12:22 /var/log/journal
system.journal
[root@rocky9-lab ~]# journalctl --disk-usage
Archived and active journals take up 8.0M in the file system.
[root@rocky9-lab ~]# journalctl -u systemd-journald -n 3 --no-pager -o cat
Time spent on flushing to /var/log/journal/eefc7636505d4b099e14a89497222b58 is 28.356ms for 2493 entries.
System Journal (/var/log/journal/eefc7636505d4b099e14a89497222b58) is 8.0M, max 200.0M, 192.0M free.
Received client request to flush runtime journal.
[root@rocky9-lab ~]# journalctl --list-boots --no-pager
IDX BOOT ID                          FIRST ENTRY                 LAST ENTRY
  0 81e17f30461a4006b5b348a7a8553948 Thu 2026-09-24 11:48:25 UTC Thu 2026-09-24 12:22:44 UTC
[root@rocky9-lab ~]# journalctl --verify 2>&1 | tail -2
PASS: /var/log/journal/eefc7636505d4b099e14a89497222b58/system.journal
[root@rocky9-lab ~]# journalctl --rotate; journalctl --vacuum-size=150M | tail -1
Vacuuming done, freed 0B of archived journals from /var/log/journal/eefc7636505d4b099e14a89497222b58.
Vacuuming done, freed 0B of archived journals from /run/log/journal.
Vacuuming done, freed 0B of archived journals from /var/log/journal.
root@ubuntu-lab:~# ls -ld /var/log/journal; journalctl --disk-usage
drwxr-sr-x+ 1 root systemd-journal 4096 Sep 24 09:37 /var/log/journal
Archived and active journals take up 31.5M in the file system.
root@ubuntu-lab:~# systemctl is-active rsyslog 2>&1; ls /var/log/*.log 2>/dev/null | head -3
inactive
/var/log/alternatives.log
/var/log/bootstrap.log
/var/log/dpkg.log

6. 결과 해석

관찰의미
journald.conf의 모든 값이 # 주석설정 파일을 건드리지 않은 상태 = 컴파일 기본값 으로 동작 중
/var/log/journal: No such file or directoryStorage=auto인데 디렉터리가 없다 → 휘발 저장
File path: /run/log/journal/.../system.journal실제로 메모리(tmpfs)에 기록 중이었다. 이 상태로 재부팅하면 지금까지의 모든 실습 로그가 사라진다
drop-in 적용 후 /var/log/journal 생성, system.journal 존재persistent 설정이 디렉터리를 만들고 영구 저장을 시작했다
Time spent on flushing to /var/log/journal/... 2493 entries/run에 있던 기존 로그 2,493건을 디스크로 옮겼다
System Journal (...) is 8.0M, max 200.0M, 192.0M freeSystemMaxUse=200M이 적용되었다
--list-boots에 부팅 1개이 컨테이너는 한 번만 부팅되었다. 운영 서버에서는 여러 줄이 보이며, 재부팅 시각과 기록 공백 을 확인하는 데 쓴다
--verify → PASS파일 구조가 정상이다. 봉인 키가 있으면 변조 여부까지 검사한다
--vacuum-size=150M → freed 0Bvacuum은 보관(archived) 파일만 지운다. 현재 사용 중인 파일은 지우지 않는다
Ubuntu: /var/log/journal 이미 존재, 31.5MUbuntu 24.04는 패키지가 디렉터리를 만들어 기본이 영구 저장 이다
Ubuntu rsyslog inactive, /var/log/*.log는 설치 로그 뿐컨테이너 이미지에는 rsyslog가 없다. 일반 Ubuntu 서버는 rsyslog가 /var/log/auth.log, syslog를 함께 남긴다

같은 Rocky 9라도 설치 방식(최소 설치, 클라우드 이미지, 컨테이너)에 따라 /var/log/journal 유무가 다를 수 있다. "기본값이니 괜찮겠지"가 아니라 반드시 확인 해야 하는 이유다.


7. 보안 관점

주제내용
휘발 저장재부팅(또는 공격자의 의도적 재부팅)만으로 흔적이 사라진다. 모든 서버는 persistent를 기준으로 한다
로그 삭제root 권한 공격자는 journalctl --vacuum-time=1s, 파일 삭제로 기록을 지울 수 있다. 서버 밖에 사본 이 있어야 한다
무결성journalctl --setup-keys로 FSS(Forward Secure Sealing)를 켜면 이후 변조를 --verify로 탐지할 수 있다. 검증 키는 서버 밖에 보관한다
로그 폭주공격자가 로그를 대량으로 발생시켜 중요 로그를 밀어내거나 용량을 채우는 공격이 가능하다. RateLimit과 용량 경보를 함께 운영한다
접근 권한/var/log/journal은 systemd-journal 그룹 읽기 권한을 가진다. 이 그룹 구성원은 모든 로그(인증 포함)를 읽을 수 있다

8. 보안관제 관점

[서버 온보딩 점검 — 관제 대상 등록 시]
  ① ls -ld /var/log/journal ; journalctl --header | grep 'File path'   → 영구 저장인가
  ② systemd-analyze cat-config systemd/journald.conf | grep -v '^#'    → 최종 설정
  ③ journalctl --disk-usage ; df -h /var                                → 용량 여유
  ④ 원격 전송 에이전트 동작 확인 (rsyslog / filebeat / fluent-bit)     → SIEM 수신 확인
  ⑤ journalctl --list-boots                                             → 기록 공백 구간
     ↓
[사고 조사 시]
  journalctl --list-boots 에 예상보다 부팅이 적다 / 첫 기록 시각이 최근이다
  → 로그 삭제·vacuum 의심 → SIEM 사본과 비교
경보 조건의미
SIEM에서 특정 서버 로그 수신 중단에이전트 중지·네트워크 차단·서버 장악 가능성
journalctl --verify 실패파일 손상 또는 변조
첫 기록 시각이 갑자기 최근으로 바뀜vacuum·삭제

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

실수결과예방
Storage=auto를 믿고 확인 안 함휘발 저장으로 재부팅 시 로그 소실/var/log/journal 확인, persistent 명시
journald.conf 원본 직접 수정패키지 업데이트 시 충돌journald.conf.d/ drop-in
용량 제한 없이 운영/var 가득 참 → 다른 서비스 장애SystemMaxUse, 보존 기간 설정
vacuum으로 현재 파일이 지워질 거라 기대보관 파일만 정리됨필요하면 --rotate 후 vacuum
서버 로컬 로그만 보관장악 시 증거 소실원격 수집 필수

10. 실습 체크리스트

[ ] journal 의 실제 저장 경로(/run 또는 /var)를 확인했다
[ ] Storage=auto 와 /var/log/journal 디렉터리의 관계를 설명할 수 있다
[ ] drop-in 으로 persistent · SystemMaxUse · MaxRetentionSec 를 설정했다
[ ] journalctl --flush 로 휘발 로그를 디스크로 옮겼다
[ ] --list-boots, --verify, --vacuum 을 실행했다
[ ] Rocky 와 Ubuntu 의 기본 저장 방식 차이를 확인했다

11. 핵심 정리

  • Storage=auto는 /var/log/journal이 있어야 영구 저장된다. 없으면 재부팅 시 로그가 사라진다.
  • 설정은 journald.conf.d/ drop-in으로 persistent·용량·보존 기간을 명시한다.
  • vacuum은 보관 파일만 지우고, --verify로 무결성을 검사한다.
  • 로컬 로그는 root 공격자가 지울 수 있으므로 원격 수집 과 FSS 봉인이 필요하다.
  • 관제 대상 서버를 등록할 때 저장 위치·용량·전송 상태를 점검한다.

12. 다음 편 예고

다음 글 「33. cron 구조와 crontab 문법」 에서는 가장 오래된 작업 스케줄러 cron을 다룬다. crontab 다섯 필드 문법과 % 이스케이프, cron 작업의 PATH가 셸과 다른 이유, Rocky와 Ubuntu의 crontab 저장 위치·로그 차이를 실제 실행으로 확인한다.


참고 자료


시리즈 이동

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

0개의 댓글