콘텐츠로 이동
Study Note서버 관리 일반

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 · loginctlutmp / logind
그 사람이 지금 뭘 하고 있나w 의 WHAT 열 · ps -u 사용자 -f—
과거에 누가 로그인했나last -F -awtmp
로그인 실패 이력sudo lastb -F · auth.logbtmp
계정별 마지막 로그인lastloglastlog
재부팅 이력last rebootwtmp
SSH 접속 상세(IP·키 지문·실패 이유)journalctl -u ssh · /var/log/auth.logjournal / rsyslog
누가 sudo로 무슨 명령을 썼나grep sudo /var/log/auth.log · journalctl _COMM=sudoauth.log
누가 패키지를 설치했나/var/log/apt/history.logapt
셸에서 친 명령~/.bash_history (신뢰도 낮음 — 아래)셸
실행된 모든 명령 (제대로)auditd (미리 설정해야 한다)커널
터미널 창
w # 사용자 · 접속 원격지 · 로그인 시각 · 유휴 시간 · 실행 중인 명령
who -a # 더 단순한 목록
loginctl list-sessions # systemd가 보는 세션 (utmp보다 정확하다)
loginctl session-status 42 # 그 세션의 프로세스 트리까지
sudo ss -tp state established '( sport = :22 )' # SSH 연결의 상대 IP
pgrep -a -u deploy # 이 사용자가 돌리는 프로세스
$ w
15:22:31 up 21 days, load average: 0.31, 0.22, 0.18
USER TTY FROM LOGIN@ IDLE WHAT
sshim pts/0 10.20.5.77 14:58 0.00s w
deploy pts/1 10.20.5.91 13:02 1:42m tail -f /var/log/app.log

IDLE이 길면 붙어만 있는 세션이다. 강제로 끊어야 하면 sudo loginctl terminate-session 42 (또는 그 세션의 셸 PID에 kill).

터미널 창
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 5
sshim pts/0 Mon Aug 10 14:58:11 2026 still logged in 10.20.5.77
deploy pts/1 Mon Aug 10 13:02:03 2026 - Mon Aug 10 15:20:41 2026 (02:18) 10.20.5.91
reboot system boot Mon Jul 20 04:11:52 2026 still running 6.8.0-51-generic

SSH 접속의 상세 — 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.log
sudo 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 ssh2
Disconnected 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 COMMAND
sudo 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_history
sudo cat /home/deploy/.bash_history
sudo cat /root/.bash_history
history # 현재 세션 (메모리 + 파일)

최소한의 개선은 해 둘 만하다 — /etc/profile.d/history.sh

터미널 창
export HISTTIMEFORMAT="%F %T " # 시각 기록
export HISTSIZE=10000
export HISTFILESIZE=20000
shopt -s histappend # 덮어쓰지 않고 이어붙인다
export PROMPT_COMMAND="history -a" # 명령마다 즉시 파일에 기록
  1. 설치 (Ubuntu 기본에는 없다)

    터미널 창
    sudo apt install -y auditd audispd-plugins
    systemctl status auditd
  2. 규칙을 건다 — /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
  3. 조회

    터미널 창
    sudo ausearch -k sudoers -i # -i 로 UID를 이름으로 풀어 준다
    sudo ausearch -ua deploy --start today -i
    sudo aureport --auth --summary
    sudo aureport -l -i # 로그인 리포트

대안으로 sudo 자체의 세션 녹화도 있다 — sudoers에 Defaults log_output 을 주면 sudoreplay로 화면 출력까지 재생할 수 있다.

  1. 언제 누가 들어왔나 — last -F -a | head -20, sudo lastb -F | head
  2. 어떻게 인증했나 — sudo grep Accepted /var/log/auth.log | tail -20 (IP·키 지문)
  3. 무슨 권한 명령을 썼나 — sudo grep COMMAND /var/log/auth.log | tail -50
  4. 패키지가 바뀌었나 — /var/log/apt/history.log · grep -E "install|remove" /var/log/dpkg.log
  5. 설정 파일이 바뀌었나 — sudo find /etc -mtime -2 -type f -ls
  6. 서비스가 재시작됐나 — journalctl --since "2 days ago" | grep -i "Started\|Stopped"
  7. 재부팅이 있었나 — last reboot, uptime -s

5번의 find /etc -mtime -2는 특히 유용하다 — 최근 이틀 안에 수정된 설정 파일을 전부 뽑아 준다.

  • 지금은 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가 빠르다