13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[태그:] Linux 보안

  • [Linux] Linux namespace 활용 사례: 격리 환경 구축과 보안 강화 실전

    [Linux] Linux namespace 활용 사례: 격리 환경 구축과 보안 강화 실전

    Linux namespace 활용 사례: 격리 환경 구축과 보안 강화 실전

    리눅스 서버를 오래 만지다 보면, 운영 호스트는 그대로 둔 채 어떤 프로세스가 실제로 무엇을 건드리는지 빨리 확인해야 하는 순간이 꼭 옵니다. 패키지를 하나 설치했더니 예상 못 한 소켓을 열고, 에이전트를 하나 올렸더니 부팅 직후 파일을 여기저기 만들고, 테스트 스크립트가 외부망으로 나가 버리는 식이죠. 저도 홈랩이든 실무든 비슷했고요. 그때 가장 손에 익혀둘 가치가 있었던 게 Linux namespace 활용이었습니다. 흔히 컨테이너의 재료 정도로만 설명되지만, 직접 unshare, ip netns, nsenter를 만져보면 이건 단순 개념 설명으로 끝낼 기술이 아니더라고요.

    중요한 건 기대치를 정확히 잡는 겁니다. namespace는 마법 상자가 아닙니다. 커널은 공유하고, CPU·메모리 같은 자원 제한도 기본으로 걸어주지 않습니다. 대신 프로세스가 보는 시야를 분리합니다. 이 차이를 이해하면 “언제 namespace를 직접 쓰고, 언제 컨테이너 런타임이나 VM으로 넘어가야 하는지” 판단이 훨씬 빨라집니다. 오늘은 정의 위주보다, Linux namespace 활용이 실제로 어디에 먹히고 어디서 한계가 드러나는지에 초점을 맞춰 보겠습니다.

    Linux namespace 활용에서 격리 범위를 보여주는 아키텍처 다이어그램

    호스트와 각 namespace가 PID, Network, Mount, UTS 관점에서 어떻게 분리되는지 한눈에 보여주는 개요 이미지입니다.

    1. 왜 Linux namespace 활용이 실무에서 바로 먹히는가

    제가 현장에서 namespace를 꺼내는 순간은 대체로 두 가지입니다. 첫째, 운영 호스트에 패키지나 설정 찌꺼기를 남기고 싶지 않을 때입니다. 둘째, 테스트 대상 프로세스가 어디까지 보이고 어디로 나가는지 통제하고 싶을 때죠. 이건 Dockerfile을 만들 정도로 반복적인 작업은 아닌데, 그렇다고 그냥 호스트에서 돌리기엔 찜찜한 상황에서 특히 유용합니다.

    • 서비스 시작 스크립트가 어떤 파일과 소켓을 만드는지 확인할 때
    • 외부 통신을 막은 채 애플리케이션 기동 여부만 검증할 때
    • 방화벽 정책이나 라우팅 변경 전, 별도 네트워크 공간에서 먼저 재현할 때
    • 문제 프로세스를 호스트 전체와 분리해 관찰할 때

    여기서 VM과 컨테이너, namespace를 같은 선상에서만 비교하면 판단이 흐려집니다. 제가 체감한 기준은 이렇습니다. VM은 커널까지 분리하는 대신 무겁고, 컨테이너는 운영 편의성이 좋지만 준비물이 생기고, namespace 직접 사용은 가장 가볍지만 조립 책임이 사용자에게 있습니다. 즉, namespace는 “기능이 약한 컨테이너”라기보다 “필요한 격리 축만 직접 뽑아 쓰는 커널 도구”에 가깝습니다.

    실무 품질을 좌우하는 포인트도 분명합니다. namespace는 CPU나 메모리 폭주를 막아주지 않습니다. 파일 시스템 쓰기 범위도 자동으로 최소화해 주지 않고요. 그래서 저는 보통 이렇게 선을 그어 설명합니다. namespace는 분리, cgroup은 제한, seccomp는 호출 축소, LSM(AppArmor/SELinux)은 정책 강제. 이걸 구분하지 않고 “격리됐다”라고만 말하면 운영 단계에서 사고 납니다.

    2. Linux namespace 활용 전, 무엇이 분리되는지 먼저 보자

    리눅스 네임스페이스는 종류를 외우는 것보다, 각 종류가 장애 분석과 보안에 어떤 영향을 주는지 아는 편이 더 중요합니다. 아래 표는 제가 실제로 판단할 때 쓰는 관점으로 정리한 겁니다.

    Namespace 종류 분리 대상 실무에서 바로 체감되는 효과 자주 놓치는 포인트 주요 도구
    PID 프로세스 ID 공간 테스트 프로세스를 별도 PID 1처럼 실행 가능 /proc를 새로 준비하지 않으면 ps 결과가 헷갈릴 수 있음 unshare --pid --fork, nsenter --pid
    NET 인터페이스, 라우팅, 포트, 방화벽 외부 송신 차단, veth 기반 통신 테스트, 정책 검증 lo를 올리지 않으면 내부 앱이 예상 밖으로 깨질 수 있음 ip netns, ip link, ip route
    MNT 마운트 포인트와 파일시스템 시야 임시 루트, bind mount, 읽기 전용 뷰 구성 전파 속성(shared/private) 이해 없이 건드리면 의도치 않게 호스트에 반영될 수 있음 unshare --mount, mount, pivot_root
    UTS 호스트명, NIS 도메인명 로그와 프롬프트에서 실험 환경 식별이 쉬워짐 보안 경계 자체는 약하고 주로 식별성 개선용 unshare --uts, hostname
    USER UID/GID 매핑과 일부 권한 범위 비특권 사용자 기반 실험에 유리 배포판 정책, 커널 설정, /etc/subuid//etc/subgid 상태에 따라 동작 차이가 큼 unshare --user --map-root-user
    IPC System V IPC, POSIX 메시지 큐 다른 프로세스와 IPC 충돌 방지 눈에 바로 안 보여서 문제 원인을 놓치기 쉬움 unshare --ipc

    저는 처음부터 전부 묶지 않습니다. 대부분 NET + MNT + PID로 시작하고, 로그 식별이 필요하면 UTS, 비특권 실험이 필요하면 USER를 추가합니다. 이유가 있습니다. namespace는 많이 넣을수록 안전해지는 면도 있지만, 동시에 원인 분리가 어려워집니다. 실패했을 때 어느 층에서 막혔는지 빨리 찾는 편이 실무에선 더 중요하거든요.

    3. 실전 시나리오: 격리된 테스트 환경을 직접 만들어보겠습니다

    이번에는 재현 가능한 시나리오로 가보겠습니다. 상황은 이렇다고 치죠. 새 에이전트를 올리기 전에, 이 프로세스가 외부망으로 나가지 못하게 막은 상태에서 정상 기동하는지 확인하고 싶습니다. 또한 호스트의 프로세스 목록과 파일시스템을 그대로 노출하고 싶지 않습니다. 이럴 때 저는 보통 1단계는 네트워크 분리, 2단계는 PID와 마운트 분리 순으로 갑니다.

    1. 호스트와 분리된 네트워크 namespace를 만듭니다.
    2. veth 페어를 붙여 통신 경로를 의도적으로 만듭니다.
    3. namespace 내부에 서비스 하나를 띄워 실제로 어디에 바인딩되는지 확인합니다.
    4. 그다음 PID, UTS, Mount namespace를 붙여 프로세스와 파일시스템 시야까지 좁힙니다.

    이 순서를 추천하는 이유는 단순합니다. 네트워크 단절, 바인딩 실패, /proc 관찰 오류, 마운트 전파 실수는 각기 보는 포인트가 다릅니다. 한 번에 다 엮으면 증상은 하나인데 원인은 넷 다 될 수 있거든요.

    3-1. 네트워크 namespace 생성

    먼저 가장 자주 쓰는 형태부터 보겠습니다. 아래 예시는 호스트 쪽 veth-host와 namespace 내부 veth-lab를 연결하고, 기본 라우트까지 넣는 최소 구성입니다. 외부 인터넷이 꼭 필요 없다면 default route는 일부러 빼도 괜찮습니다.

    sudo ip netns add labns
    sudo ip link add veth-host type veth peer name veth-lab
    sudo ip link set veth-lab netns labns
    
    sudo ip addr add 10.200.1.1/24 dev veth-host
    sudo ip link set veth-host up
    
    sudo ip netns exec labns ip addr add 10.200.1.2/24 dev veth-lab
    sudo ip netns exec labns ip link set lo up
    sudo ip netns exec labns ip link set veth-lab up
    sudo ip netns exec labns ip route add default via 10.200.1.1
    
    sudo ip netns exec labns ip addr
    sudo ip netns exec labns ip route

    이 단계에서 제일 많이 놓치는 건 두 가지입니다. 하나는 lo를 올리지 않는 것, 다른 하나는 “기본 라우트가 있어야만 내부 테스트가 된다”고 오해하는 겁니다. 사실 동일 서브넷 내 호스트와 namespace 간 통신만 볼 거라면 default route가 없어도 됩니다. 반대로 외부로 내보낼 생각이 없다면 일부러 default route를 넣지 않는 편이 더 안전합니다. 이거 진짜 편하더라고요. 시작부터 길을 다 열어 두면, 통신이 되는 이유가 라우팅인지 NAT인지 애플리케이션 재시도 때문인지 해석이 흐려집니다.

    또 하나, ip netns add는 named network namespace를 /var/run/netns/ 관례 아래에 연결해 관리합니다. 배포판에 따라 실제 경로가 /run/netns/로 보일 수도 있는데, 보통 둘은 같은 런타임 경로 계열로 이해하면 됩니다. 그래서 정리 순서도 중요합니다. 네임스페이스 내부 프로세스가 남아 있으면 삭제가 깔끔하게 안 될 수 있습니다.

    sudo ip netns pids labns
    sudo ip netns exec labns pkill -f http.server || true
    sudo ip netns del labns
    sudo ip link del veth-host || true

    여기서 ip netns pids를 먼저 보는 이유가 있습니다. veth 삭제가 실패하거나 namespace 삭제가 남는 경우, 대개 내부에 살아 있는 프로세스가 원인입니다. 명령어는 간단한데 cleanup을 무시하면 실습이 반복될수록 환경이 꼬입니다.

    3-2. namespace 내부에서 서비스 실행

    이제 namespace 내부에 실제 서비스를 올려 보겠습니다. 예시는 단순하지만, 판단 기준은 실무 그대로 가져갈 수 있습니다. 핵심은 “서비스가 떴는가”가 아니라 어디에 바인딩됐고, 어느 네임스페이스 관점에서 보여야 정상인가를 확인하는 겁니다.

    sudo mkdir -p /srv/labns/www
    printf '%s\n' 'hello from lab namespace' | sudo tee /srv/labns/www/index.html > /dev/null
    
    sudo ip netns exec labns bash -lc 'cd /srv/labns/www && python3 -m http.server 8080 --bind 10.200.1.2'
    
    # 다른 터미널에서 확인
    curl http://10.200.1.2:8080/
    sudo ip netns exec labns ss -lntp
    sudo ip netns exec labns curl -I http://10.200.1.2:8080/

    제가 이 단계에서 항상 같이 보는 건 ss -lntp와 바인딩 주소입니다. 127.0.0.1에만 바인딩된 서비스는 namespace 외부에서 접근되지 않아도 정상일 수 있습니다. 반대로 0.0.0.0에 걸렸다면 namespace 내부에선 모든 인터페이스를 받는다는 뜻이라, 추후 포워딩을 붙이면 생각보다 넓게 열릴 수 있습니다. 즉, 서비스 가시성 문제를 네트워크 문제로 오진하지 않는 것이 중요합니다.

    호스트에서 안 열리는데 namespace 내부에서는 잘 뜨는 경우도 자주 봅니다. 이때는 실패가 아니라, 오히려 격리가 의도대로 작동한 신호일 수 있습니다. 확인 순서는 보통 이렇습니다.

    • ip netns exec labns ss -lntp로 프로세스가 어떤 IP와 포트에 bind 했는지 본다
    • curl을 namespace 내부에서 먼저 날린다
    • 그다음 호스트에서 같은 IP로 접근해 경로가 있는지 확인한다
    • 필요하면 NAT나 포워딩을 추가하되, 마지막 단계로 미룬다
    리눅스 네임스페이스 기반 격리 환경 구축 네트워크 구성도

    호스트의 veth와 namespace 내부 인터페이스가 어떻게 연결되고, 테스트 서비스가 어느 IP에 바인딩되는지 설명하는 구성도입니다.

    3-3. 외부 송신까지 통제하려면 어디를 더 봐야 하나

    네트워크 namespace를 만든 뒤 많은 분들이 “이제 외부 인터넷도 자동으로 막히겠지”라고 생각하시는데, 그건 아닙니다. 경로와 NAT를 어떻게 주느냐에 따라 얼마든지 나갈 수 있습니다. 그래서 보안 검증 목적이면 처음에는 default route를 아예 주지 않거나, 주더라도 포워딩과 NAT를 넣지 않은 상태에서 출발하는 편이 낫습니다.

    외부 통신이 필요한 테스트라면 호스트에 포워딩과 NAT를 붙일 수는 있습니다. 다만 이건 편의성보다 해석 난도가 올라갑니다. 정책 검증이 목적이라면 “닫힌 상태에서 하나씩 여는 방식”이 원인 파악에 훨씬 유리합니다. 최소 예시는 아래처럼 구성합니다. 최근 배포판은 nftables를 기본으로 쓰는 경우도 많으니, 환경에 따라 iptables 명령이 호환 계층인지도 같이 확인하는 편이 좋습니다.

    # 호스트에서 IPv4 forwarding 활성화
    sudo sysctl -w net.ipv4.ip_forward=1
    
    # 예시: labns 대역을 외부 인터페이스 eth0로 NAT
    sudo iptables -t nat -A POSTROUTING -s 10.200.1.0/24 -o eth0 -j MASQUERADE
    sudo iptables -A FORWARD -i eth0 -o veth-host -m state --state RELATED,ESTABLISHED -j ACCEPT
    sudo iptables -A FORWARD -i veth-host -o eth0 -j ACCEPT

    다만 저는 일회성 분석 용도라면 여기까지는 잘 안 갑니다. NAT가 들어오면 이제 문제 원인이 애플리케이션인지, 라우팅인지, 포워딩 정책인지, 외부 DNS인지 한 층 더 늘어나기 때문입니다. 단순 기동 확인이라면 닫힌 내부망에서 끝내는 쪽이 훨씬 깔끔했습니다.

    3-4. PID, UTS, Mount까지 붙여 더 독립적인 실행 환경 만들기

    네트워크 분리만으로는 부족할 때가 많습니다. 프로세스 목록을 호스트와 분리하고, hostname을 바꾸고, 마운트 시야까지 따로 가져가면 디버깅 노이즈가 크게 줄어듭니다. 제가 자주 쓰는 형태는 아래와 같습니다.

    sudo unshare --fork --pid --mount --uts --ipc bash -lc '
      mount --make-rprivate /
      mount -t proc proc /proc
      hostname lab-host
      echo "[inside namespace] hostname: $(hostname)"
      echo "[inside namespace] process list:"
      ps -ef
      sleep 300
    '

    여기서 핵심은 두 줄입니다. mount --make-rprivate /와 mount -t proc proc /proc입니다. 첫 번째는 마운트 이벤트 전파를 끊어 의도치 않은 공유를 줄이는 역할을 하고, 두 번째는 현재 PID namespace 관점의 /proc을 다시 보여주기 위한 겁니다. 참고로 최근 unshare는 새 mount namespace에서 기본적으로 마운트를 private 쪽으로 돌리는 동작을 하기도 하지만, 명시적으로 적어두면 실습 문서나 운영 절차에서 덜 헷갈립니다.

    특히 PID namespace는 PID 1의 성격 때문에 생각보다 함정이 있습니다. 격리 환경 안에서 PID 1은 일반 프로세스보다 시그널 처리와 좀비 수거에 민감합니다. 실험처럼 짧게 돌릴 땐 괜찮지만, 조금이라도 오래 유지할 프로세스라면 tini 같은 init 래퍼나 별도 supervisor 역할을 고려하는 편이 안전합니다. 이 부분은 컨테이너에서도 똑같이 반복되죠.

    비특권 실험이 필요하면 USER namespace도 검토할 수 있습니다.

    unshare --user --map-root-user --mount --pid --fork bash -lc '
      id
      mount -t proc proc /proc
      ps -ef
      cat /proc/self/uid_map
      cat /proc/self/gid_map
    '

    다만 이건 “되면 좋고 아니면 말고” 식으로 접근하면 안 됩니다. 실제 서버에서는 /proc/sys/user/max_user_namespaces 값, 배포판 보안 정책, 그리고 일부 배포판에서만 제공되는 /proc/sys/kernel/unprivileged_userns_clone 같은 설정 때문에 막히는 경우가 적지 않습니다. 저는 이 단계에서 시간을 오래 쓰기보다, 운영 정책이 금지한 기능이라면 굳이 namespace 직접 조합에 집착하지 않는다는 쪽입니다. 홈랩과 운영 서버가 다르게 반응하는 대표적인 영역이 바로 여기거든요.

    4. Linux namespace 활용과 보안 강화, 어디까지 믿어도 되나

    Linux namespace 활용의 가장 큰 장점은 사고 범위를 줄이는 데 있습니다. 테스트 프로세스가 호스트 전체 프로세스 목록을 못 보고, 네트워크 인터페이스도 제한되고, 마운트 시야까지 좁아지면 “한 번 잘못 돌린 실험이 운영 전체를 어지럽히는 일”이 크게 줄어듭니다. 실수 방지 효과가 꽤 큽니다. 하지만 여기까지입니다. namespace만으로 완전한 샌드박스가 됐다고 보면 위험합니다.

    이유는 단순합니다. 커널은 여전히 공유합니다. 커널 취약점이 있거나 capability를 넓게 줬거나, 마운트와 디바이스 접근을 느슨하게 두면 격리 강도는 금방 낮아집니다. 제가 실무에서 namespace를 단독 보안 장치로 보지 않는 이유도 여기에 있습니다. 보통 아래 조합으로 봅니다.

    • namespace: 프로세스가 보는 세계 분리
    • cgroup: CPU, 메모리, pids 폭주 제한
    • seccomp: 허용 syscall 범위 축소
    • AppArmor 또는 SELinux: 파일·소켓·능력 사용 제약
    • capability drop: 필요 없는 권한 제거

    실무 판단을 더 직접적으로 말하면 이렇습니다. namespace는 “격리의 뼈대”이고, seccomp와 capability 조정이 “탈출 비용을 올리는 장치”이며, cgroup은 “망가질 때 피해 규모를 제한하는 장치”입니다. 하나만 넣고 안심하는 구조가 제일 위험했습니다.

    systemd를 쓰는 환경이라면, 별도 컨테이너 런타임 없이도 서비스 단에서 격리 옵션을 꽤 보강할 수 있습니다. 예를 들면 아래 같은 방향입니다.

    [Service]
    ExecStart=/usr/local/bin/my-agent
    NoNewPrivileges=yes
    PrivateTmp=yes
    ProtectSystem=strict
    ProtectHome=yes
    PrivateDevices=yes
    RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
    CapabilityBoundingSet=
    SystemCallFilter=@system-service

    이건 namespace의 대체재라기보다 보완재에 가깝습니다. 특히 “운영 서비스인데 컨테이너까지는 안 가고 싶다”는 구간에서 생각보다 강력합니다. 저도 네트워크 namespace로 송신 범위를 줄이고, systemd 옵션으로 파일시스템과 권한을 더 잠그는 조합을 자주 씁니다. 관련 옵션을 더 파고들고 싶다면 systemd 보안 설정 정리 글도 함께 보시면 흐름이 더 잘 잡힙니다.

    5. 트러블슈팅: 제가 실제로 자주 겪었던 문제들

    명령어보다 중요한 게 해석 기준입니다. namespace는 증상이 비슷해도 원인이 전혀 다른 경우가 많습니다. 아래는 제가 가장 자주 봤던 실패 패턴과, 그때 실제로 다시 확인하는 순서입니다.

    5-1. ping은 되는데 애플리케이션 통신은 안 되는 경우

    이건 라우팅보다 바인딩 주소 문제인 경우가 많았습니다. 127.0.0.1에만 바인딩된 서비스는 namespace 내부에서는 잘 떠 보이는데, 호스트나 다른 namespace에서는 닿지 않습니다. 반대로 0.0.0.0에 걸렸는데도 안 붙는다면 방화벽이나 경로를 보게 되죠. 저는 이럴 때 순서를 고정해 둡니다.

    • ss -lntp로 listening 주소를 먼저 본다
    • curl을 서비스가 실행 중인 namespace 내부에서 먼저 친다
    • 설정 파일의 listen, bind, host 값을 본다
    • 그다음에야 라우팅과 필터링 규칙을 본다

    이 순서를 추천하는 이유는, 네트워크 문제처럼 보이지만 실제론 프로세스 설정 문제인 경우가 정말 많기 때문입니다. bind 주소 확인 없이 iptables부터 보는 건 대개 시간 낭비였습니다.

    5-2. PID namespace에서 ps 결과가 이상한 경우

    거의 항상 /proc 문제였습니다. 새 PID namespace에 들어갔는데 현재 namespace에 맞는 proc 파일시스템을 보지 않으면, 프로세스 목록이 호스트 기준처럼 보여 혼란이 생깁니다.

    mount -t proc proc /proc
    ps -ef
    cat /proc/1/status
    readlink /proc/self/ns/pid

    /proc/1/status를 보면 현재 namespace 관점의 PID 1이 누구인지 알 수 있고, readlink /proc/self/ns/pid는 실제로 다른 namespace inode를 보고 있는지 확인하는 데 도움이 됩니다. 여기서 “ps가 이상하다”는 증상 자체가 문제의 본질이 아니라 관찰 대상이 잘못됐다는 신호인 경우가 많습니다.

    5-3. 사용자 namespace가 서버마다 다르게 동작하는 경우

    이건 커널 기능 차이보다 운영 정책 차이가 더 크게 체감됐습니다. 특히 비특권 사용자로 unshare --user --map-root-user를 시도할 때 서버마다 결과가 달라집니다.

    cat /proc/sys/user/max_user_namespaces
    [ -f /proc/sys/kernel/unprivileged_userns_clone ] && cat /proc/sys/kernel/unprivileged_userns_clone
    id
    uname -r
    • max_user_namespaces가 낮거나 0이면 생성이 막힐 수 있습니다.
    • unprivileged_userns_clone는 일부 배포판에서만 노출되는 설정이라, 파일이 없다고 이상한 건 아닙니다.
    • 보안 기준이 엄격한 서버는 정책상 의도적으로 비활성화한 경우가 많습니다.

    이럴 때 억지로 우회하는 건 보통 좋은 설계가 아닙니다. 제가 권하는 건 두 가지 중 하나입니다. 운영에서 반드시 필요하면 관리자 정책으로 정식 허용을 받거나, 아니면 컨테이너 런타임이나 별도 테스트 노드로 경로를 바꾸는 것. 운영 정책과 싸우는 형태는 오래 못 갑니다.

    5-4. 마운트는 분리한 줄 알았는데 호스트에 영향이 남는 경우

    이건 mount namespace를 처음 만질 때 특히 많이 겪습니다. 원인은 대개 마운트 전파 속성입니다. namespace를 나눴다고 해서 모든 마운트 이벤트가 자동으로 고립되는 건 아닙니다. 그래서 저는 mount 작업 전에 mount --make-rprivate /를 거의 습관처럼 넣습니다. 이 한 줄을 빼먹으면 bind mount나 임시 마운트가 예상 밖으로 공유돼 버릴 수 있습니다.

    증상은 보통 이렇습니다. 실험 환경 안에서만 붙인 줄 알았던 마운트가 호스트에서도 보이거나, 반대로 호스트 변경이 내부에 그대로 따라 들어옵니다. 이 경우에는 namespace 개념 자체보다 전파 모델을 다시 보는 편이 빠릅니다. 실무에서 mount namespace가 네트워크 namespace보다 까다롭게 느껴지는 이유가 여기 있습니다.

    Linux namespace 활용 점검과 트러블슈팅을 표현한 터미널 스타일 이미지

    ip netns exec, ss -lntp, mount -t proc proc /proc 같은 핵심 점검 포인트를 한 장으로 정리한 이미지입니다.

    6. 검증과 결과 확인: 무엇을 보고 성공이라 판단하나

    명령이 에러 없이 끝났다고 성공은 아닙니다. namespace는 “만들어졌다”와 “의도대로 격리됐다” 사이에 차이가 큽니다. 저는 아래 순서대로 확인합니다.

    1. 인터페이스가 의도한 namespace에 들어갔는지 본다
    2. 프로세스가 내부에서 어떤 PID와 hostname으로 보이는지 본다
    3. 서비스 포트가 기대한 주소에 bind 되었는지 확인한다
    4. 호스트와 namespace 사이 통신 범위를 점검한다
    5. 원치 않은 외부 송신 경로가 없는지 마지막에 확인한다
    ip netns list
    sudo ip netns exec labns ip addr
    sudo ip netns exec labns ip route
    sudo ip netns exec labns ps -ef
    sudo ip netns exec labns ss -lntp
    sudo ip netns exec labns curl -I http://10.200.1.2:8080/
    
    # 호스트에서 접근 확인
    curl -I http://10.200.1.2:8080/
    
    # namespace 내부에서 호스트 방향 확인
    sudo ip netns exec labns ping -c 2 10.200.1.1
    
    # 실제 네임스페이스 분리 여부 확인
    sudo ip netns exec labns readlink /proc/self/ns/net
    readlink /proc/self/ns/net

    결과 해석 기준도 미리 정해 두면 좋습니다.

    • ip netns list에 보이면 생성은 된 겁니다. 하지만 그걸로 끝은 아닙니다.
    • ip addr에 기대한 IP가 없으면 링크 이동이나 주소 할당 단계부터 다시 봐야 합니다.
    • ss -lntp에 프로세스가 없으면 서비스 시작 실패입니다. 이때는 로그보다 실행 명령과 bind 주소를 먼저 봅니다.
    • 호스트에서는 안 보이는데 내부에서는 보이면, 실패가 아니라 격리 성공일 수 있습니다.
    • 외부 통신이 되면 안 되는 시나리오인데 HTTP나 DNS가 나가면 default route, 포워딩, NAT부터 다시 봐야 합니다.

    제가 특히 강조하는 건 마지막 항목입니다. 격리 환경 검증에서 자주 빠지는 게 “열려 있지 않아야 할 경로 확인”입니다. 되는 것만 보면 절반만 본 겁니다. 막혀야 할 것이 실제로 막히는지 확인해야 보안 관점의 검증이 됩니다.

    7. 운영에 붙일 때의 현실적인 추천 조합

    실제로 써보면 모든 namespace를 한 번에 다 쓰는 방식은 종종 과합니다. 목적에 따라 조합을 나누는 편이 유지보수성이 더 좋았습니다. 아래 표는 제가 선택할 때 쓰는 기준입니다.

    상황 추천 조합 이유 굳이 피할 상황
    간단한 네트워크 테스트 NET 설정이 단순하고 효과가 바로 보입니다. 장기 운영 서비스처럼 재현성과 배포 표준화가 중요한 경우
    프로세스 독립 실행 검증 PID + UTS + MNT 프로세스, hostname, 파일시스템 시야를 함께 분리하기 좋습니다. 마운트 전파를 이해하지 못한 상태에서 급히 운영에 넣는 경우
    보안 민감한 임시 실행 USER + NET + MNT + cgroup + seccomp 권한, 시야, 자원, syscall 범위를 함께 줄일 수 있습니다. USER namespace 정책이 금지된 운영 서버
    반복 배포되는 서비스 직접 namespace보다 컨테이너 런타임 검토 이미지 관리, 재시작 정책, 로깅, 표준화 측면에서 유리합니다. 일회성 포렌식 재현이나 빠른 실험처럼 준비 비용이 아까운 경우

    여기서 제 추천은 꽤 명확합니다. 한 번성 검증이면 직접 namespace, 반복 운영이면 컨테이너 런타임이 대체로 맞습니다. 전자는 스패너처럼 빠르게 꺼내 쓰는 도구이고, 후자는 운영 체계에 가깝습니다. 둘 중 무엇이 더 우월하냐의 문제가 아니라, 조립 책임을 누가 지느냐의 차이로 보는 편이 현실적입니다.

    비용 관점에서도 비슷합니다. namespace 직접 조합은 초기 준비가 가볍지만 사람이 더 똑똑해야 합니다. 반면 컨테이너 런타임은 준비물이 늘어나지만 반복성이 좋아집니다. 그래서 팀 단위로 넘어가면 후자가 이기고, 개인 디버깅이나 빠른 재현 실험에선 전자가 여전히 강합니다. 관련해서 컨테이너와 VM 비교 글까지 같이 연결하면 내부 링크 흐름도 자연스럽게 만들 수 있습니다.

    Linux namespace 활용과 컨테이너 선택 기준을 비교한 인포그래픽

    일회성 테스트, 반복 운영, 보안 요구사항에 따라 어떤 선택이 적합한지 비교한 요약 이미지입니다.

    8. 자주 받는 질문과 마지막 권고

    Q1. namespace만 쓰면 컨테이너를 대체할 수 있나요?

    부분적으로는 가능합니다. 특히 빠른 재현, 네트워크 격리 실험, 프로세스 시야 분리 정도는 namespace만으로도 충분히 됩니다. 다만 이미지 관리, 배포 파이프라인, 재시작 정책, 로그 수집 표준화까지 생각하면 컨테이너 런타임이 훨씬 편한 구간이 분명히 있습니다. 저는 보통 “문제 재현과 분석 단계는 namespace”, “반복 서비스화 단계는 컨테이너”로 나눠 봅니다.

    Q2. 보안 강화 목적이라면 무엇부터 붙여야 하나요?

    제가 권하는 시작점은 NET namespace로 통신 범위를 먼저 줄이고, 그다음 MNT와 PID를 분리하는 방식입니다. 여기에 capability 최소화, seccomp, cgroup 제한을 얹는 쪽이 사고를 줄이기 좋았습니다. 처음부터 다 넣으면 강해 보이긴 하지만, 실패 시 원인 분리가 너무 어려워집니다.

    Q3. 홈랩에서도 해볼 가치가 있나요?

    충분합니다. 오히려 홈랩이 제일 좋습니다. 실패해도 운영 영향이 없고, namespace 특유의 함정인 bind 주소, loopback, /proc 준비, mount propagation을 몸으로 익히기 좋거든요. 저도 홈랩에서 먼저 손에 익힌 뒤에야 운영 서버에서 판단 속도가 빨라졌습니다.

    마지막으로 추천을 딱 잘라 말씀드리면 이렇습니다. 단발성 테스트, 포렌식성 재현, 서비스 기동 검증이 목적이면 Linux namespace 활용을 바로 써보는 편이 맞습니다. 반대로 팀 단위 반복 운영, 배포 자동화, 표준 이미지 관리가 목적이면 직접 namespace를 조립하기보다 컨테이너 런타임으로 넘어가는 쪽이 낫습니다. 보안 관점에서도 마찬가지입니다. namespace는 아주 좋은 출발점이지만 종착점은 아닙니다. 네트워크 분리만 믿지 말고, 권한과 syscall, 자원 제한까지 묶어야 실전에서 덜 흔들립니다.

  • [Linux 보안] SELinux vs AppArmor: 선택 가이드와 실전 비교

    [Linux 보안] SELinux vs AppArmor: 선택 가이드와 실전 비교

    [Linux 보안] SELinux vs AppArmor: 선택 가이드와 실전 비교

    리눅스 서버를 운영하다 보면 결국 한 번은 붙잡게 되는 주제가 있습니다. 바로 SELinux AppArmor 비교입니다. 방화벽만 잘 열고 닫는다고 끝나는 게 아니더라고요. 실제 운영 환경에서는 프로세스가 어디까지 접근할 수 있는지, 침해 사고가 났을 때 피해 범위를 얼마나 좁힐 수 있는지가 정말 중요합니다. 저도 홈랩에서 웹 서버, 컨테이너, 파일 공유 서비스를 이것저것 올려보면서 Linux 보안을 강화하려다가 SELinux(Security-Enhanced Linux)와 AppArmor(Application Armor)를 번갈아 만져봤습니다. 처음엔 둘 다 비슷해 보였는데, 직접 써보니까 운영 철학부터 관리 포인트까지 꽤 다르더라고요.

    혹시 이런 경험 있으신가요? 서비스는 분명 정상인데 권한 거부 때문에 접속이 안 되고, 로그를 보면 더 헷갈리는 상황 말입니다. 저도 처음엔 이게 뭔가 싶었는데, 기준만 잡고 보면 선택이 꽤 쉬워집니다. 이번 글에서는 SELinux vs AppArmor를 비교하면서, 어떤 환경에서 무엇을 선택하면 좋은지 경험 기준으로 풀어보겠습니다.

    SELinux AppArmor 비교를 보여주는 리눅스 보안 개요 다이어그램

    SELinux와 AppArmor가 커널 보안 계층에서 어떻게 동작하는지 보여주는 개요 이미지입니다.

    1. 왜 SELinux와 AppArmor가 중요한가

    쉽게 말해 둘 다 MAC(Mandatory Access Control, 강제 접근 통제) 계열입니다. 일반적인 파일 권한처럼 사용자가 알아서 결정하는 DAC(Discretionary Access Control, 임의 접근 통제)보다 훨씬 강하게 제어하는 방식이거든요. 프로세스가 root 권한을 가졌다고 해도, 보안 정책이 막고 있으면 파일을 읽거나 네트워크 동작을 하지 못하게 되는 겁니다.

    이게 왜 중요하냐면, 서비스 하나가 뚫렸을 때 전체 서버로 번지는 걸 줄여주기 때문이에요. 예를 들어 웹 서버 프로세스가 취약점으로 악용되더라도, 원래 접근하면 안 되는 디렉터리나 소켓을 막아두면 피해 확산을 줄일 수 있습니다. 제가 홈랩에서 리버스 프록시, 파일 서버, 테스트용 앱을 굴릴 때도 결국 마지막 안전장치 역할은 이런 보안 정책이 하더라고요. 평소엔 귀찮아 보여도, 문제 생기면 존재감이 정말 확실합니다.

    2. SELinux와 AppArmor 개념 설명

    2-1. SELinux는 라벨 기반입니다

    SELinux는 객체와 프로세스에 보안 컨텍스트(context, 보안 문맥) 라벨을 붙여서 제어합니다. 파일, 포트, 프로세스가 각각 어떤 타입인지 보고 허용 여부를 판단하는 방식이죠. 처음엔 정말 어렵습니다. 저도 파일 권한만 보다가 갑자기 컨텍스트를 보라니까 삽질 좀 했어요. 그런데 익숙해지면 정책이 꽤 정교하고 체계적이더라고요.

    • 파일과 디렉터리에 보안 라벨을 부여합니다.
    • 프로세스도 특정 도메인(domain, 실행 문맥)으로 실행됩니다.
    • 정책이 세밀해서 대규모 운영 환경에 잘 맞는 편입니다.

    2-2. AppArmor는 경로 기반입니다

    AppArmor는 파일 경로(path, 경로)를 기준으로 제어합니다. 그래서 사람이 읽고 이해하기가 상대적으로 훨씬 쉽습니다. 특정 실행 파일이 어느 경로를 읽고 쓸 수 있는지 프로파일(profile, 정책 파일)로 정의하는 방식이거든요. Ubuntu 계열에서 처음 보안 강화 기능을 접할 때 부담이 덜한 이유가 바로 여기에 있습니다.

    • 실행 파일별 프로파일을 적용합니다.
    • 경로 기반이라 정책을 읽기 쉽습니다.
    • 처음 도입할 때 학습 곡선이 비교적 완만합니다.

    3. SELinux와 AppArmor 비교: 무엇이 다른가

    여기서 중요한 포인트! SELinux AppArmor 비교를 할 때 단순히 “누가 더 강한가”로 보면 답이 잘 안 나옵니다. 실제로는 운영 방식에 맞는지가 훨씬 더 중요하거든요.

    항목 SELinux AppArmor
    정책 기준 라벨 기반 경로 기반
    학습 난이도 상대적으로 높음 상대적으로 낮음
    정책 세밀도 매우 세밀함 직관적이고 단순한 편
    대표 사용 환경 RHEL 계열에서 자주 사용 Ubuntu 계열에서 자주 사용
    초기 운영 체감 로그 분석이 중요 프로파일 관리가 중요
    적합한 상황 엄격한 통제, 장기 운영 빠른 도입, 단순한 서비스 보호

    제 경험상 정리하면 이렇습니다.

    • SELinux: 처음엔 어렵지만, 표준화된 운영과 세밀한 정책이 필요한 환경에서 정말 강합니다.
    • AppArmor: 빠르게 적용하고 이해하기 쉬워서 소규모 서비스나 초기 구축에 부담이 훨씬 적습니다.
    • 보안 강도만 볼 게 아니라, 팀이 정책을 유지할 수 있는지가 더 중요한 선택 기준이 됩니다.

    4. 실전 구현: SELinux 기본 확인과 적용

    이제 실전으로 가보겠습니다. 제가 직접 해보니 무조건 정책부터 건드리기보다, 현재 상태를 먼저 보는 게 훨씬 낫습니다. SELinux는 특히 더 그렇습니다. 상태 확인 없이 파일만 만지면 나중에 왜 막혔는지 추적이 훨씬 어려워지거든요.

    1. SELinux 상태를 확인합니다.
    2. 서비스 파일 컨텍스트를 점검합니다.
    3. 필요하면 적절한 컨텍스트를 다시 지정합니다.
    4. 로그를 보고 실제 차단 원인을 확인합니다.
    getenforce
    seststatus
    ls -Z /var/www
    ps -eZ | grep httpd

    getenforce는 현재 모드를 빠르게 보여줍니다. Enforcing이면 정책이 실제로 강제 적용 중이고, Permissive면 차단 대신 로그만 남깁니다. 처음 테스트할 땐 Permissive 모드에서 원인부터 보는 게 훨씬 편하더라고요.

    sudo semanage fcontext -a -t httpd_sys_content_t "/srv/myweb(/.*)?"  
    sudo restorecon -Rv /srv/myweb

    이 명령은 웹 서버 콘텐츠 디렉터리에 적절한 타입을 지정하는 예시입니다. 여기서 정말 중요한 건 chmod로 해결하려고 하지 말고 컨텍스트를 봐야 한다는 점이에요. 저도 예전에 권한은 맞는데 계속 403이 떠서 한참 헤맸는데, 알고 보니 파일 라벨이 안 맞았던 경우가 정말 많았거든요.

    sudo ausearch -m avc -ts recent
    sudo journalctl -t setroubleshoot --since today

    SELinux는 로그를 보는 습관이 정말 핵심입니다. 거부 로그를 보면 무엇이 막혔는지 힌트를 얻을 수 있어요. 드디어 됐다! 하는 순간이 대부분 여기서 옵니다.

    SELinux AppArmor 비교 글의 SELinux 컨텍스트 적용 흐름 이미지

    SELinux에서 컨텍스트를 확인하고 복구하는 흐름을 단계별로 보여주는 이미지입니다.

    5. 실전 구현: AppArmor 기본 확인과 프로파일 적용

    AppArmor는 상대적으로 진입 장벽이 훨씬 낮습니다. 실제로 써보니까 정책 파일이 사람이 읽기 쉬워서, 빠르게 감을 잡기 정말 좋더라고요. 특히 단일 서비스 보호 용도로는 꽤 편했습니다.

    1. AppArmor가 활성화되어 있는지 확인합니다.
    2. 현재 로드된 프로파일과 적용 상태를 봅니다.
    3. 필요한 프로파일을 enforce 모드로 전환합니다.
    4. 로그를 보고 누락된 권한을 보완합니다.
    sudo aa-status
    sudo apparmor_status

    배포판에 따라 둘 중 하나를 쓰게 되는 경우가 있습니다. 출력에서 어떤 프로파일이 enforce 모드인지, complain 모드인지 확인하면 됩니다. complain은 차단 대신 기록만 남기는 모드라서 테스트할 때 정말 유용해요.

    sudo aa-complain /etc/apparmor.d/usr.sbin.nginx
    sudo aa-enforce /etc/apparmor.d/usr.sbin.nginx

    AppArmor는 이렇게 프로파일 단위로 전환하는 방식이 직관적입니다. 경로 기반이라 “이 서비스가 이 디렉터리를 왜 못 읽지?”를 추적할 때 상대적으로 수월했어요. 다만 경로 변경이 잦은 환경에서는 프로파일 관리가 생각보다 귀찮을 수 있습니다.

    sudo journalctl -k | grep DENIED
    sudo dmesg | grep apparmor

    차단 로그도 꼭 봐야 합니다. AppArmor는 쉬워 보이지만, 결국 로그를 안 보면 감으로 수정하게 되거든요. 그럼 나중에 정책이 지저분해져서 관리가 힘들어집니다.

    6. 어떤 환경에서 무엇을 선택할까

    SELinux AppArmor 비교의 결론은 기술 우열보다 운영 적합성에 있습니다. 제가 홈랩과 실무성 테스트 환경에서 느낀 기준을 정리해보면 이렇습니다.

    • SELinux를 권하고 싶은 경우: RHEL 계열을 쓰고 있고, 정책 일관성과 세밀한 제어가 중요할 때
    • AppArmor를 권하고 싶은 경우: Ubuntu 계열을 쓰고 있고, 빠르게 프로파일을 읽고 조정해야 할 때
    • 팀 운영 관점: 로그 분석과 정책 유지에 익숙한 팀이면 SELinux도 충분히 잘 굴릴 수 있습니다.
    • 개인 홈랩 관점: 처음 시작할 땐 AppArmor가 덜 부담스러울 수 있어요.

    사실 제가 직접 써봤을 때 느낀 체감은 이랬습니다. SELinux는 한 번 체계가 잡히면 든든하고, AppArmor는 처음 붙을 때 편하다는 거죠. 둘 다 좋은 도구인데, 도구보다 운영자의 숙련도가 더 크게 작용하더라고요.

    SELinux AppArmor 비교 선택 가이드를 위한 의사결정 이미지

    배포판, 운영 인력, 정책 복잡도에 따라 어떤 선택이 맞는지 보여주는 결정 흐름도입니다.

    7. ⚠️ 주의사항과 트러블슈팅

    이 섹션은 정말 중요합니다. 저도 처음엔 보안 기능을 꺼버리고 끝내고 싶었던 순간이 정말 많았거든요. 근데 그 습관 들이면 나중에 훨씬 더 힘들어집니다.

    7-1. 서비스가 안 되면 바로 비활성화하지 마세요

    가장 흔한 실수가 “일단 꺼서 되게 만들자”라는 생각입니다. 테스트 중 잠깐 상태를 완화하는 건 가능하지만, 영구 비활성화는 정말 신중해야 합니다. 문제 원인을 안 보고 끄면 이후에 보안 사고 대응력이 확 떨어지거든요.

    7-2. 파일 권한과 보안 정책을 혼동하지 마세요

    SELinux에서는 파일 권한이 맞아도 컨텍스트가 틀리면 접근이 막힙니다. AppArmor에서는 프로세스가 파일 경로 접근 권한을 갖고 있는지 별도로 봐야 합니다. 즉, chmod와 chown만으로는 완벽하게 해결되는 게 아닙니다.

    7-3. 로그 없는 수정은 거의 실패합니다

    • SELinux: AVC 거부 로그 확인
    • AppArmor: DENIED 로그 확인
    • 정책 변경 전후로 무엇이 달라졌는지 기록

    제가 실무 비슷한 환경에서 자주 하는 방식은 간단합니다. 먼저 로그를 보고, 그다음 가장 좁은 범위로 수정합니다. 한 번에 크게 열어버리면 편하긴 한데, 나중에 왜 열었는지 기억이 안 나더라고요.

    8. 검증과 결과 확인

    설정을 끝냈다면 반드시 검증해야 합니다. 적용했다고 믿는 것과 실제로 동작하는 건 정말 다르거든요. 여기서 확인할 건 세 가지입니다.

    1. 서비스가 정상 동작하는지 확인합니다.
    2. 보안 정책이 실제로 enforce 중인지 확인합니다.
    3. 거부 로그가 불필요하게 반복되지 않는지 봅니다.
    curl -I http://localhost
    getenforce
    sudo aa-status
    sudo journalctl -k --since "10 minutes ago"

    정상 응답이 오고, 보안 모드가 의도한 상태이며, 로그에 반복적인 거부가 없다면 1차 검증은 통과입니다. ✅ 여기까지 오면 운영 가능한 상태에 꽤 가까워진 겁니다. 저도 이 단계까지 정리해두면 이후 서비스 추가가 훨씬 수월했어요.

    SELinux AppArmor 비교 적용 후 검증 결과를 보여주는 대시보드 이미지

    서비스 정상 응답, 정책 적용 상태, 로그 감소 여부를 한눈에 확인하는 결과 이미지입니다.

    9. 정리와 FAQ

    SELinux vs AppArmor를 한 줄로 요약하면 이렇습니다. 세밀한 통제와 표준 운영이면 SELinux, 빠른 이해와 간결한 관리면 AppArmor를 선택하는 게 맞습니다. 물론 현실에선 배포판 기본값과 팀 경험이 선택에 큰 영향을 줍니다. 그래서 제 추천은 무조건 하나를 고집하기보다, 현재 운영 체계와 문제 해결 방식에 맞추는 거예요.

    다음 단계로는 컨테이너 환경에서의 보안 프로파일, systemd 서비스 하드닝, seccomp(시스템 콜 필터링) 같은 주제까지 이어가면 좋습니다. 이 부분은 다음 글에서 다룰 예정입니다. 이전 글에서 다뤘던 리눅스 권한 구조와 함께 보시면 흐름이 더 잘 잡히실 거예요.

    자주 묻는 질문

    • Q. 둘 중 하나만 꼭 써야 하나요?
      A. 보통은 배포판과 운영 표준에 맞춰 하나를 중심으로 관리하는 편이 현실적입니다.
    • Q. 초보자에게는 뭐가 더 쉬운가요?
      A. 대체로 AppArmor가 더 직관적입니다. 다만 장기 운영 기준에선 SELinux를 배워둘 가치가 정말 큽니다.
    • Q. 보안 기능이 성능을 크게 떨어뜨리나요?
      A. 일반적인 서버 운영에서 체감보다 정책 정확성이 더 큰 이슈인 경우가 많았습니다.
    SELinux AppArmor 비교 장단점을 정리한 인포그래픽

    선택 기준, 장단점, 추천 시나리오를 한 장으로 정리한 요약 인포그래픽입니다.

    마지막으로, SELinux AppArmor 비교를 하다 보면 자꾸 정답 하나를 찾게 되는데요. 실제로 써보니까 정답은 환경마다 달랐습니다. 중요한 건 꺼두는 게 아니라 이해하고 운영하는 거예요. 처음엔 헷갈려도, 로그를 읽고 정책을 조금씩 줄여가다 보면 감이 옵니다. 그 과정이 조금 번거롭긴 해도, 서버를 오래 안정적으로 굴리려면 결국 한 번은 넘어야 할 산이더라고요. 이거 정말 중요합니다.

  • [보안] CIS 벤치마크 기반 서버 보안 강화: 실제 적용 사례와 효과

    [보안] CIS 벤치마크 기반 서버 보안 강화: 실제 적용 사례와 효과

    CIS 벤치마크 기반 서버 보안 강화: 실제 적용 사례와 효과

    서버를 오래 운영하다 보면 한 번쯤은 이런 순간이 옵니다. 서비스는 잘 돌아가는데, 막상 CIS 벤치마크 서버 보안 관점에서 보면 기본 설정이 생각보다 허술한 경우가 있거든요. 저도 홈랩(Home Lab, 개인 실험용 인프라 환경)과 업무 환경에서 Linux(리눅스) 서버를 같이 만지다 보니, “기능은 되는데 보안 감사(Security Audit, 보안 점검)에서 계속 걸리네?” 싶은 날이 있었습니다. 특히 계정 정책, SSH(Secure Shell, 원격 접속), 파일 권한, 로그 설정 같은 항목은 평소엔 문제 없어 보여도 실제 감사 시점에는 바로 드러나더라고요. 그래서 이번 글에서는 제가 직접 정리해서 적용했던 CIS 벤치마크 기반 서버 보안 강화 과정을 사례 중심으로 풀어보겠습니다.

    이 글은 특정 제품 홍보가 아니라, 실제로 많이 부딪히는 Linux 보안 강화 흐름을 기준으로 적었습니다. 보안 감사 대응이 필요하신 분, 서버 설정을 체계적으로 정리하고 싶은 분, 그리고 “어디서부터 손대야 하지?” 막막하신 분께 특히 도움이 될 겁니다.

    CIS 벤치마크 서버 보안 전체 아키텍처 개요 이미지

    기본 서버에서 계정 정책, SSH 설정, 로그 수집, 감사 항목을 단계적으로 강화하는 전체 흐름을 보여주는 이미지입니다.

    CIS 벤치마크 서버 보안, 쉽게 말하면 뭘까요?

    CIS Benchmarks(CIS 벤치마크, Center for Internet Security에서 제공하는 보안 설정 권고안)는 운영체제와 소프트웨어를 보다 안전하게 설정하기 위한 기준 모음입니다. 쉽게 말해, “이 정도는 최소한 맞춰두면 보안 사고 가능성을 꽤 줄일 수 있습니다”라는 체크리스트에 가깝습니다.

    처음엔 저도 이게 뭔가 싶었습니다. 문서를 보면 항목이 많고, 다 적용하면 서비스가 멈플 것 같아 겁부터 나거든요. 근데 실제로 써보니까 핵심은 단순합니다. 모든 항목을 기계적으로 넣는 게 아니라, 서비스 영향도를 보면서 우선순위를 나눠 적용하는 것이더라고요.

    현장에서 특히 많이 보는 점검 항목

    • 계정 및 인증(Authentication, 사용자 신원 확인): 패스워드 정책, 불필요한 계정 정리, sudo 권한 제한
    • SSH 설정: root 직접 로그인 차단, 인증 방식 점검, 접속 가능한 사용자 제한
    • 파일 권한(File Permission, 접근 권한): 중요한 설정 파일의 소유자와 권한 조정
    • 로그와 감사(Auditing, 행위 기록 추적): auth 로그, sudo 로그, 변경 이력 보관
    • 네트워크 최소화: 불필요한 포트와 서비스 비활성화
    구분 적용 전 적용 후
    계정 관리 오래된 계정 방치 불필요 계정 잠금 또는 제거
    SSH 접속 기본값 위주 운영 root 로그인 차단, 접근 사용자 제한
    로그 추적 기본 로그만 확인 감사 기준에 맞춰 로그 보강
    설정 일관성 서버마다 편차 큼 표준 설정 베이스 확보

    여기서 중요한 포인트! CIS 벤치마크 서버 보안은 만능 보안 솔루션이 아닙니다. 다만 서버 설정의 기준선을 맞춰주기 때문에, 보안 감사나 운영 안정성 측면에서 체감 효과가 꽤 큽니다.

    제가 적용할 때 잡았던 기준: 한 번에 다 하지 않기

    실전에서는 보통 신규 서버보다 기존 운영 서버가 더 문제입니다. 이미 서비스가 돌아가고 있으니 설정 하나 잘못 건드리면 장애로 이어질 수 있거든요. 저도 처음엔 의욕이 앞서서 SSH, PAM(Pluggable Authentication Modules, 인증 모듈), sysctl 커널 파라미터까지 한 번에 바꾸려다가 접속 정책 꼬여서 삽질 좀 했습니다 ㅎㅎ

    그래서 이후부터는 아래 순서로 정리했습니다.

    1. 현재 상태 수집
    2. 위험도 높은 항목 우선 적용
    3. 변경 전 백업
    4. 적용 후 즉시 검증
    5. 운영 표준 문서화

    이 순서가 별거 아닌 것 같아도 진짜 중요합니다. 특히 보안 감사 대응에서는 “무엇을 바꿨는지”보다 “왜 이렇게 바꿨고, 어떻게 검증했는지”가 더 중요할 때가 많더라고요.

    실전 구현 1: 현재 서버 설정과 계정 상태 점검

    가장 먼저 한 일은 현황 파악이었습니다. 서버 설정을 바꾸기 전에 지금 어떤 계정이 있고, SSH가 어떻게 열려 있고, 권한이 어떤지 보는 거죠. 아래 명령어는 배포판에 크게 구애받지 않고 기본 점검에 쓸 수 있습니다.

    # 접속 중인 사용자 확인
    who
    
    # 최근 로그인 이력 확인
    last -a | head
    
    # sudo 권한 그룹 확인
    getent group sudo
    getent group wheel
    
    # root 원격 로그인 설정 확인
    grep -Ei '^PermitRootLogin' /etc/ssh/sshd_config
    
    # 패스워드 정책 관련 기본 설정 확인
    grep -E 'PASS_MAX_DAYS|PASS_MIN_DAYS|PASS_WARN_AGE' /etc/login.defs
    
    # 중요 파일 권한 확인
    ls -l /etc/passwd /etc/shadow /etc/group /etc/ssh/sshd_config

    제가 직접 해보니 여기서 이미 문제점이 꽤 보였습니다. 예전 작업용 계정이 남아 있거나, SSH 설정 파일에 주석만 있고 실설정은 애매한 경우도 있었고요. 이런 상태에서 바로 강화 설정부터 넣으면, 나중에 왜 접속이 막혔는지 추적이 어려워집니다.

    CIS 벤치마크 서버 보안 점검을 위한 리눅스 터미널 이미지

    계정 상태, SSH 설정, 파일 권한을 터미널에서 점검하는 실전 분위기의 이미지입니다.

    실전 구현 2: SSH와 계정 정책부터 우선 강화

    초기 단계에서는 영향도가 크지만 효과도 분명한 항목부터 만졌습니다. 제 경험상 SSH와 계정 정책만 정리해도 보안 감사 결과가 꽤 달라지더라고요.

    1. root 직접 로그인 차단

    sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
    sudo vi /etc/ssh/sshd_config
    PermitRootLogin no
    PasswordAuthentication yes
    X11Forwarding no
    MaxAuthTries 4
    ClientAliveInterval 300
    ClientAliveCountMax 0
    sudo sshd -t
    sudo systemctl reload sshd

    여기서 중요한 건 reload 전에 반드시 문법 검증입니다. 저도 예전에 오타 하나로 SSH 데몬이 reload 실패해서 콘솔로 붙었던 적이 있습니다. 원격 서버면 정말 식은땀 납니다.

    2. 접속 허용 사용자 제한

    # /etc/ssh/sshd_config 예시
    AllowUsers adminuser deployuser

    운영 서버는 접속 대상이 많을수록 관리가 어렵습니다. 그래서 관리 계정은 분리하고, 실제 접속이 필요한 사용자만 명시하는 편이 훨씬 낫더라고요.

    3. 패스워드 정책 정리

    sudo vi /etc/login.defs
    PASS_MAX_DAYS   90
    PASS_MIN_DAYS   1
    PASS_WARN_AGE   7

    환경에 따라 MFA(Multi-Factor Authentication, 다중 인증)나 키 기반 인증 위주로 운영하는 경우도 있겠지만, 기본 정책이 비어 있으면 보안 감사에서 자주 지적됩니다. 그래서 사용 여부와 별개로 기준은 잡아두는 편이 좋더라고요.

    실전 구현 3: 파일 권한과 감사 로그 보강

    그다음은 Linux 보안에서 기본 중의 기본인 파일 권한과 로그입니다. 평소엔 잘 눈에 안 띄는데, 사고 나면 가장 먼저 후회하는 부분이기도 하죠.

    중요 파일 권한 조정

    sudo chown root:root /etc/ssh/sshd_config
    sudo chmod 600 /etc/ssh/sshd_config
    sudo chown root:root /etc/shadow
    sudo chmod 000 /etc/shadow
    sudo chmod 644 /etc/passwd /etc/group

    배포판 기본값과 다를 수 있으니 적용 전 확인은 필수입니다. 특히 자동화 도구나 이미지 템플릿이 이미 정책을 넣어둔 경우가 있어서, 무작정 덮어쓰면 오히려 표준에서 벗어날 수 있습니다.

    sudo 로그 남기기

    sudo visudo
    Defaults logfile="/var/log/sudo.log"

    이 설정은 나중에 보안 감사뿐 아니라 내부 추적에도 꽤 유용했습니다. 누가 언제 어떤 권한 명령을 쳤는지 보는 것만으로도 원인 파악 속도가 많이 달라지거든요.

    불필요 서비스 점검

    systemctl list-unit-files --type=service --state=enabled
    ss -tulpen

    여기서 안 쓰는 서비스가 떠 있으면 정리합니다. 예를 들어 테스트용으로 잠깐 열어둔 데몬이 계속 살아 있는 경우가 꽤 있습니다. 보안은 거창한 장비보다도 이런 기본 서버 설정 정리에서 차이가 납니다.

    CIS 벤치마크 서버 보안을 위한 SSH 설정과 감사 로그 구성 이미지

    SSH 접근 제어와 sudo 로그 기록 흐름을 한눈에 보여주는 구성 다이어그램 이미지입니다.

    ⚠️ 실제로 겪었던 문제와 트러블슈팅

    이 부분이 제일 중요할 수도 있습니다. 문서만 보면 다 간단해 보이는데, 실제 적용하면 꼭 한두 군데서 걸립니다.

    문제 1. SSH 설정 반영 후 접속 실패

    원인은 대부분 두 가지였습니다. 하나는 AllowUsers에 현재 운영 계정을 빼먹은 경우, 다른 하나는 sshd_config 오타였습니다.

    • 해결 방법 1: 설정 변경 전 현재 세션은 유지한 상태에서 새 세션으로 접속 테스트
    • 해결 방법 2: sshd -t로 문법 검증 후 reload
    • 해결 방법 3: 가능하면 콘솔 접속 경로 확보 후 작업

    문제 2. 보안 감사는 통과했는데 운영팀이 불편해함

    이것도 많이 겪습니다. 정책은 강화됐는데 현업이 쓰기 불편하면 결국 예외 요청이 쌓이거든요. 그래서 저는 서버 역할별로 기준을 나눴습니다. 배치 서버, 운영 웹 서버, 점프 서버(Jump Server, 중간 접속용 서버)를 같은 기준으로 묶지 않았습니다.

    서버 유형 강화 우선 항목 주의점
    운영 웹 서버 SSH 제한, 로그 강화, 불필요 서비스 제거 배포 계정 접근 경로 확인
    점프 서버 계정 감사, sudo 로그, 접속 기록 보관 관리자 편의성과 통제 균형
    배치 서버 계정 만료 정책, 스크립트 권한 점검 자동 실행 계정 영향 확인

    문제 3. 자동화 스크립트가 갑자기 실패

    패스워드 정책이나 파일 권한을 강화하고 나면 기존 스크립트가 가정하고 있던 조건이 깨질 수 있습니다. 저도 키 파일 권한을 조정한 뒤 일부 배포 스크립트가 실패한 적이 있었는데, 원인을 찾고 보니 권한 검사가 더 엄격해졌더라고요. 드디어 됐다 싶다가 다시 되돌아가서 확인하는 과정, 다들 한 번쯤 있으시죠.

    검증과 결과: 무엇이 달라졌나

    설정 적용 후에는 꼭 검증을 했습니다. 그냥 파일만 바뀌었다고 끝내면 안 됩니다. 실제 접속, 권한, 로그, 서비스 상태를 같이 봐야 하거든요.

    # SSH 설정 최종 확인
    sshd -T | egrep 'permitrootlogin|maxauthtries|clientaliveinterval|clientalivecountmax'
    
    # sudo 로그 확인
    sudo tail -n 20 /var/log/sudo.log
    
    # 열려 있는 포트 점검
    ss -tulpen
    
    # 최근 인증 로그 확인
    sudo tail -n 50 /var/log/auth.log

    검증 단계에서 제가 체감한 효과는 크게 네 가지였습니다.

    1. 보안 감사 대응 속도 향상: 항목별 근거를 바로 제시하기 쉬워졌습니다.
    2. 설정 표준화: 서버마다 제각각이던 서버 설정이 어느 정도 정리됐습니다.
    3. 추적성 확보: sudo와 인증 로그가 정리되니 사고 분석이 편해졌습니다.
    4. 운영 리스크 감소: root 직접 로그인 같은 명백한 위험 요소를 줄였습니다.

    물론 한 번 적용했다고 끝은 아닙니다. 하지만 CIS 벤치마크 서버 보안 기준으로 최소선만 맞춰도, 평소 불안하게 남아 있던 부분이 꽤 정리됩니다. 특히 보안 감사 준비할 때 체감 차이가 큽니다.

    CIS 벤치마크 서버 보안 적용 후 보안 감사 검증 대시보드 이미지

    적용 전후 점검 항목과 로그 확인 결과를 시각적으로 보여주는 대시보드 형태의 이미지입니다.

    정리: 제가 배운 점과 다음 단계

    이번 적용에서 다시 느낀 건, 보안은 대단한 도구보다도 기본을 얼마나 꾸준히 맞추느냐가 훨씬 중요하다는 점이었습니다. Linux 보안 강화는 막연히 어렵게 느껴지지만, 실제로는 계정 정책, SSH, 권한, 로그처럼 반복해서 나오는 핵심 축이 있습니다. 저도 처음엔 항목이 많아서 겁먹었는데, 하나씩 잘라서 적용하니 생각보다 정리가 되더라고요.

    혹시 지금 운영 중인 서버가 있고, 보안 감사 일정이 다가오고 있다면 이렇게 시작해보세요.

    1. 현재 계정과 SSH 설정부터 점검합니다.
    2. root 로그인 차단과 접근 사용자 제한을 우선 적용합니다.
    3. 중요 파일 권한과 로그 기록을 보강합니다.
    4. 변경 직후 검증 명령으로 바로 확인합니다.
    5. 서버 역할별 예외 정책을 문서화합니다.

    이 흐름만 잡혀도 서버 설정 품질이 눈에 띄게 좋아집니다. 다음 글에서는 Ansible(앤서블, 자동화 도구) 같은 구성 관리 방식으로 이 작업을 반복 가능하게 만드는 방법도 다뤄볼 예정입니다. 이전 글에서 다뤘던 로그 관리나 계정 표준화 주제와도 연결해서 보시면 더 이해가 쉬우실 거예요.

    CIS 벤치마크 서버 보안 적용 전후 비교 요약 이미지

    적용 전후 차이, 핵심 점검 항목, 다음 단계 체크리스트를 요약한 인포그래픽 이미지입니다.

    자주 묻는 질문

    Q1. CIS 벤치마크 항목을 모두 적용해야 하나요?

    아닙니다. 서비스 특성과 운영 환경에 따라 예외가 생길 수 있습니다. 중요한 건 근거 없이 빼는 게 아니라, 왜 제외했는지 설명 가능해야 한다는 점입니다.

    Q2. 기존 운영 서버에도 바로 적용해도 될까요?

    가능은 하지만, 반드시 백업과 검증 경로를 확보한 뒤 단계적으로 적용하는 걸 권장합니다. 특히 원격 접속 정책은 새 세션으로 꼭 테스트해보세요.

    Q3. 보안 감사 대응에 실제 도움이 되나요?

    네, 꽤 도움이 됩니다. 적어도 계정 정책, 접근 통제, 로그 추적성 같은 기본 항목에서 설명이 훨씬 쉬워집니다. 저도 실제로 써보니까 이 부분이 가장 체감되더라고요.