본문 바로가기
Tech/Dev Log

SSH 키 인증 + Fail2Ban + 방화벽으로 홈서버 보안 강화하기 [Mac Mini 홈서버 완전 정복 7/10]

by Hoft 2026. 3. 30.

Mac Mini 홈서버 보안을 SSH 키 인증, Fail2Ban, macOS 방화벽(pf)으로 강화하는 방법을 단계별로 정리합니다. Docker 네트워크 보안과 보안 체크리스트까지 한번에 확인하세요.

🖥️ Mac Mini 홈서버 완전 정복 시리즈

7/10 — 보안 강화

SSH 키 인증 + Fail2Ban + 방화벽 셋업

6편까지 Cloudflare Tunnel을 통해 홈서버를 외부에 공개하는 데 성공했습니다. 서비스가 인터넷에 노출되는 순간, 보안은 선택이 아니라 필수가 됩니다. SSH 브루트포스 공격은 서버를 공개한 지 수 분 만에 시작되고, 방치하면 하루에 수천 건의 로그인 시도가 쌓입니다. 이 글에서는 Mac Mini 홈서버의 보안을 SSH 키 인증 → Fail2Ban → macOS 방화벽(pf) → Docker 네트워크 보안 4단계로 강화합니다. 모든 설정은 2편에서 구축한 macOS Sequoia 환경과 3편의 OrbStack 기반 Docker 환경을 전제로 합니다.

📌 이런 분들에게 추천합니다

  • 홈서버를 외부에 공개했거나 공개할 예정인 분
  • SSH 접속을 패스워드 대신 키 인증으로 전환하고 싶은 분
  • 자동화된 브루트포스 방어 시스템을 구축하고 싶은 분
  • macOS 기반 서버의 방화벽 설정이 궁금한 분

⚠️ 사전 준비 — 이 글은 시리즈 2편(macOS 초기 세팅)에서 SSH(Remote Login)가 활성화되어 있고, 3편에서 OrbStack이 설치된 상태를 전제합니다. 6편(Cloudflare Tunnel)까지 완료한 상태라면 가장 이상적입니다.

1. SSH 키 인증 설정 + 패스워드 로그인 차단

SSH 보안의 첫 번째이자 가장 중요한 단계는 패스워드 로그인을 완전히 차단하고 키 인증만 허용하는 것입니다. 패스워드는 브루트포스로 뚫릴 수 있지만, Ed25519 키 페어는 현실적으로 뚫을 수 없습니다.

1-1. 클라이언트에서 SSH 키 생성

Mac Mini에 접속할 클라이언트 컴퓨터(내 작업용 Mac, 노트북 등)에서 키를 생성합니다.

# Ed25519 키 생성 (현재 가장 권장되는 알고리즘)
ssh-keygen -t ed25519 -C "my-macmini-homeserver"

# 저장 경로: 기본값(~/.ssh/id_ed25519) 엔터
# 패스프레이즈: 설정 강력 권장 (키 파일 탈취 시 2차 방어)

💡 Ed25519 vs RSA — Ed25519는 RSA 4096보다 키 길이가 짧으면서 동등 이상의 보안 강도를 제공하고, 서명/검증 속도도 빠릅니다. 특별한 이유(레거시 호환 등)가 없다면 Ed25519를 사용하세요.

1-2. Mac Mini 서버에 공개키 복사

# 방법 1: ssh-copy-id 사용 (가장 간단)
ssh-copy-id -i ~/.ssh/id_ed25519.pub username@mac-mini-ip

# 방법 2: 수동 복사 (ssh-copy-id가 없는 경우)
cat ~/.ssh/id_ed25519.pub | ssh username@mac-mini-ip \
  "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

복사 후 패스워드 없이 접속되는지 테스트합니다.

# 키 인증 테스트 — 패스워드 프롬프트 없이 접속되면 성공
ssh -i ~/.ssh/id_ed25519 username@mac-mini-ip

1-3. 패스워드 로그인 비활성화

키 인증이 확인됐으면, 이제 패스워드 로그인을 차단합니다. macOS Sequoia에서는 /etc/ssh/sshd_config.d/ 디렉토리에 커스텀 설정 파일을 만드는 것이 가장 안전합니다. /etc/ssh/sshd_config를 직접 수정하면 macOS 업데이트 시 초기화될 수 있기 때문입니다.

# Mac Mini 서버에서 실행
# sshd_config.d 디렉토리 확인 (Sequoia에는 기본 존재)
ls /etc/ssh/sshd_config.d/

# 커스텀 보안 설정 파일 생성
sudo nano /etc/ssh/sshd_config.d/01-security.conf

다음 내용을 입력합니다.

# /etc/ssh/sshd_config.d/01-security.conf
# Mac Mini 홈서버 SSH 보안 설정

# 패스워드 로그인 완전 차단
PasswordAuthentication no
KbdInteractiveAuthentication no

# 공개키 인증 활성화
PubkeyAuthentication yes

# root 로그인 차단
PermitRootLogin no

# 빈 패스워드 차단
PermitEmptyPasswords no

# 최대 인증 시도 횟수 제한
MaxAuthTries 3

# 최대 동시 세션 수 제한
MaxSessions 5

# 인증 대기 시간 (30초 안에 인증 못하면 연결 끊김)
LoginGraceTime 30

⚠️ 매우 중요 — 패스워드 로그인을 비활성화하기 전에 반드시 키 인증 접속이 정상 작동하는지 확인하세요. 키를 못 찾는 상태에서 패스워드를 차단하면 SSH 접속이 완전히 차단됩니다. 만약의 경우를 위해 Mac Mini에 물리적으로 접근(모니터+키보드)할 수 있는 환경을 유지하세요.

설정을 저장한 후 SSH 데몬을 재시작합니다.

# 설정 문법 검증 (에러 없어야 함)
sudo sshd -T | grep -i "passwordauthentication\|pubkeyauthentication\|kbdinteractive"

# SSH 서비스 재시작 (macOS 방식)
sudo launchctl unload /System/Library/LaunchDaemons/ssh.plist
sudo launchctl load -w /System/Library/LaunchDaemons/ssh.plist

검증 방법 — 기존 SSH 세션은 유지한 상태에서, 새 터미널을 열어 패스워드로 접속을 시도합니다. ssh -o PubkeyAuthentication=no username@mac-mini-ip 명령어로 테스트하면 Permission denied가 뜨면 정상입니다.

2. SSH 추가 보안 — 포트 변경, 접속 제한, 타임아웃

키 인증으로 기본 보안을 확보했다면, 추가 설정으로 공격 표면을 더 줄일 수 있습니다.

2-1. SSH 포트 변경 (선택 사항)

기본 포트 22를 변경하면 자동화된 봇 스캔을 대부분 회피할 수 있습니다. 보안 전문가들 사이에서는 "security through obscurity(불분명함에 의한 보안)"이라고 논쟁이 있지만, 실제로 로그에 찍히는 무작위 접속 시도를 극적으로 줄여줍니다.

# /etc/ssh/sshd_config.d/01-security.conf에 추가
Port 2222  # 1024~65535 사이의 원하는 포트

💡 Cloudflare Tunnel 사용자라면 — 6편에서 설정한 Cloudflare Tunnel을 통해 SSH를 노출하고 있다면, Tunnel 설정에서 내부 서비스 주소의 포트도 함께 변경해야 합니다. 다만 Tunnel 자체가 포트를 외부에 직접 노출하지 않으므로, 로컬 네트워크에서의 접근만 고려하면 됩니다.

2-2. 접속 허용 사용자 제한

# /etc/ssh/sshd_config.d/01-security.conf에 추가
# 특정 사용자만 SSH 접속 허용
AllowUsers yourusername

# 또는 특정 IP 대역에서만 허용 (LAN 전용)
# AllowUsers yourusername@192.168.1.*

2-3. 유휴 세션 타임아웃

# /etc/ssh/sshd_config.d/01-security.conf에 추가
# 5분(300초)마다 클라이언트에 keepalive 전송
ClientAliveInterval 300

# 3번 응답 없으면 연결 종료 (= 15분 유휴 시 자동 끊김)
ClientAliveCountMax 3

2-4. 완성된 01-security.conf 전체

# /etc/ssh/sshd_config.d/01-security.conf
# Mac Mini 홈서버 SSH 보안 설정 (통합)

# 포트 변경 (선택)
# Port 2222

# 인증 방식
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
PermitEmptyPasswords no

# 접속 제한
AllowUsers yourusername
MaxAuthTries 3
MaxSessions 5
LoginGraceTime 30

# 유휴 타임아웃
ClientAliveInterval 300
ClientAliveCountMax 3

# X11/Agent 포워딩 비활성화 (서버에서 불필요)
X11Forwarding no
AllowAgentForwarding no

변경 후 반드시 재시작합니다.

sudo launchctl unload /System/Library/LaunchDaemons/ssh.plist && \
sudo launchctl load -w /System/Library/LaunchDaemons/ssh.plist

💡 Sequoia SSH 방화벽 이슈 — macOS Sequoia에서는 시스템 방화벽(Application Firewall)이 sshd-session을 차단하는 문제가 보고되고 있습니다. SSH 접속이 안 되면 시스템 설정 → 개인 정보 보호 및 보안 → 방화벽에서 sshd-session이 "허용"으로 되어 있는지 확인하세요. 2편에서 설정한 자동 로그인도 재부팅 후 SSH가 바로 작동하려면 필수입니다.

3. Fail2Ban Docker 배포 — 브루트포스 자동 차단

Fail2Ban은 로그 파일을 모니터링하다가 일정 횟수 이상 인증에 실패한 IP를 자동으로 차단하는 도구입니다. SSH 키 인증만으로도 실제 침입은 거의 불가능하지만, 브루트포스 시도 자체가 로그를 오염시키고 시스템 리소스를 소비하므로 Fail2Ban으로 원천 차단하는 것이 좋습니다.

3-1. macOS + OrbStack 환경에서의 Fail2Ban 전략

🤔 네이티브 Linux 서버와 다른 점

일반적인 Linux 서버에서 Fail2Ban은 호스트의 iptables/nftables를 직접 조작해 IP를 차단합니다. 하지만 Mac Mini에서는 Docker가 OrbStack의 경량 Linux VM 위에서 돌아가므로, Docker 컨테이너 안의 Fail2Ban이 macOS 호스트의 네트워크를 직접 제어할 수 없습니다.

따라서 Mac Mini에서의 Fail2Ban 전략은 두 가지로 나뉩니다.

Docker 서비스 보호 → Fail2Ban Docker 컨테이너로 NPM(Nginx Proxy Manager) 등의 로그를 감시

macOS SSH 보호 → macOS의 pf 방화벽 + 스크립트 기반 차단 (섹션 4에서 다룸)

3-2. Fail2Ban Docker Compose 배포

LinuxServer.io의 Fail2Ban 이미지를 사용합니다. ARM64를 지원하므로 M4 Mac Mini에서 네이티브로 실행됩니다.

# 디렉토리 구조 생성
mkdir -p ~/homelab/fail2ban/config
mkdir -p ~/homelab/fail2ban/config/fail2ban/jail.d
mkdir -p ~/homelab/fail2ban/config/fail2ban/filter.d

Docker Compose 파일을 작성합니다.

# ~/homelab/fail2ban/docker-compose.yml
services:
  fail2ban:
    image: lscr.io/linuxserver/fail2ban:latest
    container_name: fail2ban
    cap_add:
      - NET_ADMIN
      - NET_RAW
    network_mode: host
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Asia/Seoul
      - VERBOSITY=-vv
    volumes:
      - ./config:/config
      # NPM 로그 마운트 (5편에서 설정한 NPM 경로에 맞게 수정)
      - ~/homelab/npm/data/logs:/remotelogs/npm:ro
      # 다른 서비스 로그도 필요 시 추가
      # - ~/homelab/vaultwarden/logs:/remotelogs/vaultwarden:ro
    restart: unless-stopped

3-3. NPM(Nginx Proxy Manager) Jail 설정

Fail2Ban의 핵심은 "jail" 설정입니다. 어떤 로그 파일에서 어떤 패턴을 감지하면, 어떤 조치를 취할지 정의합니다.

# ~/homelab/fail2ban/config/fail2ban/jail.d/npm.local
[npm-general]
enabled  = true
filter   = npm-general
logpath  = /remotelogs/npm/fallback_error.log
maxretry = 5
findtime = 600
bantime  = 3600
chain    = DOCKER-USER
action   = iptables-allports[name=npm-general, chain=DOCKER-USER]

[npm-401]
enabled  = true
filter   = npm-401
logpath  = /remotelogs/npm/fallback_access.log
maxretry = 5
findtime = 600
bantime  = 3600
chain    = DOCKER-USER
action   = iptables-allports[name=npm-401, chain=DOCKER-USER]

필터 파일을 생성합니다.

# ~/homelab/fail2ban/config/fail2ban/filter.d/npm-general.local
[Definition]
failregex = ^<HOST>.*(GET|POST|HEAD).*" (400|403|404|405|444) .*$
ignoreregex = .*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot).*

# ~/homelab/fail2ban/config/fail2ban/filter.d/npm-401.local
[Definition]
failregex = ^<HOST>.*(GET|POST|HEAD).*" 401 .*$
ignoreregex =

3-4. 배포 및 확인

# 컨테이너 시작
cd ~/homelab/fail2ban
docker compose up -d

# 로그 확인
docker logs -f fail2ban

# jail 상태 확인
docker exec fail2ban fail2ban-client status
docker exec fail2ban fail2ban-client status npm-general

# 특정 IP 수동 차단/해제
docker exec fail2ban fail2ban-client set npm-general banip 1.2.3.4
docker exec fail2ban fail2ban-client set npm-general unbanip 1.2.3.4

💡 자주 쓰는 Fail2Ban 명령어 정리

• 전체 상태: docker exec fail2ban fail2ban-client status

• 특정 jail 상태: docker exec fail2ban fail2ban-client status [jail이름]

• 차단된 IP 목록: docker exec fail2ban fail2ban-client get [jail이름] banned

• IP 해제: docker exec fail2ban fail2ban-client set [jail이름] unbanip [IP]

• 설정 리로드: docker exec fail2ban fail2ban-client reload

💡 Fail2Ban 설정 팁

findtime: 이 시간(초) 내에 maxretry만큼 실패하면 차단

bantime: 차단 유지 시간(초). -1로 설정하면 영구 차단

ignoreip: 차단 제외 IP. 본인의 고정 IP나 LAN 대역(예: 192.168.1.0/24)을 넣으면 실수로 자기 자신이 차단되는 것을 방지할 수 있습니다

• jail.d에 default.local 파일을 만들어 공통 설정을 넣으면 편리합니다

# ~/homelab/fail2ban/config/fail2ban/jail.d/default.local
[DEFAULT]
# 본인 LAN 대역을 차단 제외
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24

# 기본 차단 시간: 1시간
bantime = 3600

# 10분 내 5번 실패 시 차단
findtime = 600
maxretry = 5

4. macOS 방화벽 설정 — Application Firewall + pf

macOS에는 두 가지 방화벽이 내장되어 있습니다. 역할이 다르므로 둘 다 설정하는 것을 권장합니다.

항목 Application Firewall pf (Packet Filter)
제어 단위 앱(프로세스) 단위 포트/IP/프로토콜 단위
설정 방법 시스템 설정 GUI CLI (pfctl)
용도 앱별 인바운드 허용/차단 네트워크 레벨 패킷 필터링
홈서버 활용 OrbStack, SSH 등 허용 관리 특정 포트/IP 세밀 제어

4-1. Application Firewall 설정

시스템 설정 → 네트워크 → 방화벽에서 활성화합니다.

GUI 설정 순서

1. 시스템 설정 → 네트워크 → 방화벽 →

2. "옵션..." 클릭

3. "자동으로 내장 소프트웨어가 들어오는 연결을 허용" → 체크 해제

4. "다운로드한 서명된 소프트웨어가 들어오는 연결을 자동으로 허용" → 체크 해제

5. "스텔스 모드 활성화" → 체크 (ping에 응답하지 않음)

6. 앱 목록에서 OrbStack Helper, sshd-session을 "허용"으로 추가

CLI로도 동일하게 설정할 수 있습니다.

# 방화벽 활성화
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate on

# 스텔스 모드 활성화
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setstealthmode on

# 자동 허용 비활성화
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setallowsigned off
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setallowsignedapp off

# 현재 설정 확인
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --listapps

4-2. pf (Packet Filter) 기본 설정

pf는 BSD 계열의 강력한 패킷 필터링 방화벽입니다. macOS에서 기본 제공되지만, Apple이 OS 업데이트 시 /etc/pf.conf를 초기화하기 때문에, 커스텀 앵커 파일을 별도로 만들어 관리하는 것이 핵심입니다.

# 1. 커스텀 앵커 파일 생성
sudo nano /etc/pf.anchors/homeserver

다음 규칙을 작성합니다.

# /etc/pf.anchors/homeserver
# Mac Mini 홈서버 pf 방화벽 규칙

# 인터페이스 매크로 (ifconfig로 확인 후 수정)
ext_if = "en0"

# 허용할 포트 정의
ssh_port = "22"
# ssh_port = "2222"  # 포트 변경한 경우
http_ports = "{ 80, 443 }"

# LAN 대역 (본인 네트워크에 맞게 수정)
lan_net = "192.168.1.0/24"

# 기본 정책: 들어오는 트래픽 차단
block in on $ext_if

# 나가는 트래픽 허용
pass out on $ext_if

# 루프백 허용
pass on lo0

# 이미 확립된 연결 허용
pass in on $ext_if proto tcp from any to any flags A/A

# SSH: LAN에서만 허용
pass in on $ext_if proto tcp from $lan_net to any port $ssh_port

# HTTP/HTTPS: 모든 곳에서 허용 (Cloudflare Tunnel 사용 시 불필요할 수 있음)
# pass in on $ext_if proto tcp from any to any port $http_ports

# ICMP(ping) 제한: LAN에서만 허용
pass in on $ext_if inet proto icmp from $lan_net
# 2. pf.conf에 앵커 참조 추가
# ⚠️ macOS 업데이트 시 pf.conf가 초기화될 수 있으므로 백업 필수
sudo cp /etc/pf.conf /etc/pf.conf.backup

# pf.conf 끝에 앵커 추가
echo 'anchor "homeserver"' | sudo tee -a /etc/pf.conf
echo 'load anchor "homeserver" from "/etc/pf.anchors/homeserver"' | sudo tee -a /etc/pf.conf
# 3. 규칙 로드 및 활성화
# 문법 검증 (에러 없어야 함)
sudo pfctl -n -f /etc/pf.conf

# 규칙 로드
sudo pfctl -f /etc/pf.conf

# pf 활성화
sudo pfctl -e

# 현재 규칙 확인
sudo pfctl -sr
sudo pfctl -a homeserver -sr

4-3. pf 자동 시작 설정

macOS 재부팅 시 pf가 자동으로 활성화되도록 LaunchDaemon을 설정합니다.

# 커스텀 LaunchDaemon 생성
sudo nano /Library/LaunchDaemons/com.homeserver.pf.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.homeserver.pf</string>
  <key>ProgramArguments</key>
  <array>
    <string>/sbin/pfctl</string>
    <string>-e</string>
    <string>-f</string>
    <string>/etc/pf.conf</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
</dict>
</plist>
# 권한 설정 + 로드
sudo chown root:wheel /Library/LaunchDaemons/com.homeserver.pf.plist
sudo chmod 644 /Library/LaunchDaemons/com.homeserver.pf.plist
sudo launchctl load /Library/LaunchDaemons/com.homeserver.pf.plist

⚠️ macOS 업데이트 주의 — Apple은 메이저 업데이트(Sequoia → 다음 버전) 시 /etc/pf.conf를 초기화할 수 있습니다. 하지만 /etc/pf.anchors/ 하위의 커스텀 파일은 보존되는 경우가 많습니다. 업데이트 후에는 /etc/pf.conf에 앵커 참조가 남아있는지 반드시 확인하세요. 이것이 앵커를 별도 파일로 분리하는 이유입니다.

5. Docker 네트워크 보안 — 불필요한 포트 노출 차단

Docker 컨테이너의 포트 바인딩은 의외로 큰 보안 허점이 될 수 있습니다. 기본 설정대로 -p 8080:80을 하면 0.0.0.0:8080에 바인딩되어, 같은 네트워크의 모든 기기에서 접근 가능합니다.

5-1. 포트 바인딩을 127.0.0.1로 제한

리버스 프록시(NPM)를 통해서만 서비스에 접근하는 구조라면, 개별 서비스의 포트를 외부에 노출할 필요가 없습니다.

# ❌ 위험: 모든 인터페이스에서 접근 가능
ports:
  - "8080:80"

# ✅ 안전: localhost에서만 접근 가능
ports:
  - "127.0.0.1:8080:80"

OrbStack에서는 기본적으로 안전합니다 — OrbStack의 Docker 포트 포워딩은 기본적으로 macOS의 localhost에서만 접근 가능합니다. 하지만 명시적으로 127.0.0.1을 지정하는 습관을 들이면, 나중에 다른 환경으로 이전해도 안전합니다.

5-2. 포트 노출이 필요 없는 서비스 식별

같은 Docker 네트워크 안에 있는 컨테이너들은 포트를 외부에 노출하지 않아도 서로 통신할 수 있습니다. NPM(리버스 프록시)과 같은 네트워크에 연결된 서비스는 ports: 설정 자체가 불필요합니다.

# docker-compose.yml 예시
services:
  npm:
    image: jc21/nginx-proxy-manager:latest
    ports:
      - "80:80"       # HTTP — 외부 접근 필요
      - "443:443"     # HTTPS — 외부 접근 필요
      - "127.0.0.1:81:81"  # 관리 패널 — 로컬만
    networks:
      - proxy

  portainer:
    image: portainer/portainer-ce:latest
    # ❌ ports: 제거 — NPM을 통해서만 접근
    networks:
      - proxy

  uptime-kuma:
    image: louislam/uptime-kuma:1
    # ❌ ports: 제거 — NPM을 통해서만 접근
    networks:
      - proxy

networks:
  proxy:
    external: true

5-3. Docker 네트워크 분리

서비스별로 Docker 네트워크를 분리하면, 한 컨테이너가 침해되더라도 다른 서비스로의 횡적 이동(lateral movement)을 방지할 수 있습니다.

# 용도별 네트워크 생성
docker network create proxy        # 리버스 프록시 + 웹 서비스
docker network create monitoring   # 모니터링 스택
docker network create internal     # DB 등 내부 전용

네트워크 분리 설계 원칙

proxy 네트워크: NPM + 외부에 공개할 서비스만 연결

internal 네트워크: DB, Redis 등 외부 접근이 절대 불필요한 서비스

monitoring 네트워크: Prometheus, Grafana 등 모니터링 스택

• 서비스가 여러 네트워크에 속해야 한다면 networks:에 복수 네트워크를 지정

# 예시: DB는 internal에만, 웹앱은 proxy + internal 양쪽에
services:
  webapp:
    image: my-webapp:latest
    networks:
      - proxy       # NPM에서 접근 가능
      - internal    # DB 접근 가능

  postgres:
    image: postgres:16
    # ports: 없음 — 외부 노출 완전 차단
    networks:
      - internal    # 내부에서만 접근 가능

5-4. Docker Compose 보안 기본 설정

각 컨테이너에 추가하면 좋은 보안 설정들입니다.

services:
  myservice:
    image: some-image:latest
    # 읽기 전용 파일시스템 (쓰기가 필요한 경로만 tmpfs/volume으로 마운트)
    read_only: true
    tmpfs:
      - /tmp
      - /var/run

    # 권한 상승 방지
    security_opt:
      - no-new-privileges:true

    # 리소스 제한 (과도한 자원 소비 방지)
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '1.0'

    # 자동 재시작 (크래시 복구)
    restart: unless-stopped

6. 보안 체크리스트 — 정기 점검 항목 정리

설정을 한 번 하고 끝이 아니라, 주기적으로 점검해야 보안이 유지됩니다. 아래 체크리스트를 월 1회 정도 확인하는 것을 권장합니다.

🔒 월간 보안 체크리스트

SSH

☐ 패스워드 로그인 비활성화 확인: sudo sshd -T | grep passwordauthentication

☐ authorized_keys에 불필요한 키 없는지 확인

☐ SSH 접속 로그 이상 징후 확인: log show --predicate 'process == "sshd"' --last 7d

Fail2Ban

☐ 컨테이너 정상 실행 중인지 확인: docker ps | grep fail2ban

☐ jail 활성화 상태 확인: docker exec fail2ban fail2ban-client status

☐ 차단된 IP 목록 리뷰 (오탐 확인)

방화벽

☐ Application Firewall 활성화 상태: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate

☐ pf 활성화 상태: sudo pfctl -s info | head -1

☐ pf 규칙 확인: sudo pfctl -a homeserver -sr

Docker

☐ 불필요한 포트 노출 없는지 확인: docker ps --format "{{.Names}}: {{.Ports}}"

☐ 이미지 업데이트 확인 (Watchtower 알림 또는 수동)

☐ 사용하지 않는 컨테이너/이미지/볼륨 정리: docker system prune

macOS

☐ macOS 보안 업데이트 적용 여부 확인

☐ 시스템 설정 → 일반 → 소프트웨어 업데이트 → "보안 응답 및 시스템 파일 자동 설치" 활성화 확인

☐ FileVault(디스크 암호화) 활성화 여부 확인

🛡️ 보안 레이어 요약 — 이 글에서 설정한 방어 체계

Layer 1: Cloudflare Tunnel — 포트 직접 노출 없이 서비스 공개 (6편)
Layer 2: macOS Application Firewall — 앱 단위 인바운드 제어
Layer 3: pf 방화벽 — 포트/IP 단위 패킷 필터링
Layer 4: SSH 키 인증 — 브루트포스 원천 차단
Layer 5: Fail2Ban — 반복 실패 IP 자동 차단
Layer 6: Docker 네트워크 분리 — 횡적 이동 방지
Layer 7: 포트 바인딩 제한 — 불필요한 서비스 노출 차단

완벽한 보안은 없지만, 이 정도면 홈서버로서는 상당히 견고한 방어 체계입니다. 핵심은 하나의 레이어가 뚫려도 다음 레이어가 막아주는 다층 방어(Defense in Depth)입니다.

🖥️ Mac Mini 홈서버 완전 정복 시리즈

✅ 1편. 왜 Mac Mini인가? — 홈서버 하드웨어 선택 가이드

✅ 2편. macOS 서버 초기 세팅 — 헤드리스 최적화 완벽 가이드

✅ 3편. Docker 환경 구축 — OrbStack vs Docker Desktop vs Colima 비교

✅ 4편. 홈서버 필수 서비스 한방에 띄우기 — Docker Compose 실전편

✅ 5편. 리버스 프록시 구축 — Nginx Proxy Manager로 도메인 + HTTPS

✅ 6편. 외부 접속 — Cloudflare Tunnel로 포트포워딩 없이 공개

✅ 7편. 보안 강화 — SSH 키 인증 + Fail2Ban + 방화벽 (현재 글)

📌 8편. 스토리지 & 백업 전략 — Time Machine + Docker 볼륨 + 오프사이트 백업

📌 9편. 실전 서비스 배포 — 미디어 + 사진 + 스마트홈

📌 10편. 종합 모니터링 & 운영 자동화

이 글에서는 SSH 키 인증, Fail2Ban, macOS 방화벽(Application Firewall + pf), Docker 네트워크 보안을 설정하여 Mac Mini 홈서버의 보안을 다층으로 강화했습니다. 다음 8편에서는 이렇게 안전하게 구축한 서버의 데이터를 지키는 방법 — Time Machine 서버 구성, Docker 볼륨 자동 백업, 3-2-1 백업 룰 적용, 그리고 복구 테스트까지 전 과정을 다룹니다.

도움이 됐다면 공감 ❤️ 부탁드려요! 시리즈 알림 받으시려면 구독도 부탁합니다.

반응형

▲ TOP