서버 세우고 30분 안에 해야 할 보안 5가지

3편에서 500 오류를 잡고 사이트가 IP로 떴습니다. 여기서 바로 도메인 연결로 넘어가고 싶은 마음을 한 번만 눌러주세요. 서버는 켜진 순간부터 공격받고 있습니다. 비유가 아니라 사실입니다. 제 서버를 만든 지 하루도 안 돼서 로그를 봤더니 이랬습니다.

sudo grep "Failed password" /var/log/auth.log | wc -l

세 자리 숫자가 나왔습니다. 아무한테도 IP를 알려준 적 없는데요. 공개 IP는 전 세계 스캐너가 24시간 훑고 다닙니다. 접속해서 root / admin / ubuntu에 흔한 비밀번호를 넣어보는 봇들입니다. 이들은 제 블로그에 관심이 없습니다. 그냥 열려 있는 문을 찾을 뿐입니다.

그래서 도메인을 붙이기 전에 30분을 씁니다. 다섯 가지고, 순서가 중요합니다. 특히 두 번째는 우분투 24.04에서 대부분의 튜토리얼대로 하면 안 먹힙니다. 제가 그렇게 당했습니다.

#할 일막는 공격시간
1SSH 키 인증(ed25519) 설정비밀번호 추측10분
2비밀번호 로그인 차단무차별 대입 전체5분
3root 직접 로그인 차단최고권한 직행2분
4자동 보안 업데이트알려진 취약점5분
5Fail2ban 설치반복 시도·로그 오염8분

SSH 키 인증 — 비밀번호를 아예 없앤다

비밀번호는 아무리 복잡해도 맞힐 수 있는 것입니다. 봇은 지치지 않고 초당 수십 번 시도합니다. 키는 다릅니다. 맞히는 게 아니라 갖고 있거나 없거나입니다.

비밀번호SSH 키
공격 방식계속 찍어보면 언젠가 맞음찍는 게 불가능
유출 경로재사용, 피싱, 어깨너머내 PC가 털려야 함
서버에 저장되는 것해시(그래도 대조 가능)공개키만 — 털려도 무의미
입력매번 타이핑자동

키 방식의 핵심은 개인키가 서버에 절대 올라가지 않는다는 점입니다. 서버엔 공개키만 둡니다. 서버가 통째로 털려도 공개키로는 아무것도 못 합니다.

키 종류는 ed25519를 씁니다.

RSA 2048/4096ed25519
키 길이길다 (수백 자)짧다 (한 줄)
속도느림빠름
안전성길이를 늘려야 확보기본으로 충분
호환성어디서나OpenSSH 6.5+ (2014년 이후)
추천아주 오래된 장비용기본값으로 이것

내 PC에서 키를 만듭니다. 서버가 아닙니다. 개인키가 내 PC를 떠나면 안 되니까요.

ssh-keygen -t ed25519 -C "mj@ddlabx"

저장 경로는 기본값(엔터), 암호구문(passphrase)은 넣는 걸 권합니다. 노트북을 잃어버렸을 때 마지막 방어선입니다. 윈도우면 C:\Users\사용자명\.ssh\에 두 파일이 생깁니다.

파일정체취급
id_ed25519개인키절대 밖으로. 백업은 오프라인으로
id_ed25519.pub공개키서버에 올리는 것. 공개돼도 무방

주의 — 윈도우 PowerShell에는 ssh-copy-id가 없습니다. 리눅스·맥 기준으로 쓰인 튜토리얼을 그대로 따라 하면 “명령을 찾을 수 없습니다”가 납니다. 아래 방법을 쓰세요.

PowerShell에서 공개키를 서버에 등록하는 한 줄입니다.

type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh mj@서버IP "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

권한(700/600)이 중요합니다. SSH는 .ssh 폴더나 authorized_keys가 남에게 열려 있으면 보안상 이유로 키를 아예 무시합니다. “키를 분명히 넣었는데 계속 비밀번호를 묻는” 사례의 대부분이 이 권한 문제입니다.

이제 새 창에서 확인합니다. 기존 창은 닫지 마세요.

ssh mj@서버IP

비밀번호를 안 묻고 바로 들어가면 성공입니다.

비밀번호 로그인 차단 — 여기서 다들 속습니다

키가 되니까 이제 비밀번호를 막습니다. 튜토리얼은 하나같이 이렇게 시킵니다.

sudo nano /etc/ssh/sshd_config
# PasswordAuthentication no 로 수정
sudo systemctl restart ssh

그리고 비밀번호 로그인이 여전히 됩니다. 저는 여기서 30분을 날렸습니다. 설정을 몇 번을 다시 봐도 no인데 왜 되는지 이해가 안 갔어요.

범인은 두 가지가 겹친 것이었습니다.

첫째, Ubuntu 24.04의 /etc/ssh/sshd_config파일 맨 위에 이 줄이 있습니다.

Include /etc/ssh/sshd_config.d/*.conf

둘째, OpenSSH의 설정 규칙은 “먼저 나온 값이 이긴다”입니다. 대부분의 설정 파일이 “나중 것이 덮어쓴다”인 것과 반대예요. 그러니까 맨 위에서 불러온 파일의 값이 최종값이 되고, 아래에서 내가 고친 건 읽히지도 않습니다.

그리고 Vultr 같은 클라우드 이미지는 cloud-init이 부팅할 때 이런 파일을 몰래 만들어 둡니다.

/etc/ssh/sshd_config.d/50-cloud-init.conf
  → PasswordAuthentication yes

즉 내 no는 처음부터 진 싸움이었습니다. 먼저 진짜 어디에 뭐가 적혀 있는지 찾습니다.

sudo grep -ri passwordauthentication /etc/ssh/sshd_config /etc/ssh/sshd_config.d/

50-cloud-init.conf가 나오면 그 파일을 고칩니다.

sudo nano /etc/ssh/sshd_config.d/50-cloud-init.conf
PasswordAuthentication no

참고 — “번호가 큰 파일을 새로 만들면 덮어쓰지 않나?” 아닙니다. 60-hardening.conf를 만들어도 50번이 먼저 읽히니 50번이 이깁니다. 굳이 별도 파일로 하려면 01-hardening.conf처럼 번호를 더 낮게 줘야 합니다. 이 함정이 진짜 고약합니다.

그리고 우분투 24.04는 SSH 재시작 방법도 다릅니다. 22.10부터 SSH가 소켓 활성화(socket activation) 방식으로 바뀌었습니다. 평소엔 sshd가 떠 있지 않다가 접속이 들어오면 그때 뜹니다. 메모리를 아끼는 구조인데, 설정 반영 방식이 달라집니다.

sudo sshd -t
sudo systemctl daemon-reload
sudo systemctl restart ssh.socket

daemon-reload가 빠지면 소켓 설정이 재생성되지 않습니다. 포트를 바꿀 때도 ssh.service가 아니라 ssh.socket을 재시작해야 합니다.

가장 중요한 건 검증입니다. 설정 파일을 눈으로 보지 말고, sshd에게 직접 물어보세요. 이 명령은 모든 파일을 합친 최종 결과를 보여줍니다.

sudo sshd -T | grep -i passwordauthentication

passwordauthentication no가 나와야 진짜 꺼진 겁니다. 이 명령을 몰랐다면 저는 아직도 헤매고 있었을 겁니다.

root 직접 로그인 차단

공격자 입장에서 로그인은 아이디 + 비밀번호 두 개를 맞히는 문제입니다. 그런데 root모든 리눅스에 반드시 있고 이름이 정해져 있습니다. 즉 절반을 공짜로 주는 셈입니다. 봇들이 root부터 두드리는 이유가 이겁니다.

2편에서 mj 계정을 만들고 sudo를 준 게 이걸 위한 밑작업이었습니다. 이제 root 문을 닫습니다.

sudo grep -ri permitrootlogin /etc/ssh/sshd_config /etc/ssh/sshd_config.d/

찾은 파일에서 값을 바꿉니다.

PermitRootLogin no
의미평가
yes비밀번호로도 root 접속 가능최악
prohibit-password키로만 root 접속 가능 (24.04 기본값)보통 — root 이름은 여전히 유효
noroot 직접 접속 완전 차단권장 — sudo로 쓰면 됩니다
sudo sshd -t && sudo systemctl daemon-reload && sudo systemctl restart ssh.socket
sudo sshd -T | grep -i permitrootlogin

주의 — 여기서도 원칙은 같습니다. 지금 접속창을 절대 닫지 말고 새 창에서 ssh mj@서버IP가 되는지 먼저 확인하세요. 확인 전에 창을 닫았다가 설정이 잘못됐으면, 그때부터는 Vultr 웹콘솔로 들어가야 합니다.

자동 보안 업데이트 — 게으름을 인정하는 설정

솔직해집시다. 매일 apt upgrade를 칠 사람이 몇이나 될까요. 저는 못 합니다. 서버 취약점 대부분은 제로데이가 아니라 패치가 나온 지 몇 달 지났는데 아무도 안 올린 것입니다. 그러니 자동화합니다.

sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades

파란 화면에서 Yes. 이걸로 보안 업데이트만 자동 설치됩니다. 일반 패키지 업그레이드는 건드리지 않아요. 설정이 마음대로 바뀔 걱정은 안 해도 됩니다.

cat /etc/apt/apt.conf.d/20auto-upgrades

Unattended-Upgrade "1";이 보이면 켜진 겁니다. 동작 기록은 여기 쌓입니다.

sudo tail -20 /var/log/unattended-upgrades/unattended-upgrades.log

참고 — 커널이 업데이트되면 재부팅해야 적용됩니다. 자동 재부팅은 기본적으로 꺼져 있어요. /var/run/reboot-required 파일이 있으면 재부팅이 필요하다는 뜻입니다. 개인 블로그면 한 달에 한 번쯤 확인해서 수동 재부팅하는 정도로 충분합니다.

Fail2ban — 반복해서 두드리면 문을 닫는다

키 인증만 켰으면 사실 비밀번호 공격은 이미 무의미합니다. 그런데도 봇은 계속 두드리고, 그 시도가 전부 로그에 쌓입니다. 로그가 오염되면 진짜 문제가 생겼을 때 그 줄을 못 찾습니다. 3편에서 로그가 얼마나 중요한지 봤죠. Fail2ban은 그 로그를 지켜주는 장치이기도 합니다.

sudo apt install fail2ban -y

기본 설정 파일(jail.conf)은 직접 고치지 않습니다. 업데이트 때 덮어써지거든요. jail.local을 새로 만듭니다. 이게 우선합니다.

sudo nano /etc/fail2ban/jail.local
[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 3

[sshd]

enabled = true

항목이 설정에서는
findtime세는 시간 창10분 안에
maxretry허용 실패 횟수3번 틀리면
bantime차단 기간1시간 차단
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd

이 확인을 꼭 하세요. “설치했으니 되겠지” 하고 넘어가면 안 됩니다. Currently banned 숫자가 보이면 정상 작동 중입니다. 며칠 뒤에 다시 보면 차단된 IP가 쌓여 있을 겁니다. 그게 다 나를 노렸던 시도들입니다.

참고fail2ban-client status sshd에서 오류가 나거나 jail을 못 찾는다고 하면, jail.local[sshd] 아래에 backend = systemd를 추가하고 재시작해 보세요. 로그를 어디서 읽을지 못 정해서 생기는 문제입니다.

SSH 포트를 바꾸는 건 보안일까

많이들 22번 포트를 2222 같은 걸로 바꾸라고 합니다. 솔직하게 말하면 이건 보안이 아닙니다. 포트 스캔 한 번이면 다 드러납니다. 자물쇠를 바꾼 게 아니라 문을 옆으로 옮긴 것뿐이에요.

그럼 무의미하냐 — 그건 아닙니다. 로그가 조용해집니다. 자동화된 봇 대부분은 22번만 훑고 지나가거든요. 로그 소음이 줄면 진짜 이상 신호가 눈에 띕니다. 그게 유일하고 진짜인 이득입니다.

얻는 것못 얻는 것비용
포트 변경로그 소음 감소실질 보안 (스캔에 다 걸림)접속 때마다 -p, 방화벽 재설정
키 인증 + 비번 차단무차별 대입 원천 차단초기 10분

우선순위가 명확합니다. 1~3번을 먼저 하세요. 포트 변경은 그 다음에 취향껏입니다. 게다가 24.04에서는 앞서 말한 ssh.socket 때문에 포트 변경이 한 단계 더 까다롭습니다. sshd_config에서 Port를 바꾼 뒤 반드시 daemon-reloadssh.socket을 재시작해야 하고, UFW에서 새 포트를 열어두는 걸 잊으면 그대로 잠깁니다.

자주 묻는 질문

Q. 개인키를 잃어버리면 서버에 영영 못 들어가나요?

A. SSH로는 못 들어갑니다. 하지만 Vultr 웹콘솔이 있습니다. 대시보드에서 브라우저로 서버 화면에 직접 접속하는 기능인데, SSH를 거치지 않습니다. 여기로 들어가서 새 공개키를 authorized_keys에 넣으면 됩니다. 그러려면 root나 mj 계정의 비밀번호는 알고 있어야 하니, 비밀번호 로그인을 껐더라도 비밀번호 자체는 비밀번호 관리자에 보관해 두세요.

Q. 이 5가지면 충분한가요?

A. 아닙니다. “서버 세우고 30분 안에”에 해당하는 최소한입니다. 침입 경로의 큰 덩어리를 막을 뿐이에요. 워드프레스 자체 보안(관리자 계정명, 로그인 시도 제한, 플러그인 최신화)은 별개 주제고, 백업은 그 무엇보다 중요합니다. 보안의 마지막 보루는 언제나 복구 가능한 백업입니다.

Q. UFW는 왜 없나요?

A. 2편에서 이미 했기 때문입니다. sudo ufw status로 확인해 보세요. OpenSSHNginx Full만 있으면 정상입니다. 뭔가 더 열려 있다면 왜 열었는지 스스로 설명할 수 있어야 합니다. 설명 못 하면 닫으세요.

Q. sudo 쓸 때 비밀번호 안 묻게 하면 편하지 않나요?

A. 편하고, 위험합니다. 그 순간 SSH 세션 하나만 뚫리면 곧바로 root입니다. sudo 비밀번호는 마지막 확인 절차예요. 하루에 몇 번 치는 게 그렇게 큰 비용은 아닙니다.

Q. 이미 공격받고 있는지 어떻게 아나요?

A. 이 두 줄이면 대충 보입니다. 숫자를 보면 왜 이걸 해야 하는지 바로 납득이 됩니다.

sudo grep "Failed password" /var/log/auth.log | wc -l
sudo grep "Accepted" /var/log/auth.log | tail -5

두 번째 줄은 성공한 로그인입니다. 여기에 내가 모르는 접속이 있으면 그건 다른 차원의 문제입니다.

정리하면

  • 서버는 켜진 순간부터 스캔당합니다. grep "Failed password" /var/log/auth.log | wc -l로 직접 확인해 보세요.
  • ed25519 키 인증이 1순위입니다. 개인키는 내 PC를 떠나지 않습니다.
  • 윈도우에는 ssh-copy-id가 없습니다. PowerShell 파이프 방식을 쓰세요.
  • Ubuntu 24.04의 함정sshd_config.d/50-cloud-init.confPasswordAuthentication yes를 먼저 선언합니다. OpenSSH는 먼저 나온 값이 이깁니다. 그 파일을 직접 고치세요.
  • 설정은 눈으로 믿지 말고 sudo sshd -T | grep -i passwordauthentication으로 최종값을 확인하세요.
  • 24.04는 SSH가 소켓 활성화입니다. daemon-reloadssh.socket을 재시작합니다.
  • 포트 변경은 보안이 아니라 로그 소음 감소입니다. 순서를 착각하지 마세요.
  • 모든 SSH 설정 변경은 기존 창을 열어둔 채 새 창에서 검증합니다.

문을 잠갔으니 이제 간판을 답니다. 다음 편은 가비아에서 산 도메인을 이 서버로 연결하는 A레코드 설정과 DNS 전파 이야기입니다.

댓글 남기기