서버를 VPC에 올린 지 30초면 Shodan 같은 스캐너에 잡힌다. 공개 IP를 받는 순간부터 SSH 22번 포트를 향한 로그인 시도가 시작된다. 기본 설정 그대로 두면 다음 4가지 경로로 침해가 발생한다.
Linux 서버 보안 하드닝 6단계 — 배포 직후 반드시 적용해야 할 실전 체크리스트
AWS EC2·GCP·DigitalOcean 등에 Ubuntu/Debian 서버를 배포한 직후 적용해야 할 보안 강화 6단계. SSH 키 인증·UFW 방화벽·Fail2ban·자동 업데이트·unattended-upgrades 설정까지 실제 명령어 기준으로 정리. root 접근 차단, 포트 화이트리스트, 침입 감지까지 커버.
한 줄 요약: 신규 Linux 서버를 배포한 직후 반드시 적용해야 할 보안 강화 6단계를 실제 명령어 기준으로 정리했다. SSH 키 인증, UFW 방화벽, Fail2ban, 자동 업데이트까지 순서대로 따라가면 된다.
- AWS EC2, GCP, DigitalOcean 등에서 Ubuntu/Debian 서버를 처음 열어본 개발자
- 비밀번호 로그인, 루트 직접 접속이 아직 활성화된 서버를 운영 중인 팀
- 보안 감사를 앞두고 기본 하드닝 체크리스트가 필요한 DevOps 담당자
※ 2026년 4월 기준, Ubuntu 22.04 LTS / Debian 12 환경에서 검증. root 또는 sudo 권한 필요.
배포 직후 서버가 노출되는 공격 벡터 4가지
이 4가지는 서버 운영 경력과 무관하게 기본 배포 스크립트에 포함시켜야 할 항목이다. 아래 6단계를 순서대로 적용하면 위 취약점이 모두 제거된다.
1단계 — SSH 키 인증 설정 (비밀번호 로그인 완전 제거)
비밀번호 기반 SSH 로그인은 자동화된 브루트포스 도구에 무방비다. 로컬에서 키 페어를 생성하고 서버에 공개 키를 등록한 뒤, 비밀번호 로그인을 비활성화한다. 순서가 중요하다 — 키를 먼저 등록하고 비밀번호를 차단해야 잠기지 않는다.
로컬 머신에서 SSH 키 생성 (없는 경우)# Ed25519 키 생성 (RSA 4096 대신 권장) ssh-keygen -t ed25519 -C "your@email.com" -f ~/.ssh/id_ed25519 # 생성된 공개 키 확인 cat ~/.ssh/id_ed25519.pub # 서버에 공개 키 복사 (비밀번호 로그인이 아직 가능한 상태에서 실행) ssh-copy-id -i ~/.ssh/id_ed25519.pub user@your-server-ip # 키 인증으로 접속 확인 (이 단계가 성공해야 다음으로 진행) ssh -i ~/.ssh/id_ed25519 user@your-server-ip
키 인증 접속이 확인되면 서버에서 /etc/ssh/sshd_config를 수정한다. 이 파일이 SSH 데몬의 핵심 설정이다.
/etc/ssh/sshd_config 핵심 보안 설정# 루트 직접 로그인 금지 PermitRootLogin no # 비밀번호 로그인 비활성화 (키 등록 후에만 적용) PasswordAuthentication no # 빈 비밀번호 계정 접속 금지 PermitEmptyPasswords no # SSH 프로토콜 버전 (2만 허용) Protocol 2 # 로그인 시도 최대 횟수 MaxAuthTries 3 # 연결 후 로그인 제한 시간 (초) LoginGraceTime 30 # 인증된 사용자만 접속 허용 (실제 계정명으로 교체) AllowUsers deploy ubuntu # 설정 적용 # sudo systemctl restart sshd
sshd 재시작 전에 반드시 확인:
1.
~/.ssh/authorized_keys 파일에 공개 키가 등록되어 있는가2. 현재 터미널 세션을 유지한 상태로 새 터미널에서 키 인증 접속 테스트
3. 접속 성공 후 기존 세션에서
sudo systemctl restart sshd 실행
2단계 — UFW 방화벽으로 필요한 포트만 열기
UFW(Uncomplicated Firewall)는 iptables의 프론트엔드로, 명령어가 직관적이고 Ubuntu/Debian에 기본 설치되어 있다. 전략은 단순하다: 기본 차단, 필요한 포트만 명시적으로 허용.
UFW 방화벽 초기 설정# UFW 상태 확인 sudo ufw status verbose # 기본 정책: 인바운드 차단, 아웃바운드 허용 sudo ufw default deny incoming sudo ufw default allow outgoing # SSH 포트 허용 (반드시 먼저! 안 하면 잠김) sudo ufw allow 22/tcp # 웹 서버 포트 허용 sudo ufw allow 80/tcp sudo ufw allow 443/tcp # 특정 IP에서만 SSH 허용 (권장) # sudo ufw allow from 123.45.67.89 to any port 22 # UFW 활성화 sudo ufw enable # 규칙 확인 sudo ufw status numbered
UFW 규칙 관리 — 추가/삭제/포트 변경# 번호로 규칙 삭제 sudo ufw status numbered sudo ufw delete 3 # 3번 규칙 삭제 # 포트 범위 허용 sudo ufw allow 8000:8080/tcp # 서비스 이름으로 허용 sudo ufw allow 'Nginx Full' # 80 + 443 # 규칙 초기화 (주의: 모든 규칙 삭제) # sudo ufw reset # UFW 로그 확인 sudo tail -f /var/log/ufw.log
SSH 포트를 22에서 변경하면 스캔 노출을 줄일 수 있지만,
/etc/ssh/sshd_config의 Port를 수정하고 UFW도 새 포트를 허용해야 한다. 변경 후 새 포트로 접속 확인 전까지 기존 포트 규칙을 유지할 것.3단계 — Fail2ban으로 반복 로그인 시도 자동 차단
키 인증만으로도 상당히 안전하지만, 잘못된 키 시도가 반복되면 로그가 쌓이고 서비스에 부하가 생긴다. Fail2ban은 인증 실패 로그를 감시하다가 임계값을 초과하면 해당 IP를 자동으로 차단한다.
Fail2ban 설치 및 기본 설정# 설치 sudo apt update && sudo apt install -y fail2ban # 서비스 시작 및 자동 시작 등록 sudo systemctl enable --now fail2ban # 설정 파일 복사 (원본은 건드리지 않는다) sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
/etc/fail2ban/jail.local — SSH 차단 설정[DEFAULT] # 차단 해제까지 대기 시간 (초) — 10분 bantime = 600 # 실패 횟수 집계 시간 (초) — 10분 findtime = 600 # 이 횟수 초과 시 차단 maxretry = 5 # 이메일 알림 (선택) # destemail = admin@yourdomain.com # action = %(action_mwl)s [sshd] enabled = true port = ssh logpath = %(sshd_log)s backend = %(sshd_backend)s maxretry = 3 # 설정 적용 # sudo systemctl restart fail2ban
Fail2ban 상태 확인과 IP 차단/해제# 활성 jail 목록 확인 sudo fail2ban-client status # SSH jail 상세 상태 (차단된 IP 목록 포함) sudo fail2ban-client status sshd # 특정 IP 수동 차단 sudo fail2ban-client set sshd banip 1.2.3.4 # 특정 IP 차단 해제 sudo fail2ban-client set sshd unbanip 1.2.3.4 # 로그 실시간 확인 sudo tail -f /var/log/fail2ban.log

4단계 — 일반 사용자 계정과 sudo 권한 최소화
루트로 모든 작업을 처리하는 습관은 실수 하나로 서버 전체를 날릴 수 있다. 배포 전용 계정을 만들고, sudo는 꼭 필요한 명령에만 허용하는 것이 원칙이다.
배포 전용 사용자 생성 및 sudo 설정# 새 사용자 생성 sudo adduser deploy # sudo 그룹 추가 (전체 sudo 권한) sudo usermod -aG sudo deploy # 또는 특정 명령만 허용 (권장) # /etc/sudoers.d/deploy 파일 생성 sudo visudo -f /etc/sudoers.d/deploy # 아래 내용 추가: # deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart app, /usr/bin/docker # deploy 계정에 SSH 공개 키 등록 sudo mkdir -p /home/deploy/.ssh sudo cp ~/.ssh/authorized_keys /home/deploy/.ssh/ sudo chown -R deploy:deploy /home/deploy/.ssh sudo chmod 700 /home/deploy/.ssh sudo chmod 600 /home/deploy/.ssh/authorized_keys # 현재 계정의 sudo 권한 확인 sudo -l
Nginx, Node.js 앱 실행용 계정은 로그인 불가 계정(
--shell /usr/sbin/nologin)으로 만든다. 배포 자동화에 쓰는 deploy 계정은 SSH 접속은 가능하되 루트 권한 없이 설계한다. 두 역할을 같은 계정으로 묶지 않는다.5단계 — 자동 보안 업데이트와 시스템 로그 모니터링
패키지 취약점은 대부분 업데이트로 패치된다. 수동 업데이트를 매번 기억하기 어려우므로 보안 패치만 자동으로 적용하는 unattended-upgrades를 설정한다. 커널 업데이트를 제외하면 재부팅 없이 운영 가능하다.
자동 보안 업데이트 설정# 패키지 설치 sudo apt install -y unattended-upgrades apt-listchanges # 설정 활성화 (대화형) sudo dpkg-reconfigure --priority=low unattended-upgrades # 설정 파일 확인 및 수정 sudo nano /etc/apt/apt.conf.d/50unattended-upgrades # 핵심 설정 (보안 업데이트만 자동 적용) # Unattended-Upgrade::Allowed-Origins { # "${distro_id}:${distro_codename}-security"; # }; # Unattended-Upgrade::AutoFixInterruptedDpkg "true"; # Unattended-Upgrade::Remove-Unused-Packages "true"; # Unattended-Upgrade::Automatic-Reboot "false"; // 자동 재부팅 비활성화 # 테스트 실행 (dry-run) sudo unattended-upgrade --dry-run --debug
시스템 로그 모니터링 명령어 모음# SSH 로그인 시도 확인 (Ubuntu/Debian) sudo journalctl -u ssh --since "1 hour ago" | grep -i "failed|invalid|accepted" # 최근 로그인 기록 last -n 20 lastb -n 20 # 실패한 로그인 # 현재 접속 중인 사용자 who # 실시간 인증 로그 확인 sudo tail -f /var/log/auth.log # 비정상 프로세스 확인 ps aux --sort=-%cpu | head -20 # 열린 포트 확인 (ss 또는 netstat) ss -tlnp

보안 강화 후 점검 방법 — 실제 동작 확인
6단계를 모두 적용했다면 실제로 동작하는지 검증해야 한다. 아래 점검 항목을 순서대로 실행하면 된다.
추가로 lynis를 사용하면 서버 전체 보안 감사를 자동화할 수 있다. sudo apt install lynis && sudo lynis audit system을 실행하면 Hardening Index 점수와 개선 항목 목록을 준다. 이 글의 설정을 모두 적용했다면 점수가 60~70점대 이상으로 올라간다.