대상 OS: Amazon Linux 2023

systemd는 단순히 프로세스를 올리고 내리는 도구가 아니라, 서비스 프로세스의 “실행 환경(권한/파일시스템/커널 인터페이스)”을 제한하는 샌드박스 역할도 합니다. 취약점이 완전히 사라지지 않더라도, 침해 시 피해 범위를 줄이고(Blast Radius 감소) 횡적 이동(lateral movement)을 늦추는 데 큰 효과가 있습니다.

아래는 “효과 대비 적용 난이도”가 높은 옵션 위주로, Amazon Linux 2023 기준으로 정리한 최소 실무 템플릿입니다. 실제 적용 전에는 반드시 스테이징/롤백 절차를 함께 준비하세요.

0) 적용 방식: 원본 유닛을 건드리지 말고 override로

패키지 업데이트로 유닛 파일이 바뀔 수 있으므로, 가능하면 systemctl edit로 override를 만듭니다.

# 서비스 이름 예: myapp.service
sudo systemctl cat myapp.service
sudo systemctl edit myapp.service

에디터가 열리면 아래 예시를 참고해 필요한 옵션을 추가합니다.

1) 전용 사용자로 실행(User/Group, 가능하면 DynamicUser)

가장 기본이자 효과가 큰 하드닝은 “root로 실행하지 않기”입니다. 서비스 전용 계정을 두고 파일 권한도 함께 재정렬하세요.

# (선택) 고정 계정 방식
sudo useradd --system --home /var/lib/myapp --shell /sbin/nologin myapp
sudo install -d -o myapp -g myapp -m 0750 /var/lib/myapp
sudo install -d -o myapp -g myapp -m 0750 /var/log/myapp

override 예시:

# /etc/systemd/system/myapp.service.d/override.conf
[Service]
User=myapp
Group=myapp

가능하면 DynamicUser=yes도 고려할 수 있지만(임시 UID/GID), 상태 디렉터리/권한 설계가 필요합니다.


2) 권한 상승 차단(NoNewPrivileges)

SUID/SGID 바이너리나 권한 상승 벡터가 있더라도 “프로세스가 새 권한을 얻지 못하게” 막는 장치입니다. 대체로 부작용이 적어 우선 적용 후보입니다.

[Service]
NoNewPrivileges=true


3) 파일시스템 보호(ProtectSystem/ProtectHome + 필요한 쓰기 경로만 허용)

서비스가 시스템 파일을 덮어쓰는 사고(또는 침해)를 막기 위해 파일시스템을 읽기 전용에 가깝게 만듭니다. 그 다음 “정말 필요한 쓰기 경로”만 열어줍니다.

[Service]
ProtectSystem=strict
ProtectHome=true

# 서비스가 써야 하는 경로만 정확히 지정
ReadWritePaths=/var/lib/myapp /var/log/myapp

주의: 애플리케이션이 /tmp, /var/tmp, 소켓 경로 등에 쓰기가 필요할 수 있습니다. 로그/캐시/런타임 파일의 위치를 먼저 파악한 뒤 반영하세요.


4) 임시 디렉터리 격리(PrivateTmp) + 런타임 디렉터리 명시

다른 서비스의 /tmp를 들여다보거나 악성 파일을 주고받는 위험을 줄입니다.

[Service]
PrivateTmp=true

# (권장) 런타임 디렉터리 생성/권한을 systemd가 관리
RuntimeDirectory=myapp
RuntimeDirectoryMode=0750


5) 커널/제어그룹 인터페이스 보호(ProtectKernelTunables/ProtectControlGroups 등)

서비스가 커널 튜닝, cgroup 설정을 건드리는 것을 막아 “호스트 제어면”으로의 접근을 줄입니다.

[Service]
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true

드라이버/커널 모듈 로딩이 필요한 특수 서비스는 예외가 될 수 있습니다.


6) 네트워크 접근 범위 축소(RestrictAddressFamilies, IPAddressDeny/Allow)

서비스가 사용하는 주소 패밀리(IPv4/IPv6/UNIX)만 허용하면, 의도치 않은 통신/터널링을 줄일 수 있습니다.

[Service]
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6

또는 systemd 버전/환경에 따라 IP 기반 제한도 고려합니다(운영 환경에서 지원 여부 확인 필요).


7) Capability 최소화(CapabilityBoundingSet, AmbientCapabilities)

root가 아니라도 특정 capability가 붙으면 위험해질 수 있습니다. “필요한 capability만 남기고 나머지는 제거”하는 게 목표입니다.

[Service]
CapabilityBoundingSet=
AmbientCapabilities=

예: 1024 이하 포트를 열어야 하면 CAP_NET_BIND_SERVICE만 부여하는 식으로 점진 적용합니다.


8) 시스템콜 필터(SystemCallFilter)와 실행 메모리 제한(MemoryDenyWriteExecute)

난이도는 높지만 효과가 큽니다. 다만 애플리케이션/런타임(특히 JVM, 일부 JIT)과 충돌할 수 있으니 단계적으로 적용하세요.

[Service]
# 보수적으로 시작: 기본 허용 후 위험군 차단 등 전략이 필요
MemoryDenyWriteExecute=true
LockPersonality=true


9) 적용/검증/롤백 루틴

하드닝은 “서비스가 정상 동작한다”까지 포함해서 완료입니다.

sudo systemctl daemon-reload
sudo systemctl restart myapp.service
sudo systemctl status myapp.service --no-pager
sudo journalctl -u myapp.service -b --no-pager | tail -n 200

문제 발생 시 즉시 롤백(override 제거):

sudo systemctl revert myapp.service
sudo systemctl daemon-reload
sudo systemctl restart myapp.service

사례(현장에서 자주 겪는 상황)

  • 취약한 웹 애플리케이션이 침해되어도, 프로세스가 /etc를 수정하지 못해 백도어 지속성(persistence)이 크게 줄어든다.
  • 운영자가 “급해서” root로 올린 배치 서비스가 사고를 내고, 동일 호스트의 다른 서비스 설정까지 오염시키는 케이스가 잦다 → 전용 계정/쓰기 경로 제한으로 피해가 국소화된다.
  • 침해 후 공격자가 커널 튜닝/모듈 로딩을 시도하지만 ProtectKernel*로 실패해 탐지·대응 시간을 번다.

트러블슈팅(증상→원인→해결)

  • 증상: 재시작 직후 서비스가 바로 죽고, 로그에 “Read-only file system” 류 오류
    원인: ProtectSystem=strict 적용 후 필요한 쓰기 경로를 안 열어둠
    해결: 실제 쓰기 경로(로그/소켓/캐시)를 파악해 ReadWritePaths=에 추가하거나, 임시로 ProtectSystem=full로 완화 후 다시 조정
  • 증상: 서비스가 /tmp 사용 시 권한 오류/파일 미생성
    원인: PrivateTmp=true로 격리된 tmp를 기대하지 못하는 코드/경로 하드코딩
    해결: 애플리케이션의 tmp 경로를 런타임 디렉터리로 변경하거나, 필요한 경우에만 PrivateTmp 해제(대신 다른 하드닝을 강화)
  • 증상: 80/443 포트 바인딩 실패
    원인: root 권한 제거 후 low port 바인딩 capability 미부여
    해결: AmbientCapabilities=CAP_NET_BIND_SERVICE 등 최소 capability만 추가하거나, 리버스 프록시(nginx)로 포트 종단을 분리
  • 증상: 특정 기능에서만 “Operation not permitted”가 간헐적으로 발생
    원인: capability/커널 보호 옵션이 필요한 커널 인터페이스를 차단
    해결: 해당 기능의 커널 요구사항을 확인하고 옵션을 부분 완화(원인 추적 후 최소한으로 예외 처리)

- 이 글은 ai가 random적으로 만들어 올리는 글입니다. -