대상 OS: Debian 12 (bookworm)
서버를 안전하게 만든다는 건 “패키지 업데이트”만으로 끝나지 않습니다.
실제로 사고를 키우는 건 취약점 그 자체보다, 서비스가 뚫린 뒤에 파일시스템/프로세스/네트워크로 얼마나 쉽게 퍼질 수 있느냐(수평 확산)입니다.
systemd는 단순한 init이 아니라, 서비스 격리를 위한 수많은 샌드박싱 옵션을 제공합니다.
문제는 옵션이 너무 많아서 ‘뭘 켜야 하는지’ 막막하다는 것인데, Debian 12에서는 systemd-analyze security가 좋은 출발점이 됩니다.
오늘은 특정 유닛을 대상으로 점검 → Drop-In으로 강화 → 영향 확인 → 롤백까지, 운영 가능한 루프로 정리합니다.
---
# 1) 시작: 내 서비스가 지금 얼마나 위험한지 숫자로 보기
1) 점검 대상 유닛 선택
- 예시로 nginx를 쓰지만, 어떤 서비스든 동일하게 적용할 수 있습니다.
systemctl status nginx 2>/dev/null | sed -n '1,30p' || true
2) systemd-analyze security 실행
systemd-analyze security nginx.service 2>/dev/null | sed -n '1,220p' || true
3) 해석 방법(운영 기준)
- 점수/등급은 ‘절대’가 아니라 ‘비교’에 유용합니다.
- “치명적으로 위험한 권한(쓰기 가능 경로가 넓음, 특권 과다)”부터 줄이는 것이 우선입니다.
---
# 2) Drop-In으로만 바꾸기: 패키지 업데이트에도 유지되는 하드닝 방식
1) 왜 Drop-In인가
- `/lib/systemd/system/*.service`를 직접 수정하면 패키지 업데이트 때 덮어씌워집니다.
- Drop-In은 `/etc/systemd/system/<unit>.d/*.conf`로 관리되어 운영에 적합합니다.
sudo systemctl edit nginx.service
위 명령은 편집기를 열어 Drop-In을 만들게 해줍니다.
CLI로 파일을 만들고 싶다면 아래처럼 진행합니다.
sudo install -d -m 0755 /etc/systemd/system/nginx.service.d
---
# 3) 1차 하드닝(대부분의 서비스에 안전한 기본 세트)
1) Drop-In 파일 생성
sudo bash -lc 'cat >/etc/systemd/system/nginx.service.d/10-hardening.conf <<"EOF"
[Service]
# 권한 상승 차단
NoNewPrivileges=true
# 임시 공간 격리
PrivateTmp=true
# 홈 디렉터리 차단(필요 시 read-only로 조정)
ProtectHome=true
# 시스템 경로 보호(서비스 특성에 따라 strict/full 조정)
ProtectSystem=full
# 장치 파일 접근 최소화
PrivateDevices=true
# 커널 인터페이스 보호
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
# 프로세스 정보 노출 줄이기
ProtectProc=invisible
ProcSubset=pid
# 네임스페이스/권한 범위 줄이기
RestrictSUIDSGID=true
RestrictNamespaces=true
# 메모리 실행 억제(호환성 확인 필요)
MemoryDenyWriteExecute=true
# 기본 umask 강화
UMask=0077
EOF'
'
2) systemd 리로드 및 재기동
sudo systemctl daemon-reload
sudo systemctl restart nginx
sudo systemctl --no-pager -l status nginx | sed -n '1,40p'
3) 다시 점수 확인
systemd-analyze security nginx.service 2>/dev/null | sed -n '1,220p' || true
---
# 4) DynamicUser 적용 여부 판단: ‘영구 사용자’를 없애는 강력한 옵션
1) DynamicUser가 좋은 상황
- 서비스가 상태를 영구 저장하지 않거나, 저장 경로를 명확히 격리할 수 있는 경우
2) DynamicUser가 어려운 상황
- 정적 UID/GID를 요구하는 외부 스토리지(NFS) 권한 설계
- 파일을 특정 소유권으로 남겨야 하는 레거시 구성
3) 적용 예시(테스트 환경에서 먼저)
- 예시 유닛이 DynamicUser와 호환되는지 확인해야 합니다.
sudo bash -lc 'cat >/etc/systemd/system/nginx.service.d/20-dynamicuser.conf <<"EOF"
[Service]
DynamicUser=true
StateDirectory=nginx
CacheDirectory=nginx
LogsDirectory=nginx
EOF'
'
sudo systemctl daemon-reload
sudo systemctl restart nginx
sudo systemctl --no-pager -l status nginx | sed -n '1,60p'
- 실패하면 주로 쓰기 경로/권한 문제이므로, `StateDirectory=` 같은 “systemd가 관리하는 디렉터리”로 옮기는 방식이 안전합니다.
---
# 5) 시스템콜/네트워크 제한(고급): 서비스별로 꼭 필요한 것만 남기기
1) SystemCallFilter로 위험한 호출 줄이기
- 범용으로는 `@system-service`가 출발점이 될 수 있습니다.
- 너무 빡빡하게 하면 서비스가 바로 죽을 수 있으니, 단계적으로 적용합니다.
sudo bash -lc 'cat >/etc/systemd/system/nginx.service.d/30-syscall.conf <<"EOF"
[Service]
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
EOF'
'
sudo systemctl daemon-reload
sudo systemctl restart nginx
sudo journalctl -u nginx -S -10m --no-pager | tail -n 120
2) 네트워크 주소 패밀리 제한(IPv4만 쓰는 서버라면)
sudo bash -lc 'cat >/etc/systemd/system/nginx.service.d/40-net.conf <<"EOF"
[Service]
RestrictAddressFamilies=AF_UNIX AF_INET
EOF'
'
sudo systemctl daemon-reload
sudo systemctl restart nginx
---
# 6) 영향 확인: “서비스는 살아있지만 보안은 깨졌다”를 막기
1) 포트 리스닝 확인
ss -lntp | grep nginx || true
2) 파일 쓰기/로그 위치 확인
sudo journalctl -u nginx -S -30m --no-pager | tail -n 200
3) 보안 하드닝 설정 확인(최종 유효값)
systemctl cat nginx.service | sed -n '1,220p'
systemctl show -p DynamicUser -p ProtectSystem -p ProtectHome -p NoNewPrivileges -p PrivateTmp nginx.service
---
# 7) 롤백 전략: “잠금 사고”를 피하는 운영 절차
1) Drop-In만 제거하면 원복된다
sudo ls -al /etc/systemd/system/nginx.service.d
2) 특정 Drop-In만 비활성화(파일명 변경)
sudo mv /etc/systemd/system/nginx.service.d/30-syscall.conf /etc/systemd/system/nginx.service.d/30-syscall.conf.disabled.$(date +%F_%H%M%S)
sudo systemctl daemon-reload
sudo systemctl restart nginx
3) 전체 Drop-In 제거(최후 수단)
sudo mkdir -p /root/systemd-dropin-backup
sudo cp -a /etc/systemd/system/nginx.service.d /root/systemd-dropin-backup/nginx.service.d.$(date +%F_%H%M%S)
sudo rm -rf /etc/systemd/system/nginx.service.d
sudo systemctl daemon-reload
sudo systemctl restart nginx
---
# 8) 운영 체크리스트: 점수 올리기보다 중요한 것
1) 변경은 2~3개 옵션씩만
2) 재시작 후 기능 테스트(헬스체크/HTTP 200 등)를 자동화
3) systemd-analyze security 결과를 기준선으로 저장
systemd-analyze security nginx.service 2>/dev/null > /root/nginx.security.$(date +%F_%H%M%S).txt || true
4) 강화는 ‘가장 위에서’부터
- NoNewPrivileges, ProtectSystem/ProtectHome, PrivateTmp 같은 “큰 축”이 먼저입니다.
- syscall 필터링은 마지막에 천천히.