대상 OS: Ubuntu Server 24.04 LTS

서버 수가 늘수록 SSH 공개키는 ‘파일’이 아니라 ‘자산’이 됩니다.
authorized_keys를 서버마다 흩어 놓으면 퇴사자 키 회수, 외주 만료, 임시 접근 승인 같은 운영이 항상 늦고, 그 지연이 그대로 보안 사고로 이어집니다.
오늘은 OpenSSH의 AuthorizedKeysCommand를 이용해 “로그인 시점에” 중앙 정책으로 키를 내려주고, 키 회수/만료/조건부 허용을 서버 설정 한 곳에서 강제하는 방법을 정리합니다.
핵심은 (1) sshd는 최소 권한으로 조회만, (2) 중앙 데이터는 캐시·서명·로깅으로 안전하게, (3) 장애 시에도 접근이 완전히 끊기지 않는 설계입니다.
아래 예시는 파일 기반 ‘미니 중앙 저장소’로 시작해, 나중에 API/CMDB로 확장할 수 있게 구조를 잡습니다.

---

# 1) 왜 AuthorizedKeysCommand인가: 운영 리스크를 줄이는 보안 기능

1) “키 배포”를 사람이 안 하게 만든다

- 서버에 로그인할 때마다 키를 조회하므로, 키 추가/회수의 반영 지연이 줄어듭니다.

2) 키 자체에 정책을 붙일 수 있다

- 특정 계정은 특정 소스 IP에서만
- 특정 키는 특정 명령만
- 만료일/승인 상태에 따라 자동 차단

3) 감사(Audit)가 쉬워진다

- “누가 어떤 키로 들어왔는지”를 중앙에서 조회 로그로 남길 수 있습니다.

---

# 2) 설계: 중앙 키 저장소(파일) + 조회 스크립트 + 로컬 캐시

1) 구성 요소

- `/etc/ssh/authorized_keys.d/` : “중앙에서 내려온 결과”를 저장하는 캐시/정적 파일(선택)
- `/usr/local/sbin/ssh-keys-fetch` : sshd가 호출하는 조회 프로그램
- `/etc/ssh/keysdb/keys.tsv` : 예시용 중앙 DB(서버 한 대에서 시작)

2) 보안 원칙

- 조회 스크립트는 root가 아니라 “전용 저권한 사용자”로 실행
- 스크립트/DB 파일 권한을 엄격히(쓰기 차단)
- 출력은 OpenSSH가 기대하는 “authorized_keys 형식”만

---

# 3) 준비: 전용 계정과 디렉터리 만들기

1) 조회용 전용 계정 생성

sudo useradd --system --home /nonexistent --shell /usr/sbin/nologin sshkeys

2) 중앙 DB 디렉터리 생성(예시)

sudo install -d -m 0750 -o root -g sshkeys /etc/ssh/keysdb
sudo install -d -m 0755 -o root -g root /etc/ssh/authorized_keys.d

---

# 4) ‘중앙 키 DB’ 예시 포맷(가장 단순한 TSV)

1) keys.tsv 예시

- 형식: `usernamestatusnot_afterkey_optionspublic_key`
- status: `active` 또는 `revoked`
- not_after: `YYYY-MM-DD` (만료일)
- key_options: OpenSSH authorized_keys 옵션(필요할 때만)

sudo bash -lc 'cat >/etc/ssh/keysdb/keys.tsv /dev/null || true
sudo rm -f /run/sshd_keys_test.pid

4) 본 서비스 재시작

sudo systemctl restart ssh
sudo systemctl --no-pager -l status ssh | sed -n '1,30p'

---

# 8) 감사/탐지 포인트: 중앙 조회 로그를 남겨라

1) 단순하지만 강력한 방법: 시스템 로그로 조회 이벤트 기록

- 조회 스크립트에서 “누가 조회됐는지”를 남기면, 계정 열람/접속 시도 패턴을 볼 수 있습니다.
- 아래는 예시로, 너무 시끄러우면 rate limit을 붙이세요.

sudo bash -lc 'grep -n "awk -F" /usr/local/sbin/ssh-keys-fetch'

(운영에서는 아래처럼 logger를 넣어도 됩니다. 필요 시 직접 편집)

sudo sed -n '1,80p' /usr/local/sbin/ssh-keys-fetch

2) SSH 접속 로그 확인

sudo journalctl -u ssh -S -2h --no-pager | tail -n 200

---

# 9) 하드닝 체크리스트(중요)

1) AuthorizedKeysCommandUser 권한 최소화

- sshkeys 계정은 로그인 불가(nologin)
- DB 파일은 root 소유 + sshkeys 그룹 읽기만(0640)

2) 스크립트 무결성

- `/usr/local/sbin/ssh-keys-fetch`는 root:root, 0755
- 배포 체계(Ansible, GitOps)로 변경 이력 관리

3) 장애 대비

- 로컬 비상키(최소 1개) 유지
- 중앙 DB는 백업/버전 관리
- 변경은 “테스트 포트 sshd”로 검증 후 반영

4) 확장(다음 단계)

- keys.tsv 대신 HTTPS API로 조회(서명된 응답, mTLS)
- 응답 캐시(짧은 TTL)로 부하/장애 완화
- 사용자 승인 워크플로우(만료 자동화)