서버는 한 번 설정되고 그 뒤로는 혼자 살아갑니다. 한 달 뒤에는 일부 패키지가 업그레이드를 기다리고, 인증서는 만료까지 삼 주 거리로 다가와 있고, 관련 없는 무언가를 설치한 뒤 SSH 설정에 불필요한 줄이 하나 생겨 있으며, 로그인을 지켜봐야 할 jail은 아무도 모르는 사이에 멈춰 있습니다. 그중 어느 것도 소리치지 않습니다. 모든 것이 그저 조용히 사실이기를 그만둘 뿐입니다.
아래는 정기적으로 점검할 가치가 있는 열두 가지이며, 명령도 함께 적었습니다. 한 바퀴에 십오 분쯤 걸립니다. 순서는 사람들이 들어오는 길에서 시작해 저절로 망가지는 것으로 이어집니다.
SSH: 다섯 줄
보아야 할 것은 파일이 아니라 실제로 적용되는 설정입니다. 그것은 /etc/ssh/sshd_config와 sshd_config.d 디렉터리 전체, 그리고 빌드 기본값에서 조합됩니다.
sudo sshd -T | grep -iE 'permitrootlogin|passwordauthentication|permitemptypasswords|maxauthtries|x11forwarding'
보고 싶은 값은 이렇습니다. permitrootlogin no, passwordauthentication no, permitemptypasswords no, maxauthtries는 3~4, 그리고 x11forwarding no. 앞의 셋은 문이고, 뒤의 둘은 그 경첩입니다. 연결당 시도 횟수의 상한과, 서버에는 쓸 일이 없는 그래픽 전달이지요.
두 가지 단서, 둘 다 스스로를 바깥에 가두는 쉬운 방법입니다. 첫째, 키가 작동한다는 것을 확인하기 전에는 비밀번호 로그인과 root 로그인을 끄지 마십시오. 별도의 세션에서, 지금 열린 세션은 닫지 않은 채로 확인합니다. 지금 root로 비밀번호를 써서 접속한 채 이 글을 읽고 있다면, 그 두 줄이 바깥에 가두는 사람은 바로 당신입니다.
둘째, 파일의 순서입니다. OpenSSH는 어떤 매개변수에 대해 가장 먼저 만난 값을 취하고, sshd_config.d의 파일들은 알파벳 순서로 읽힙니다. 클라우드 이미지는 보통 거기에 50-cloud-init.conf를 남겨 두는데, 당신이 자기 파일을 90-hardening.conf 같은 이름으로 지었다면 그쪽이 이깁니다. 자신의 설정은 더 작은 번호 아래에 둡니다. 00- 또는 10-입니다. 그리고 결과는 파일을 읽는 대신 sshd -T로 확인합니다.
방화벽: 한 가지 점검
sudo ufw status verbose
여기서 기억할 점은, 이 출력이 보여 주는 것은 결과가 아니라 의도라는 사실입니다. 규칙이 목록에 있으면서도 아무것도 막지 않을 수 있습니다. UFW 자체가 꺼져 있거나, 포트를 Docker가 게시한 무언가가 맡고 있거나(그것은 자기 규칙을 UFW 아래 iptables에 씁니다), 트래픽이 다른 경로로 서버에 닿고 있는 경우입니다. 그러니 규칙 목록을 실제로 바깥을 향해 듣고 있는 것과 맞춰 보는 편이 좋습니다.
sudo ss -tulpn | grep -v '127.0.0.1\|::1'
fail2ban: 두 가지 점검
서비스가 돌아가는 것만으로는 부족합니다. SSH를 위한 작동하는 jail도 있어야 합니다.
sudo fail2ban-client status
sudo fail2ban-client status sshd
여기서 가장 흔한 말썽은 침묵하는 jail입니다. Debian 12와 최신 Ubuntu에서는 /var/log/auth.log가 아예 없을 수 있습니다. rsyslog가 설치되어 있지 않고 기록은 journald 안에서만 살기 때문입니다. 기본 logpath를 쓰는 jail은 그래도 시작되고, 스스로 활성 상태라고 보고하며, 누구도 결코 차단하지 않습니다. 고치는 방법은 systemd 저널을 가리키게 하는 것입니다.
printf '[sshd]\nenabled = true\nbackend = systemd\n' | sudo tee /etc/fail2ban/jail.d/sshd-systemd.local
sudo systemctl restart fail2ban
설정을 뒤지지 않고 이것을 알아채는 신호는 이렇습니다. fail2ban-client status sshd에서 「Currently failed」와 「Total failed」가 0에 머물러 있는데, 저널에는 실패한 로그인이 분명히 들어 있는 상태입니다.
업데이트: 세 가지 점검
따로따로 봅니다. 전부 몇 개의 패키지가 대기 중인지, 그중 보안 관련은 몇 개인지, 그리고 커널 업데이트 뒤에 시스템이 재부팅을 요구하는지.
sudo apt update
apt list --upgradable 2>/dev/null | grep -c -- '-security'
ls /var/run/reboot-required 2>/dev/null && echo '재부팅 필요'
세 번째 점검은 자동 보안 업데이트가 켜져 있는지입니다. 그래야 앞의 둘이 매달 치르는 의식이 되지 않습니다.
systemctl status unattended-upgrades --no-pager
cat /etc/apt/apt.conf.d/20auto-upgrades
그 파일은 두 줄 모두 1이어야 합니다. 패키지가 없다면 sudo apt install unattended-upgrades && sudo dpkg-reconfigure -plow unattended-upgrades.
인증서: 한 가지 점검
Let's Encrypt 인증서의 수명은 아흔 날이고, 갱신은 대개 저절로 돌아갑니다. 돌아가지 않게 되는 바로 그날까지는요. 보아야 할 것은 등록 대행사의 화면이 아니라 서버가 실제로 내주는 것입니다.
echo | openssl s_client -connect localhost:443 -servername example.com 2>/dev/null | openssl x509 -noout -enddate
sudo certbot renew --dry-run
첫 번째 명령은 그 이름으로 제공되는 인증서의 만료일을 알려 주고, 두 번째는 진짜 기한을 기다리지 않고 갱신이 통과할지 확인해 줍니다. 주 도메인만이 아니라 서버가 맡고 있는 모든 이름을 훑어볼 가치가 있습니다. 잊히는 것은 대개 나중에 추가한 하위 도메인이니까요.
디스크: 한 가지 점검
df -h
df -i
명령이 둘인 까닭은 공간과 파일 기록이 서로 독립적으로 바닥나기 때문입니다. 대부분의 서버에서는 뒤쪽이 먼저 천장에 닿습니다. PHP 세션이나 애플리케이션 캐시에 있는 수백만 개의 작은 파일이, 기가바이트가 남아 있는데도 IUse%를 100으로 만들어 버립니다. 두 경우 모두에 대해 별도의 글이 있습니다.
이 목록의 문제
목록 자체는 옳습니다. 다만 한 가지 성질이 있습니다. 누군가 그것을 기억하고 있어야 한다는 점입니다. 일주일에 십오 분은 많은 시간이 아닙니다. 서버가 한 대인 동안, 그리고 할 이유가 있는 동안은요. 조용한 두 달이 지나면 그 한 바퀴는 건너뛰게 되고, jail이 멈췄다는 소식은 남의 로그에서 듣게 됩니다.
그래서 바로 이 열두 가지 점검을 별도의 무료 패널에 담았습니다 — Arcivéo FREE. 명령 한 줄로 설치되고, 여러분의 서버에서 돌아가며, 우리에게 아무것도 보내지 않고, 가입도 필요 없습니다. 수집기가 cron에서 오 분마다 실행되어 같은 것들을 로컬 데이터베이스에 기록합니다. SSH 로그인과 거부된 연결, UFW와 fail2ban의 상태, 대기 중인 보안 업데이트, 인증서의 기한, 디스크 공간입니다. 기록은 이레 치이고 화면은 34개 언어로 제공됩니다. 가격은 0이며, 업무용 서버에서도 마찬가지입니다. 라이선스는 회사 장비를 포함해 자신의 장비에 패널을 설치하는 것을 허용하고, 재판매하거나 남의 서버를 위한 서비스로 운영하는 것은 허용하지 않습니다.
무료판의 구성은 수수합니다. UFW와 fail2ban을 설치하고 켜 준 다음, 그때부터 상태를 보여 줍니다. ModSecurity, Suricata, AIDE를 비롯한 나머지 모듈은 들어 있지 않으며, 그것들은 유료판의 몫입니다. 다만 매주의 한 바퀴를 머릿속에 지고 다니지 않게 되기에는 이것으로 충분합니다. 같은 데이터가 전체 패널에서 어떻게 보이는지는 아래 데모 페이지에서.