‘내 서버’가 된 순간, 모든 건 내 책임이다
지난 시간, 나는 리눅스 CLI와 권한(Permission)이라는 벽을 넘었다. chmod, chown, 그리고 절대로 하면 안 되는 chmod 777까지. 파일 하나에 대한 접근 제어는 어느 정도 감을 잡았다.
하지만 그때의 나는 큰 착각을 하고 있었다. ‘파일 권한’만 잘 관리하면 서버가 안전하다고 생각한 것이다.
“서버 보안? 그건 보안팀이 하는 거 아닌가요?”
대기업이라면 그럴 수 있다. 하지만 나는 1인 개발자다. 서버 관리자도, 보안 담당자도, 네트워크 엔지니어도 전부 ‘나’였다. 보안의 모든 책임은 내 어깨 위에 올라와 있었다.
그리고 그 사실을 깨닫게 해준 건, 어느 월요일 아침에 발견한 서버 접속 로그였다.

위기: 누군가 내 서버 문을 두드리고 있었다
월요일 아침, 습관처럼 서버에 SSH로 접속해서 로그를 확인하던 중 이상한 걸 발견했다.
# 최근 SSH 접속 실패 기록 확인
$ grep "Failed password" /var/log/auth.log | tail -20
화면에 올라온 로그를 보고 소름이 돋았다.
Feb 10 03:14:22 sshd: Failed password for root from 185.220.xx.xx
Feb 10 03:14:25 sshd: Failed password for root from 185.220.xx.xx
Feb 10 03:14:27 sshd: Failed password for admin from 185.220.xx.xx
Feb 10 03:14:30 sshd: Failed password for ubuntu from 185.220.xx.xx
...
새벽 3시부터 수백 건의 로그인 시도. IP는 해외. 계정 이름은 root, admin, ubuntu를 돌아가며 시도하고 있었다. 이건 누군가가 ‘자동화 프로그램(봇)’을 돌려 비밀번호를 무차별 대입(Brute Force)하고 있었던 것이다.
“어? 나 아직 해킹당한 건 아니지…?”
다행히 뚫리지는 않았다. 하지만 문 앞에서 열쇠를 하나하나 돌려보고 있는 도둑 앞에서 "안 뚫렸으니 괜찮아"라고 말할 수 있을까? 나는 당장 서버의 보안 상태를 점검하기 시작했다.

1단계: 현관문 잠그기 — SSH 보안 강화
서버의 현관문은 SSH(Secure Shell)다. 내가 서버에 접속할 때 쓰는 바로 그것. 문제는 해커도 같은 문으로 들어오려고 한다는 것이다. 가장 먼저 이 현관문을 강화해야 한다.
SSH 포트 변경 (기본 22번 → 다른 번호)
SSH의 기본 포트는 22번이다. 해커의 자동 스캐너는 가장 먼저 22번 포트를 두드린다. 포트 번호를 바꾸는 것만으로도 무차별 공격의 90% 이상을 걸러낼 수 있다.
# SSH 설정 파일 열기
$ sudo vi /etc/ssh/sshd_config
# 포트 번호 변경 (기본 22 → 예: 2222)
Port 2222
# SSH 서비스 재시작
$ sudo systemctl restart sshd
비유하자면, 도둑은 보통 1층 정문(22번)을 노린다. 입구를 3층 옥상문(2222번)으로 옮기면 도둑 대부분은 문 위치조차 찾지 못한다.
root 계정 직접 로그인 차단
로그를 보면 해커들이 가장 먼저 시도하는 계정이 ‘root’다. root는 리눅스의 최고 관리자 계정이니, 이걸 뚫으면 서버 전체를 장악할 수 있기 때문이다.
# SSH 설정 파일에서 root 직접 로그인을 막는다
$ sudo vi /etc/ssh/sshd_config
PermitRootLogin no
이제 root로는 SSH 접속 자체가 불가능하다. 일반 계정으로 로그인한 뒤, 필요할 때만 sudo로 권한을 올리면 된다.
비밀번호 대신 SSH 키 인증
비밀번호가 아무리 복잡해도, 자동 프로그램이 수만 가지 조합을 시도하면 언젠가 뚫릴 수 있다. 가장 확실한 방법은 비밀번호 로그인 자체를 끄고, ‘SSH 키(Key)’로만 접속하는 것이다.
# 내 컴퓨터(로컬)에서 SSH 키 쌍 생성
$ ssh-keygen -t rsa -b 4096
# 공개 키를 서버에 복사
$ ssh-copy-id -p 2222 user@my-server-ip
# 서버에서 비밀번호 로그인 비활성화
$ sudo vi /etc/ssh/sshd_config
PasswordAuthentication no
SSH 키 인증은 ‘열쇠’가 아니라 ‘지문 인식’이다. 비밀번호(열쇠)는 복제할 수 있지만, 내 컴퓨터에만 있는 개인 키(지문)는 복제가 사실상 불가능하다.
2단계: 담장 세우기 — 방화벽(Firewall)
현관문(SSH)을 강화했다면, 이제 건물 주변에 담장을 치고 출입구를 통제할 차례다. 이것이 ‘방화벽(Firewall)’이다.
서버에는 총 65,535개의 포트(출입구)가 있다. 방화벽 없이는 이 6만 개 문이 모두 열려 있는 셈이다. 필요한 문만 열고, 나머지는 전부 잠가야 한다.
리눅스에서 가장 쉽게 쓸 수 있는 방화벽 도구는 ‘UFW(Uncomplicated Firewall)’다.
# UFW 활성화
$ sudo ufw enable
# 기본 정책: 들어오는 건 전부 막고, 나가는 건 허용
$ sudo ufw default deny incoming
$ sudo ufw default allow outgoing
# 필요한 포트만 열기
$ sudo ufw allow 2222/tcp # SSH (변경한 포트)
$ sudo ufw allow 80/tcp # HTTP
$ sudo ufw allow 443/tcp # HTTPS
# 현재 방화벽 상태 확인
$ sudo ufw status
| 포트 | 용도 | 개방 여부 |
|---|---|---|
| 2222 | SSH (변경된 포트) | ✅ 허용 |
| 80 | HTTP (웹) | ✅ 허용 |
| 443 | HTTPS (보안 웹) | ✅ 허용 |
| 22 | SSH (기본, 더 이상 사용 안 함) | ❌ 차단 |
| 나머지 | 전부 | ❌ 차단 |
6만 개의 문 중 딱 3개만 열어두고 나머지는 벽돌로 막아버렸다. 도둑이 두드릴 문 자체가 사라진 것이다.

3단계: 통신 암호화 — HTTPS
서버가 웹 서비스를 제공한다면, 사용자와 서버 사이의 통신도 보호해야 한다. 이것이 ‘HTTPS’다.
HTTP는 데이터를 ‘엽서’처럼 그냥 보내는 것이다. 중간에 누구든 내용을 엿볼 수 있다. HTTPS는 데이터를 ‘밀봉된 편지’에 넣어서 보내는 것이다. 중간에 가로채도 내용을 읽을 수 없다.
사용자가 로그인 폼에 입력한 아이디/비밀번호가 HTTP로 전송된다면? 와이파이를 공유하는 카페의 누군가가 그 내용을 그대로 볼 수 있다.
실무에서 HTTPS를 적용하는 가장 쉬운 방법은 ‘Let’s Encrypt’로 무료 SSL 인증서를 발급받는 것이다.
# Certbot 설치 (Let's Encrypt 자동화 도구)
$ sudo apt install certbot python3-certbot-nginx
# 인증서 발급 및 Nginx에 자동 적용
$ sudo certbot --nginx -d mydomain.com
# 자동 갱신 테스트 (인증서는 90일마다 갱신 필요)
$ sudo certbot renew --dry-run
이 명령어 몇 줄이면 내 서비스의 주소가 http://에서 https://로 바뀌고, 브라우저 주소창에 자물쇠 아이콘이 표시된다. 사용자에게 "이 사이트는 안전합니다"라는 신뢰를 주는 것이다.
4단계: 자동 방어 시스템 — Fail2Ban
SSH 포트를 바꾸고, 키 인증을 설정해도, 세상에는 끈질긴 봇이 존재한다. 변경된 포트를 찾아내고 접속을 시도하는 놈들이 반드시 있다.
이럴 때 유용한 것이 ‘Fail2Ban’이다. 일정 횟수 이상 로그인에 실패한 IP를 자동으로 차단해주는 도구다.
# Fail2Ban 설치
$ sudo apt install fail2ban
# SSH 보호 설정
$ sudo vi /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 2222
maxretry = 5
bantime = 3600
findtime = 600
| 설정 | 의미 |
|---|---|
| maxretry = 5 | 5번 틀리면 |
| bantime = 3600 | 1시간 동안 해당 IP를 차단한다 |
| findtime = 600 | 10분 이내에 5번 실패 시 적용 |
# Fail2Ban 시작
$ sudo systemctl enable fail2ban
$ sudo systemctl start fail2ban
# 현재 차단된 IP 확인
$ sudo fail2ban-client status sshd
Fail2Ban은 현관문 앞에 세워둔 ‘CCTV + 자동 잠금장치’다. 수상한 사람이 비밀번호를 여러 번 틀리면, 자동으로 출입을 금지시킨다.

실무 조언: 보안 체크리스트
지금까지 설정한 내용을 체크리스트로 정리한다. 서버를 새로 세팅할 때마다 이 목록을 따라가면 기본적인 방어막은 갖출 수 있다.
| # | 항목 | 명령어/설정 |
|---|---|---|
| 1 | SSH 포트 변경 | Port 2222 in sshd_config |
| 2 | root 로그인 차단 | PermitRootLogin no |
| 3 | SSH 키 인증 전환 | PasswordAuthentication no |
| 4 | 방화벽 활성화 | ufw enable, 필요한 포트만 허용 |
| 5 | HTTPS 적용 | Let’s Encrypt + Certbot |
| 6 | Fail2Ban 설치 | 로그인 실패 시 자동 IP 차단 |
| 7 | 정기 업데이트 | sudo apt update && sudo apt upgrade |
7번이 의외로 중요하다. 보안 취약점은 매일같이 발견된다. 아무리 방화벽을 세워도 OS나 소프트웨어 자체에 구멍이 있으면 소용없다. 정기적인 업데이트는 가장 기본적이면서도 가장 효과적인 보안 조치다.
마치며: 보안은 특별한 것이 아니라, 습관이다
처음에는 보안이라는 단어가 거창하게 느껴졌다. 해킹 방어, 침입 탐지, 암호화… 영화에서나 나올 법한 이야기라고 생각했다.
하지만 실제로 해보니 대단한 기술이 필요한 게 아니었다. SSH 포트 바꾸고, 방화벽 켜고, 키 인증 설정하고, Fail2Ban 깔기. 명령어 몇 줄이면 끝나는 일이었다. 중요한 건 ‘아는 것’이 아니라 ‘실행하는 것’이었다.
보안 사고는 고도의 해킹 기술이 아니라, 기본적인 조치를 하지 않은 서버에서 벌어진다.
나는 그날 이후로, 서버를 새로 세팅할 때마다 코드를 올리기 전에 반드시 이 보안 체크리스트를 먼저 실행한다. 문을 열기 전에 자물쇠부터 확인하는 것. 그것이 서버를 지키는 가장 기본적인 자세다.
지금까지 우리는 리눅스라는 야생에서 살아남기 위한 기초 체력(CLI, 권한, 보안)을 길렀다. 그런데 여전히 근본적인 문제가 남아 있다. 내 노트북(윈도우)과 리눅스 서버의 환경이 다르다는 것. 자바 버전도 다르고, 라이브러리도 다르다.
"내 컴퓨터에선 되는데요?"라는 변명을 영원히 끝내버릴 기술이 있다. 다음 시간에는 환경 차이를 아예 없애버리는 ‘가상화와 컨테이너’에 대해 알아보자.