11. 누가 접속했고 무엇을 했나
감사는 사후에 만들 수 없다. 기본 설정으로 무엇이 남고 무엇이 안 남는지를 사고 나기 전에 알아 둬야 한다
이 장에서 처음 나오는 말5개
utmp / wtmp / btmp- 로그인 기록을 담는 바이너리 파일 셋. utmp = 지금 접속 중(
w·who), wtmp = 과거 로그인 이력(last), btmp = 실패한 로그인(lastb). 텍스트가 아니라 전용 명령으로만 읽는다. lastlog- 사용자별 마지막 로그인 시각만 기록하는 별도 파일. "이 계정 아직 쓰이나"를 판단할 때 쓴다.
auditdLinux Audit daemon- 커널 감사 서브시스템의 사용자 공간 데몬. 파일 접근·시스템 콜·명령 실행을 정책대로 기록한다. Ubuntu에 기본 설치돼 있지 않다 — 규제 대응이 필요하면 직접 깐다.
PAMPluggable Authentication Modules- 로그인·인증 절차를 모듈로 조립하는 리눅스의 인증 프레임워크. 세션 기록·접근 제한·2단계 인증이 여기 끼워진다.
/etc/pam.d/아래가 그 설정이다. 비대화형 세션non-interactive session- SSH로 명령만 실행하고 나가는 접속(
ssh host "명령") 등. 셸 히스토리에 안 남고 로그인 기록도 형태가 다르다 — 히스토리에만 의존하면 안 되는 이유다.
알고 싶은 것 → 명령
섹션 제목: “알고 싶은 것 → 명령”| 알고 싶은 것 | 명령 | 출처 |
|---|---|---|
| 지금 누가 접속해 있나 | w · who · loginctl | utmp / logind |
| 그 사람이 지금 뭘 하고 있나 | w 의 WHAT 열 · ps -u 사용자 -f | — |
| 과거에 누가 로그인했나 | last -F -a | wtmp |
| 로그인 실패 이력 | sudo lastb -F · auth.log | btmp |
| 계정별 마지막 로그인 | lastlog | lastlog |
| 재부팅 이력 | last reboot | wtmp |
| SSH 접속 상세(IP·키 지문·실패 이유) | journalctl -u ssh · /var/log/auth.log | journal / rsyslog |
| 누가 sudo로 무슨 명령을 썼나 | grep sudo /var/log/auth.log · journalctl _COMM=sudo | auth.log |
| 누가 패키지를 설치했나 | /var/log/apt/history.log | apt |
| 셸에서 친 명령 | ~/.bash_history (신뢰도 낮음 — 아래) | 셸 |
| 실행된 모든 명령 (제대로) | auditd (미리 설정해야 한다) | 커널 |
지금 접속 중인 사람
섹션 제목: “지금 접속 중인 사람”w # 사용자 · 접속 원격지 · 로그인 시각 · 유휴 시간 · 실행 중인 명령who -a # 더 단순한 목록loginctl list-sessions # systemd가 보는 세션 (utmp보다 정확하다)loginctl session-status 42 # 그 세션의 프로세스 트리까지sudo ss -tp state established '( sport = :22 )' # SSH 연결의 상대 IPpgrep -a -u deploy # 이 사용자가 돌리는 프로세스$ w 15:22:31 up 21 days, load average: 0.31, 0.22, 0.18USER TTY FROM LOGIN@ IDLE WHATsshim pts/0 10.20.5.77 14:58 0.00s wdeploy pts/1 10.20.5.91 13:02 1:42m tail -f /var/log/app.logIDLE이 길면 붙어만 있는 세션이다. 강제로 끊어야 하면
sudo loginctl terminate-session 42 (또는 그 세션의 셸 PID에 kill).
과거 로그인 이력 — last
섹션 제목: “과거 로그인 이력 — last”last # 최근부터last -F -a -n 30 # 전체 시각(-F) + 호스트를 끝 열에(-a), 30줄last deploy # 특정 사용자만last -s -7days -t today # 기간 지정last -x # 런레벨 변경·셧다운까지 포함last reboot # 재부팅 이력만 ← 장애 조사의 단골sudo lastb -F -n 20 # 실패한 로그인 (btmp — root 권한 필요)lastlog # 계정별 마지막 로그인 (한 번도 없으면 "Never logged in")lastlog -b 90 # 90일 이상 로그인 없는 계정 ← 계정 정리에 쓴다$ last -F -a -n 5sshim pts/0 Mon Aug 10 14:58:11 2026 still logged in 10.20.5.77deploy pts/1 Mon Aug 10 13:02:03 2026 - Mon Aug 10 15:20:41 2026 (02:18) 10.20.5.91reboot system boot Mon Jul 20 04:11:52 2026 still running 6.8.0-51-genericSSH 접속의 상세 — auth.log와 journal
섹션 제목: “SSH 접속의 상세 — auth.log와 journal”last는 “언제 누가”까지만 알려준다. 어떤 키로 붙었는지, 왜 실패했는지는 여기 있다.
sudo journalctl -u ssh --since "2026-08-01" | grep -E "Accepted|Failed|Invalid"sudo grep -E "Accepted|Failed password|Invalid user" /var/log/auth.logsudo zgrep "Accepted" /var/log/auth.log.*.gz # 로테이션된 옛 로그까지Accepted publickey for deploy from 10.20.5.91 port 51422 ssh2: ED25519 SHA256:kJ8…Failed password for invalid user admin from 203.0.113.9 port 40122 ssh2Disconnected from authenticating user root 203.0.113.9 port 40122 [preauth]SHA256:… 지문으로 어느 키가 쓰였는지 특정할 수 있다 —
공유 계정에서 “누가 들어왔나”를 가리는 유일한 단서인 경우가 많다.
내 키의 지문은 ssh-keygen -lf ~/.ssh/id_ed25519.pub로 확인한다.
집계해서 보면 공격 패턴이 드러난다.
sudo grep "Failed password" /var/log/auth.log \ | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head # 실패 IP 상위sudo grep "Accepted" /var/log/auth.log | awk '{print $9, $11}' | sort | uniq -c # 성공 계정·IP무슨 명령을 실행했나
섹션 제목: “무슨 명령을 실행했나”sudo 로그 기본 제공
섹션 제목: “sudo 로그 ”sudo로 실행한 명령은 기본으로 전부 기록된다. 실무에서 가장 쓸모 있는 감사 흔적이다.
sudo grep sudo /var/log/auth.log | grep COMMANDsudo journalctl _COMM=sudo --since "7 days ago"sshim : TTY=pts/0 ; PWD=/etc/nginx ; USER=root ; COMMAND=/usr/bin/systemctl restart nginx어느 디렉터리에서(PWD) 무슨 명령을(COMMAND) 실행했는지까지 남는다.
sudo -i나 sudo su -로 root 셸에 들어간 뒤의 명령은 기록되지 않는다 —
그래서 sudoers에서 NOPASSWD로 개별 명령만 허용하는 편이 감사 관점에서 낫다(10장).
셸 히스토리 신뢰도 낮음
섹션 제목: “셸 히스토리 ”cat ~/.bash_historysudo cat /home/deploy/.bash_historysudo cat /root/.bash_historyhistory # 현재 세션 (메모리 + 파일)최소한의 개선은 해 둘 만하다 — /etc/profile.d/history.sh
export HISTTIMEFORMAT="%F %T " # 시각 기록export HISTSIZE=10000export HISTFILESIZE=20000shopt -s histappend # 덮어쓰지 않고 이어붙인다export PROMPT_COMMAND="history -a" # 명령마다 즉시 파일에 기록제대로 하려면 — auditd
섹션 제목: “제대로 하려면 — auditd”-
설치 (Ubuntu 기본에는 없다)
터미널 창 sudo apt install -y auditd audispd-pluginssystemctl status auditd -
규칙을 건다 —
/etc/audit/rules.d/audit.rules# 중요 파일 변경 감시-w /etc/passwd -p wa -k identity-w /etc/sudoers -p wa -k sudoers-w /etc/ssh/sshd_config -p wa -k sshd# 특정 사용자의 명령 실행 전부 (auid=로그인 사용자 — su로 바꿔도 추적된다)-a always,exit -F arch=b64 -S execve -F auid=1001 -k user_cmd터미널 창 sudo augenrules --load && sudo auditctl -l -
조회
터미널 창 sudo ausearch -k sudoers -i # -i 로 UID를 이름으로 풀어 준다sudo ausearch -ua deploy --start today -isudo aureport --auth --summarysudo aureport -l -i # 로그인 리포트
대안으로 sudo 자체의 세션 녹화도 있다 — sudoers에
Defaults log_output 을 주면 sudoreplay로 화면 출력까지 재생할 수 있다.
조사 순서 — 무엇이 바뀌었나
섹션 제목: “조사 순서 — 무엇이 바뀌었나”- 언제 누가 들어왔나 —
last -F -a | head -20,sudo lastb -F | head - 어떻게 인증했나 —
sudo grep Accepted /var/log/auth.log | tail -20(IP·키 지문) - 무슨 권한 명령을 썼나 —
sudo grep COMMAND /var/log/auth.log | tail -50 - 패키지가 바뀌었나 —
/var/log/apt/history.log·grep -E "install|remove" /var/log/dpkg.log - 설정 파일이 바뀌었나 —
sudo find /etc -mtime -2 -type f -ls - 서비스가 재시작됐나 —
journalctl --since "2 days ago" | grep -i "Started\|Stopped" - 재부팅이 있었나 —
last reboot,uptime -s
5번의 find /etc -mtime -2는 특히 유용하다 —
최근 이틀 안에 수정된 설정 파일을 전부 뽑아 준다.
11장 요약
섹션 제목: “11장 요약”- 지금은
w·loginctl, 과거는last -F -a, 실패는lastb, 계정 정리는lastlog -b last가 보여주는 범위는 로테이션 주기(대개 한 달)까지 — 옛날은-f wtmp.1- utmp/wtmp는 사라지는 중이다 — 새 스크립트는
loginctl/journalctl기준으로 - SSH의 IP와 키 지문은
auth.log의Accepted publickey줄에 있다 - sudo 명령은 기본으로 다 남는다 — 단
sudo -i이후의 셸 명령은 안 남는다 ~/.bash_history는 감사 근거가 못 된다 (지워지고, 시각이 없고, 비대화형은 안 남는다)- 명령 단위 감사가 필요하면 auditd를 미리 깔고 범위를 좁혀 규칙을 건다
- “어제 뭐가 바뀌었나”는
find /etc -mtime -2와apt/history.log가 빠르다