LFCS — 리눅스 재단 시스템 관리자 · SELinux 와 firewalld 를 손으로 — 사용자·ACL·sshd·dnf 까지 · 이론
지금 값과 영구 값은 다른 자리에 산다 — SELinux·firewalld·sudoers·ACL·sshd
한 줄 요약
RHEL 계열의 관리 도구는 거의 전부 "지금" 과 "영구" 를 따로 둔다. setsebool 과 -P, chcon 과 semanage fcontext, firewalld 의 runtime 과 permanent. 그리고 실효값은 파일이 아니라 도구가 알려 준다 — getsebool, firewall-cmd --list-all, sudo -l -U, getfacl, sshd -T. 이 모듈의 실습은 AlmaLinux 9 VM 에서 그 둘을 다 채점한다.
왜 이게 필요했나
LFCS 의 Operations 도메인은 "SELinux 로 접근을 관리하라" 를 명시하고, Networking 도메인은 패킷 필터링과 OpenSSH 구성을 요구한다. 파드에는 SELinux 도 firewalld 도 없어서 이 항목들은 모의고사에서만 스쳤다. 앞의 RHCE 실습은 같은 것을 Ansible 로 했지만, 시험장에서 손에 있는 것은 semanage 와 firewall-cmd 다.
이 VM 은 Enforcing 이다. 그래서 포트 컨텍스트를 빠뜨리면 httpd 가 8081 을 bind 하지 못해 정말로 뜨지 않고, firewalld 에 permanent 를 빠뜨리면 --reload 한 순간 규칙이 사라진다. 채점 에이전트가 8899/tcp 로 들어오므로 그 포트를 지우거나 기본 존을 바꾸면 채점이 닿지 않는다 — RHCE 실습에서 실측한 함정 그대로다.
어떻게 동작하나
SELinux 는 DAC 위의 한 겹이다. [RHEL 9 Using SELinux](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/using_selinux/index) 가 설명하는 대로, 프로세스와 파일에 붙은 컨텍스트(타입)가 정책의 허용 규칙과 맞아야 접근이 된다. root 여도 예외가 아니다. 불리언은 정책을 다시 만들지 않고 켜고 끄는 스위치이고, [setsebool(8)](https://man7.org/linux/man-pages/man8/setsebool.8.html) 의 -P 가 없으면 지금은 바뀌지만 재부팅하면 돌아간다. semanage boolean -l -C 가 기본값에서 바꾼 것만 보여 주고, (on , on) 은 (현재, 영구) 다.
파일 컨텍스트는 두 곳에 산다. 파일의 xattr(지금 값)과 정책의 규칙 목록(원래 그래야 하는 값). chcon 은 앞만 바꾸고 [semanage-fcontext(8)](https://man7.org/linux/man-pages/man8/semanage-fcontext.8.html) 는 뒤를 바꾼다. 뒤를 안 바꾸면 restorecon 한 번에 되돌아간다. 그래서 순서는 규칙 추가 → restorecon -R 이다. 포트에도 타입이 있다. [semanage-port(8)](https://man7.org/linux/man-pages/man8/semanage-port.8.html) 로 8081 을 http_port_t 에 넣지 않으면 httpd_t 는 그 포트를 열 수 없고, 거부는 ausearch -m avc 에 남는다.
firewalld 는 존과 두 상태다. [firewall-cmd](https://firewalld.org/documentation/man-pages/firewall-cmd.html) 에서 --permanent 를 붙이면 설정 파일에만, 안 붙이면 지금 도는 규칙에만 적용된다. --reload 는 permanent 를 runtime 으로 다시 읽으므로 runtime 에만 넣은 것은 그때 사라진다. [firewalld.zone(5)](https://firewalld.org/documentation/man-pages/firewalld.zone.html) 의 존은 출발지나 인터페이스별로 다른 허용 목록을 두는 단위이고, 새 존은 permanent 로만 만들 수 있어 reload 뒤에야 runtime 에 나타난다. 서비스·포트만으로 못 적는 조건(출발지 + 포트 + 동작)은 [rich rule](https://firewalld.org/documentation/man-pages/firewalld.richlanguage.html) 한 줄로 적는다.
sudoers 는 조각으로, 검사부터. [sudoers(5)](https://man7.org/linux/man-pages/man5/sudoers.5.html) 의 %ops ALL=(ALL) NOPASSWD: /usr/bin/systemctl 은 "ops 그룹이 어디서든 누구로든 비밀번호 없이 systemctl 만" 이다. /etc/sudoers.d/ 에 두면 패키지 갱신이 덮어쓰지 않고, 조각 하나가 틀려도 그 조각만 거른다. 문법이 틀린 채로 두면 sudo 전체가 거부하므로 visudo -cf 로 먼저 검사한다. 결과는 sudo -l -U deploy 가 알려 준다.
로그인 환경과 자원 제한은 로그인 순간에 걸린다. /etc/profile.d/*.sh 는 로그인 셸이 /etc/profile 을 거치며 읽는 조각이고, export 가 없으면 그 셸 안에서만 보이고 셸이 띄운 프로그램에는 전달되지 않는다. [limits.conf(5)](https://man7.org/linux/man-pages/man5/limits.conf.5.html) 의 네 필드(대상·soft/hard·항목·값)는 pam_limits 가 세션을 열 때 적용하므로 이미 열린 셸에서 ulimit 을 봐도 바뀌지 않는다. 그룹은 @ops 로 적는다.
ACL 은 아홉 비트 밖의 권한이다. 소유자·그룹·나머지로는 "이 사용자 한 명만 더" 를 표현할 수 없다. [setfacl(1)](https://man7.org/linux/man-pages/man1/setfacl.1.html) 의 -m u:deploy:rwx 가 그것이고, -d 의 기본 ACL 은 디렉터리 안에 앞으로 생기는 것이 물려받는다. ACL 이 붙으면 ls -l 의 그룹 자리는 실제 그룹 권한이 아니라 mask 를 보여 준다 — 750 에 ACL 을 주면 770 처럼 보이는 것이 정상이다. mask 가 실효 권한을 자르므로 getfacl 의 #effective 를 본다.
sshd 는 먼저 읽은 값을 쓴다. [sshd_config(5)](https://man7.org/linux/man-pages/man5/sshd_config.5.html) 에 따르면 같은 키워드가 여러 번 나오면 첫 값이 이긴다. RHEL 9 의 sshd_config 는 첫 줄이 Include /etc/ssh/sshd_config.d/*.conf 라 드롭인이 본문을 이긴다. sshd -t 가 문법을, sshd -T 가 모든 Include 와 Match 를 푼 실효값을 알려 준다.
dnf 는 트랜잭션이다. [dnf 명령 참조](https://dnf.readthedocs.io/en/latest/command_ref.html) 의 history 가 트랜잭션 단위로 이력을 남기고 undo 로 되돌린다. 업그레이드에서 빼는 방법은 둘이다. [versionlock 플러그인](https://dnf-plugins-core.readthedocs.io/en/latest/versionlock.html) 은 지금 버전에 고정하고, dnf.conf 의 exclude= 는 후보에서 아예 뺀다. 우분투의 apt-mark hold 와 같은 자리다.
현장에서 만나는 모습
"httpd 가 안 뜬다, 설정은 맞다" 의 RHEL 판은 열에 아홉이 SELinux 다. journalctl -u httpd 에 Permission denied 가 보이면 ausearch -m avc -ts recent 를 본다. 포트라면 semanage port, 파일이라면 semanage fcontext + restorecon 이 답이고, setenforce 0 은 답이 아니다.
"방화벽을 열었는데 재부팅하니 닫혔다" 는 --permanent 를 빠뜨린 것이고, "열었는데 지금 안 된다" 는 --permanent 만 주고 --reload 를 안 한 것이다. 같은 도구의 두 얼굴이라 증상이 정반대로 나온다.
"ACL 을 줬는데 쓰기가 안 된다" 는 mask 를 본다. chmod g-w 같은 그룹 권한 변경이 mask 를 함께 깎아 user:deploy:rwx 의 실효값이 r-x 로 떨어진다. getfacl 이 #effective:r-x 로 알려 준다.
다음 실습에서 할 것
sestatus 저장 + setsebool -P → /srv/web 의 fcontext 규칙 + restorecon + 8081 을 http_port_t 로 + httpd 응답 → public 존에 http·8081 을 runtime·permanent 양쪽에(8899 유지) → 존 lab + 출발지 + 리치 룰 → ops/deploy + sudoers.d/ops(0440, visudo -cf) → profile.d export + limits.d → /srv/web/upload 의 ACL 과 기본 ACL → sshd 드롭인 + sshd -T → tree 설치·잠금·dnf history. 채점기는 getsebool, semanage … -C, firewall-cmd --permanent, su - deploy, sshd -T 로 실효값을 본다. 파일에 적은 것과 도구가 말하는 것이 같을 때만 통과한다.