
서비스 · 프로세스 관리 32 / 50 · Part 4. 로그·스케줄링·운영
실습 환경: Rocky Linux 9.8 · Ubuntu 24.04.5 (systemd로 부팅한 Docker 격리 컨테이너, 테스트 계정analyst)
침해 사고 조사에서 가장 허무한 상황은 "로그가 없다"는 것이다. 공격자가 지웠을 수도 있지만, 의외로 흔한 원인은 처음부터 로그가 디스크에 저장되지 않았던 경우 다. journald는 설정과 디렉터리 유무에 따라 로그를 메모리(/run)에만 두기도 하는데, 이 경우 재부팅 한 번에 모든 기록이 사라진다.
이번 글에서는 실습 서버의 journal이 실제로 어디에 저장되고 있었는지 확인하고, 영구 저장·용량 제한·보존 기간을 drop-in으로 설정한다. 그리고 무결성 검증과 원격 전송까지, 관제에 필요한 로그 보존 기준을 정리한다.
| 설정 | 의미 | 기본값 |
|---|---|---|
Storage= | auto / persistent / volatile / none | auto |
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편과 같은 원칙).
| 명령 | 용도 |
|---|---|
journalctl --disk-usage | 사용 용량 |
journalctl --list-boots | 부팅별 기록 범위 |
journalctl --flush | /run의 로그를 /var로 옮김 |
journalctl --rotate | 현재 파일을 닫고 새 파일 시작 |
journalctl --vacuum-size= / --vacuum-time= | 오래된 보관 파일 정리 |
journalctl --verify | 파일 무결성(구조·봉인) 검사 |
journalctl --setup-keys | FSS 봉인 키 생성 |

부팅 초기 : journald 는 항상 /run/log/journal 에 먼저 기록 (디스크가 아직 준비 전)
↓ systemd-journal-flush.service (Storage 가 persistent 또는 auto+디렉터리 존재)
정상 운영 : /var/log/journal 로 옮기고 계속 기록
↓ SystemMaxUse / MaxRetentionSec 도달
정리 : 가장 오래된 "보관(archived)" 파일부터 삭제
Storage=auto인데 /var/log/journal 디렉터리가 없으면 flush가 일어나지 않고 계속 /run에만 기록된다. 이것이 이번 실습의 첫 번째 발견이다.
# 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




텍스트 원본(실제 출력):
[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
| 관찰 | 의미 |
|---|---|
journald.conf의 모든 값이 # 주석 | 설정 파일을 건드리지 않은 상태 = 컴파일 기본값 으로 동작 중 |
/var/log/journal: No such file or directory | Storage=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 free | SystemMaxUse=200M이 적용되었다 |
--list-boots에 부팅 1개 | 이 컨테이너는 한 번만 부팅되었다. 운영 서버에서는 여러 줄이 보이며, 재부팅 시각과 기록 공백 을 확인하는 데 쓴다 |
--verify → PASS | 파일 구조가 정상이다. 봉인 키가 있으면 변조 여부까지 검사한다 |
--vacuum-size=150M → freed 0B | vacuum은 보관(archived) 파일만 지운다. 현재 사용 중인 파일은 지우지 않는다 |
Ubuntu: /var/log/journal 이미 존재, 31.5M | Ubuntu 24.04는 패키지가 디렉터리를 만들어 기본이 영구 저장 이다 |
Ubuntu rsyslog inactive, /var/log/*.log는 설치 로그 뿐 | 컨테이너 이미지에는 rsyslog가 없다. 일반 Ubuntu 서버는 rsyslog가 /var/log/auth.log, syslog를 함께 남긴다 |
같은 Rocky 9라도 설치 방식(최소 설치, 클라우드 이미지, 컨테이너)에 따라
/var/log/journal유무가 다를 수 있다. "기본값이니 괜찮겠지"가 아니라 반드시 확인 해야 하는 이유다.
| 주제 | 내용 |
|---|---|
| 휘발 저장 | 재부팅(또는 공격자의 의도적 재부팅)만으로 흔적이 사라진다. 모든 서버는 persistent를 기준으로 한다 |
| 로그 삭제 | root 권한 공격자는 journalctl --vacuum-time=1s, 파일 삭제로 기록을 지울 수 있다. 서버 밖에 사본 이 있어야 한다 |
| 무결성 | journalctl --setup-keys로 FSS(Forward Secure Sealing)를 켜면 이후 변조를 --verify로 탐지할 수 있다. 검증 키는 서버 밖에 보관한다 |
| 로그 폭주 | 공격자가 로그를 대량으로 발생시켜 중요 로그를 밀어내거나 용량을 채우는 공격이 가능하다. RateLimit과 용량 경보를 함께 운영한다 |
| 접근 권한 | /var/log/journal은 systemd-journal 그룹 읽기 권한을 가진다. 이 그룹 구성원은 모든 로그(인증 포함)를 읽을 수 있다 |
[서버 온보딩 점검 — 관제 대상 등록 시]
① 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·삭제 |
| 실수 | 결과 | 예방 |
|---|---|---|
| Storage=auto를 믿고 확인 안 함 | 휘발 저장으로 재부팅 시 로그 소실 | /var/log/journal 확인, persistent 명시 |
| journald.conf 원본 직접 수정 | 패키지 업데이트 시 충돌 | journald.conf.d/ drop-in |
| 용량 제한 없이 운영 | /var 가득 참 → 다른 서비스 장애 | SystemMaxUse, 보존 기간 설정 |
| vacuum으로 현재 파일이 지워질 거라 기대 | 보관 파일만 정리됨 | 필요하면 --rotate 후 vacuum |
| 서버 로컬 로그만 보관 | 장악 시 증거 소실 | 원격 수집 필수 |
[ ] journal 의 실제 저장 경로(/run 또는 /var)를 확인했다
[ ] Storage=auto 와 /var/log/journal 디렉터리의 관계를 설명할 수 있다
[ ] drop-in 으로 persistent · SystemMaxUse · MaxRetentionSec 를 설정했다
[ ] journalctl --flush 로 휘발 로그를 디스크로 옮겼다
[ ] --list-boots, --verify, --vacuum 을 실행했다
[ ] Rocky 와 Ubuntu 의 기본 저장 방식 차이를 확인했다
Storage=auto는 /var/log/journal이 있어야 영구 저장된다. 없으면 재부팅 시 로그가 사라진다.journald.conf.d/ drop-in으로 persistent·용량·보존 기간을 명시한다.--verify로 무결성을 검사한다.다음 글 「33. cron 구조와 crontab 문법」 에서는 가장 오래된 작업 스케줄러 cron을 다룬다. crontab 다섯 필드 문법과 % 이스케이프, cron 작업의 PATH가 셸과 다른 이유, Rocky와 Ubuntu의 crontab 저장 위치·로그 차이를 실제 실행으로 확인한다.