튜토리얼
새 VPS를 하드닝하는 방법: 첫 10분이 전부다
새 서버는 IP 주소를 받는 그 순간 가장 크게 노출된다. 자동화된 스캐너는 IPv4 전체 공간을 쉬지 않고 훑고 다니므로, 1분 전에 배포한 박스에 대한 첫 로그인 시도는 대개 환영 메시지를 다 읽기도 전에 도착한다. 이는 당신을 노린 공격이 아니다 — 배경 방사선 같은 것이며, root에 비밀번호를 걸어 둔 하드닝되지 않은 서버가 몇 달이 아니라 몇 시간 단위로 뚫리는 이유이기도 하다. 다행히 이를 막는 일은 짧게 끝난다: 정해진 순서로 10분만 들이면 기회주의적 위험은 사실상 모두 사라진다. 이 가이드는 그 체크리스트와, 스스로를 걸어 잠그지 않는 순서, 그리고 대부분의 하드닝 튜토리얼이 절대 언급하지 않는 한 가지 — 당신과 연결되지 않게 하려고 일부러 빌린 머신에 기본 설정이 조용히 남겨 놓는 개인정보까지 다룬다.
하드닝이 실제로 지켜주는 것
하드닝을 할 가치가 있는 이유는 정확히, 그 효과가 좁지만 확실하기 때문이다. 그 경계를 분명히 알아 두어야 하드닝이 닿지 않는 영역에서 잘못된 안전감에 빠지지 않는다.
- 기회주의적 공격자를 완전히 차단한다. 크리덴셜 스터핑 봇, SSH 무차별 대입, 노출된 관리자 패널을 노리는 스캔 모두 키 전용 인증과 기본 거부 방화벽 앞에서는 실패한다.
- 당신이 돌리는 무언가에 결함이 있을 때 피해 범위를 제한한다. 닫힌 포트 뒤에서 localhost에만 바인딩된 서비스는 그 CVE가 공개되는 날에도 낯선 사람이 닿을 수 없다.
- 실수 하나의 대가를 줄여준다. 권한 없는 사용자, 분리된 키, 자동 패치가 있으면 잘못된 판단 하나로 머신 전체를 내주는 일은 없다.
- 서버를 호스트로부터 숨겨주지는 않는다. 하이퍼바이저에 접근할 수 있는 사람은 원칙적으로 가동 중인 머신의 메모리를 읽을 수 있다 — 이는 방화벽 규칙이 아니라 누구에게서 빌리는가의 문제다.
- 당신을 익명으로 만들어주지도 않는다. 하드닝은 박스를 지켜줄 뿐이다. 그 박스가 당신의 이름과 연결되는지는 첫 로그인보다 훨씬 전, 가입과 결제 시점에 이미 정해진다.
사람들이 가장 자주 거꾸로 이해하는 것이 바로 이 마지막 구분이다. 본명으로 신용카드를 긁어 완벽하게 하드닝한 서버라 해도, 여전히 당신의 본명으로 된 서버일 뿐이다. 이 두 계층은 서로 보완적이다: no-KYC 호스팅과 암호화폐로 채운 잔액이 서류상 그 머신이 누구의 것인지를 결정하고, 아래에 나오는 10분이 그 안에 누가 들어갈 수 있는지를 결정한다.
스스로를 걸어 잠그지 않는 순서
하드닝에 얽힌 공포담은 거의 다 같은 이야기다: 키가 작동하는지 확인하기도 전에 비밀번호 인증부터 꺼버렸거나, SSH 규칙이 빠진 방화벽을 켜버려서 더 이상 닿을 수 없는 머신 밖으로 스스로를 걸어 잠근 것이다. 순서가 존재하는 이유는 바로 이런 일을 원천적으로 막기 위해서다. 키를 먼저 설치하고 두 번째 창에서 작동을 확인하라. 그런 다음에야 비밀번호를 끈다. 방화벽 규칙에 SSH를 추가하는 것은 반드시 방화벽을 켜기 전이어야 하며, 절대 나중이 되어서는 안 된다.
복구 경로가 무엇인지 필요해지기 전에 미리 알아두는 것도 가치가 있다. SSH로만 접근할 수 있는 VPS에는 문이 정확히 하나뿐이므로, 제공업체 대시보드의 콘솔이야말로 잠금을 재구축이 아니라 사소한 불편으로 바꿔주는 대안이다. 아직 모든 것이 멀쩡할 때 그 콘솔을 열 수 있는지 미리 확인해 두어라.
단계별 안내: 새 박스에서 보내는 첫 10분
- 01패키지 인덱스와 설치된 패키지 업데이트하기새 이미지는 빌드된 시점의 스냅샷일 뿐이다. 최신 상태로 맞추는 것이 목록에서 가장 값어치가 큰 작업이며, 1분도 채 걸리지 않는다.
- 02sudo 권한을 가진 일반 사용자 만들기항상 root로 작업한다는 것은 오타 하나, 프로세스 하나까지 전권을 쥔 채 실행된다는 뜻이다. 일반 사용자를 만들어 sudo나 wheel 그룹에 추가하고, 그 계정을 사용하라.
- 03그 사용자에게 공개 키 복사하기아직 없다면 자신의 머신에서 ed25519 키를 생성한 다음, ssh-copy-id로 공개 키 절반만 전송하라. 개인 키는 절대 노트북을 벗어나지 않는다.
- 04두 번째 터미널을 열고 키 로그인이 되는지 확인하기이 단계를 건너뛰지 마라. 인증과 관련된 무언가를 바꾸기 전에, 새 창에서 키로 새 사용자로 로그인해 보라.
- 05비밀번호 인증과 root 로그인 비활성화하기PasswordAuthentication no와 PermitRootLogin prohibit-password를 설정한 다음 sshd를 다시 불러오라. 이제 박스를 노린 무차별 대입 공격은 더 이상 추측할 대상이 없다.
- 06SSH만 허용한 기본 거부 방화벽 활성화하기모든 인바운드를 거부하고, SSH 포트만 허용한 다음 활성화하라. 자신의 서비스에 필요한 포트는 그 이후에 하나씩 추가하면 된다.
- 07자동 보안 업데이트 켜기Debian과 Ubuntu에서는 unattended-upgrades, RHEL 계열에서는 dnf-automatic을 사용하라. 6개월 뒤 더 이상 신경 쓰지 않게 되었을 때도 머신을 안전하게 지켜주는 것이 바로 이것이다.
- 08무엇이 열려 있는지 확인하고 있어서는 안 될 것은 닫기ss -tulpn 한 번이면 열려 있는 모든 소켓을 확인할 수 있다. 의도적으로 열어두지 않은 것은 모두 제거하거나 localhost에 바인딩해야 한다.
SSH: 키만 남기고, 중요한 설정들
공개 키 인증이 SSH 하드닝의 전부라고 해도 과언이 아니다. 비밀번호를 끄고 나면 공격자에게는 갖고 있지 않은 개인 키가 필요해지며, 아무리 추측해도 그 키는 나오지 않는다. ed25519 키를 생성하라 — 짧고 빠르며 요즘의 기본값이다 — 그리고 키에 패스프레이즈를 걸어서 노트북을 도난당해도 서버까지 도난당하지 않게 하라. sshd_config의 나머지 설정은 모두 이 한 가지 결정 위에 얹히는 다듬기에 불과하다.
- PasswordAuthentication no — 무차별 대입을 끝내는 설정이다. 먼저 두 번째 세션에서 키가 작동하는지 확인하라.
- PermitRootLogin prohibit-password — 복구용으로 키를 통한 root 접근은 여전히 가능하지만, 비밀번호로는 절대 불가능하다. sudo 사용자가 제대로 동작함을 확인했다면 no로 바꿔라.
- AllowUsers 또는 AllowGroups — 애초에 누가 로그인할 수 있는지 명시적으로 허용 목록을 두어, 어떤 패키지가 만든 서비스 계정이 SSH 진입점이 되는 일을 원천 차단한다.
- KbdInteractiveAuthentication no — 일부 배포판에서 조용히 비밀번호 프롬프트를 다시 열어버릴 수 있는 또 다른 대화형 경로를 닫는다.
- 개인 키 하나를 여기저기 복사해 쓰지 말고, 기기마다 별도의 키를 써라. 휴대폰을 잃어버렸을 때는 authorized_keys에서 한 줄만 지우면 되어야지, 모든 것을 다시 발급해야 해서는 안 된다.
- SSH를 22번 포트 밖으로 옮기면 로그량이 극적으로 줄어들지만, 이는 보안이 아니라 소음 감소로 취급해야 한다 — 실제로 당신의 IP를 들여다보는 사람에게는 아무것도 달라지지 않는다.
키보다 한 걸음 더 나아가고 싶다면, 가장 강력한 방법은 SSH를 아예 공개하지 않는 것이다: 머신을 WireGuard 터널 뒤에 두고, 22번 포트를 방화벽으로 막아 터널 주소에서 오는 연결만 받아들이게 하라. 그러면 SSH 데몬은 인터넷이 아예 볼 수 없는 존재가 되며, 이는 노출된 상태에서 아무리 설정을 다듬는 것보다도 나은 결과다.
방화벽: 기본은 거부, 쓰는 것만 정확히 연다
방화벽은 기본값이 거부일 때만 의미가 있다. 일단 다 허용해 놓고 알려진 나쁜 포트만 막는 방식은 순서가 거꾸로다 — 결국 기억하고 있던 서비스만 지키고, 잊어버린 서비스는 그대로 노출된다. 모든 인바운드 트래픽을 거부하고 아웃바운드는 허용한 다음, 필요한 것을 배포할 때마다 포트를 하나씩 열어라. 요즘 리눅스는 내부적으로 nftables를 쓰며, ufw나 firewalld는 그 위에 얹히는 훌륭한 프런트엔드다 — 어떤 도구를 쓰는지보다 기본값이 무엇인지가 훨씬 더 중요하다.
- 방화벽을 켜기 전에 SSH부터 허용하라, 나중이 아니라. 이것이 스스로를 걸어 잠그는 두 번째로 흔한 방법이다.
- 서비스가 외부로부터 정말로 필요로 하는 포트만 열어라. 웹 서버라면 80과 443이 필요하지만, 데이터베이스는 거의 아무것도 필요하지 않다.
- 로컬 전용 서비스는 0.0.0.0이 아니라 127.0.0.1에 바인딩하라. 닫힌 포트와 애초에 공개적으로 리스닝하지 않는 서비스는 서로 독립된 두 겹의 방어이며, 둘 다 갖추어야 한다.
- IPv4뿐 아니라 IPv6 규칙도 작성하라. 듀얼 스택 박스에서 v4만 다루는 규칙 세트는 같은 서비스를 v6 주소에서 활짝 열어둔 채로 방치하는 셈이다.
- 가능하다면 관리용 포트는 출발지 주소로 제한하라. 관리 접속이 항상 하나의 VPN 엔드포인트에서 온다면, 그 사실을 규칙에 명시하라.
- 서비스를 추가한 뒤에는 규칙을 다시 읽어보라. 이미 지워버린 무언가를 위해 열어두었던 포트야말로 깔끔했던 시작을 조용히 무너뜨리는 것들이다.
일부 워크로드는 이 논리를 뒤집어 일부러 활짝 열린 포트를 필요로 한다 — Tor 릴레이는 어디서든 오는 연결을 받아들여야 하고, Bitcoin 풀 노드는 8333에 닿을 수 있어야만 피어에게 서비스를 제공한다. 그것도 괜찮다. 원칙은 "아무것도 열지 마라"가 아니라 "의도적으로 열어라"이며, 공개되도록 설계된 서비스라면 그 자체가 의도적인 선택이다.
fail2ban, 그리고 생각보다 덜 중요한 이유
fail2ban은 로그를 지켜보다가 인증에 반복해서 실패하는 주소를 차단한다. 분명 유용하지만, 이미 비밀번호 인증을 꺼버린 뒤라면 그것이 실제로 무엇을 해주는지 솔직해질 필요가 있다: 그 시점부터는 아무도 추측만으로 성공할 수 없으므로, fail2ban이 더 이상 침입을 막아주는 것은 아니다. 대신 막아주는 것은 하루 수천 줄에 이르는 쓸모없는 로그, 그 접속 시도들이 잡아먹는 CPU, 그리고 몇 달 전부터 더 이상 읽지도 않는 소음 속에 진짜 사건이 묻혀버릴 위험이다.
그래도 10초짜리 설정을 해 둘 가치는 충분하며, 비밀번호를 실제로 받는 무언가 — 웹 애플리케이션 로그인, 메일 서버, 컨트롤 패널 — 를 돌리는 순간 진짜로 보호 효과를 발휘한다. sshd만이 아니라 그런 로그들도 가리키게 설정하라. 차단 기간은 분이 아니라 시간 단위로 설정하고, 손가락이 미끄러진 비밀번호 한 번으로 자기 머신에서 스스로 차단당하지 않도록 자신의 주소는 무시 목록에 추가해 두어라.
기억하지 않아도 되는 업데이트
제대로 설정된 서버가 실제로 마주하는 위협은 누군가 SSH 키를 뚫어내는 것이 아니다. 설치해 놓고 잊어버린 무언가에 대해 취약점이 공개되고, 사흘 뒤 다른 일을 하는 사이 스캐너에게 이용당하는 것이다. 자동 보안 업데이트가 그 답이며, Debian이나 Ubuntu에서는 보안 패키지만 설치하도록 설정한 unattended-upgrades가, Fedora나 Rocky, Alma에서는 dnf-automatic이 그 역할을 한다. 첫 10분 사이에 이를 켜두면, 당신의 관심이 다른 곳으로 옮겨간 한참 뒤에도 머신은 스스로 패치를 계속한다.
커널과 libc 업데이트만은 여전히 당신의 손이 필요한 예외다: 재시작을 거쳐야만 적용되므로, 400일째 켜져 있는 서버는 거의 확실히 1년 전에 디스크에서나 패치된 코드를 그대로 돌리고 있는 셈이다. Debian의 needrestart는 어떤 서비스가 이미 지워진 라이브러리를 붙든 채 돌아가고 있는지 알려주며, 한 달에 한 번이라도 정해진 재부팅 창을 두는 것이 설치된 패치와 실제로 적용된 패치 사이의 차이를 만든다. 어차피 무엇을 돌리든 예기치 못한 재부팅쯤은 견뎌내야 한다 — 그렇지 못하다면 그것은 따로 고쳐야 할 별개의 문제다.
하드닝 체크리스트가 놓치는 신원 유출
오프쇼어, no-KYC 호스트를 일부러 선택했다면 이 절이 특히 중요하다. 일반적인 하드닝 가이드는 소유자가 공개 기록에 남아 있는 회사 서버를 염두에 두고 쓰여지므로, 기본 설정이 당신에 대해 무엇을 기록해 두는지는 애초에 묻지 않는다. 익명으로 빌린 머신에서는 그런 기본값 몇 가지가 조용히 당신의 이름을 다시 그 위에 붙여 놓는다.
- SSH 공개 키에는 주석이 붙는다 — 기본값으로는 당신의 로컬 사용자 이름과 노트북 호스트네임, 이를테면 alex@alex-macbook 같은 형태이며 — 이 주석은 서버의 authorized_keys에 그대로 저장된다. 키를 생성할 때 -C 플래그로 직접 지정하거나, 복사한 뒤 그 줄을 편집하라.
- 당신이 정한 서버 호스트네임은 로그, 메일 헤더, 모니터링 출력, 때로는 서비스 배너에까지 그대로 남는다. 평범한 호스트네임은 아무것도 드러내지 않지만, 당신이나 회사의 이름이 들어간 호스트네임은 그렇지 않다.
- 시스템 타임존이다. 클라우드 이미지는 기본값이 UTC이며, 이는 아무에게도 아무것도 알려주지 않는다. 이를 자신의 로컬 시간대로 바꾸는 순간 당신이 어디에 있는지가 좁혀지고, 이후 모든 로그의 타임스탬프가 당신의 활동 시간대를 확인해 준다.
- 박스에 복사해 넣은 Git 설정에는 당신이 커밋할 때 쓰는 이름과 이메일 주소가 담겨 있다. 셸 히스토리 파일, 워크스테이션에서 동기화한 닷파일, 급하게 테스트하다 캐시된 자격 증명도 마찬가지다.
- 인증서를 발급받을 때 Let's Encrypt에 건네는 이메일 주소는 그 인증서의 공개 기록 일부가 된다. 당신의 신원과 연결되지 않은 주소를 사용하라.
- 웹 서버 버전 배너와 X-Powered-By 헤더, 그리고 계정 식별자를 매단 채 머신에서 자기 회사로 신호를 보내는 분석 도구, 크래시 리포터, 벤더 모니터링 에이전트도 마찬가지다.
이 계층들을 헷갈리지 않고 구분해 두는 것이 중요하다. 하드닝은 낯선 사람을 머신 밖으로 막아준다. 이런 세부 사항들은 이미 그 머신을 들여다보고 있는 누군가에게 머신이 당신에 대해 떠벌리지 못하게 막아준다. 그리고 결제 계층은 애초에 찾아낼 이름이 존재했는지 자체를 결정한다 — 이 주제는 Bitcoin으로 VPS를 사면 정말 익명인가에서 솔직하게 다루었고, Monero로 충전하는 것은 그보다 한 걸음 더 나아간다. 각 계층은 홀로는 무너지지만, 함께라면 버텨낸다.
백업: 하드닝이 대신해 줄 수 없는 부분
어떤 방화벽 규칙도 삭제된 데이터베이스나 고장 난 디스크, 그때는 합리적으로 보였던 설정 변경으로부터는 지켜주지 못한다. 백업은 스스로 저지른 실수로부터 당신을 구해주는 유일한 대책이며, 통계적으로 잘 보안된 머신에서 데이터를 잃는 가장 유력한 경로가 바로 그것이다. 서버를 벗어나기 전에 암호화하고, 최소 한 벌은 서버 자체가 닿을 수 없는 곳에 보관하며, 기억날 때가 아니라 정해진 일정에 맞춰 실행되게 하라.
마지막 항목은 들리는 것보다 훨씬 중요하다: 서버가 덮어쓸 수 있는 백업 프로세스는 그 서버에서 root 권한을 가진 무엇이든 파괴할 수 있는 백업이기도 하다. 암호화한 아카이브를 별도의 스토리지 서버로 밀어 넣거나 — 혹은 자격 증명이 백업 대상 머신 밖에 머물도록 그쪽에서 끌어오는 방식으로 — 머신이 사라져도 살아남는 사본을 확보하라. 그리고 아무 문제도 없을 때 일부러 한 번쯤 복원을 직접 해보라. 테스트해보지 않은 백업은 안전망이 아니라 그저 가설일 뿐이다.
조용히 공든 탑을 무너뜨리는 실수들
- 키를 테스트하기도 전에 비밀번호 인증부터 꺼버린 다음, 더 이상 로그인할 수 없는 노트북 앞에서야 실수를 깨닫는 경우.
- SSH를 허용하기 전에 방화벽부터 켜버리는 경우 — 반대 방향에서 도달한 같은 잠금이다.
- IPv4 규칙만 작성하고, 박스의 IPv6 주소에서는 모든 서비스가 그대로 닿을 수 있게 방치하는 경우.
- 첫날에는 꼼꼼히 하드닝해 놓고, 이후 컨트롤 패널과 데이터베이스, 모니터링 스택을 설치하면서 저마다 묻지도 않고 자기 포트를 열어버리는 경우.
- sudo 한 번 덜 치려고 모든 것을 root로 돌리는 경우 — 그중 무엇 하나에서라도 결함이 나오면 곧바로 전체가 뚫린다.
- fail2ban을 키의 대체품으로 여기는 경우 — 그저 속도만 느려졌을 뿐인 비밀번호 추측 게임을 계속 돌아가게 내버려 두는 셈이다.
- 디스크를 암호화하면 대여한 서버를 호스트로부터도 감출 수 있다고 착각하는 경우 — 전체 디스크 암호화는 도난당한 드라이브를 지켜줄 뿐, 가동 중인 머신은 지켜주지 못한다.
- 재부팅을 아예 하지 않는 경우 — 몇 달치 커널 패치가 디스크에 설치된 채 쌓여 있는 동안 예전 커널이 계속 돌아간다.
이 중 어느 것도 어렵지 않고, 오래 걸리지도 않는다. 순서대로 진행하고, 다음 단계로 넘어가기 전에 매번 두 번째 터미널에서 확인하면, 몇 분 전에 배포한 그 머신은 더 이상 주소 공간을 훑는 누구에게나 열린 기회가 아니게 된다. 그런 다음에야 애초에 하려던 것 — 웹사이트든, 노드든, 터널이든, 프라이빗 모델 엔드포인트든 — 을 이제는 근거를 갖고 이해할 수 있는 토대 위에서 배포하라.