Microsoft MVP성태의 닷넷 이야기
Linux: 112. Linux - 데몬을 위한 SELinux 보안 정책 설정 [링크 복사], [링크+제목 복사],
조회: 3603
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)
(시리즈 글이 3개 있습니다.)
Linux: 112. Linux - 데몬을 위한 SELinux 보안 정책 설정
; https://www.sysnet.pe.kr/2/0/13862

Linux: 113. Linux - 프로세스를 위한 전용 SELinux 보안 문맥 지정
; https://www.sysnet.pe.kr/2/0/13863

Linux: 114. eBPF를 위해 필요한 SELinux 보안 정책
; https://www.sysnet.pe.kr/2/0/13864




Linux - 데몬을 위한 SELinux 보안 정책 설정

이상하군요, 분명히 systemd에 등록한 서비스가 uid == 0으로 실행되고 있는데 RemoveMemlock 코드에서 이런 오류가 발생합니다.

// uid: 0, euid: 0, gid: 0

if err := rlimit.RemoveMemlock(); err != nil {
    log.Fatal("Removing memlock:", err)
    // Removing memlock:unexpected error detecting memory cgroup accounting: permission denied
}

"Golang + bpf2go를 사용한 eBPF 기본 예제" 글에서 root 권한으로 실행하지 않으면 "failed to set memlock rlimit: operation not permitted" 오류가 발생한다고 했는데요, 이번에 오류 메시지가 다릅니다.

검색해 보면,

unable to set memory resource limits #20676
; https://github.com/cilium/cilium/issues/20676

I have the same problem, it's due to selinux. After I disabled selinux everything works.


SELinux 환경에서 발생한다고 하는데요, 실제로 저 오류가 발생한 CentOS 7 서버에 SELinux가 활성화돼 있었습니다.

5.3. Main Configuration File
; https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/6/html/security-enhanced_linux/sect-security-enhanced_linux-working_with_selinux-main_configuration_file

$ cat /etc/selinux/config

# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
SELINUX=enforcing
# SELINUXTYPE= can take one of three values:
#     targeted - Targeted processes are protected,
#     minimum - Modification of targeted policy. Only selected processes are protected.
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted

또는, 이렇게 확인할 수도 있습니다.

$ getenforce
Enforcing

$ sestatus
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
Current mode:                   enforcing
Mode from config file:          enforcing
Policy MLS status:              enabled
Policy deny_unknown status:     allowed
Max kernel policy version:      31

이 상황을 해결하는 가장 단순한 방법은 /etc/selinux/config 파일에서 SELinux 설정을 비활성화하고,

SELINUX=disabled

재부팅을 하면 됩니다.




하지만, 테스트 환경을 제외한다면 실제 업무 서버에 SELinux가 설치된 경우 이를 비활성화하는 것은 현실적이지 않습니다. 그렇다면 SELinux 환경에 뭔가 예외 설정을 해야 할 것 같은데요, 좀 더 살펴보겠습니다. ^^

우선, SELinux가 설치된 환경에서도 systemd 데몬으로서가 아닌, 단순히 명령행에서 프로세스를 sudo로 실행하면 저런 오류가 발생하지 않는데요, 이 차이점은 실행된 상태의 프로세스 속성을 확인해 보면 알 수 있습니다.

// systemd로 실행된 상태인 경우,

$ ps -eZ
...[생략]...
system_u:system_r:unconfined_service_t:s0 10102 ? 00:00:00 test-myapp
...[생략]...

// sudo로 실행한 상태인 경우,

$ ps -eZ
...[생략]...
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 11700 pts/1 00:00:00 test-myapp
...[생략]...

보는 바와 같이 systemd로 실행된 상태에서 적용된 system_u의 경우 SELinux 정책에 등록된 사용자에 해당하고,

$ sudo semanage login -l

Login Name           SELinux User         MLS/MCS Range        Service

__default__          unconfined_u         s0-s0:c0.c1023       *
root                 unconfined_u         s0-s0:c0.c1023       *
system_u             system_u             s0-s0:c0.c1023       *

$ seinfo -u

Users: 8
   sysadm_u
   system_u
   xguest_u
   root
   guest_u
   staff_u
   user_u
   unconfined_u

이에 대해 문서를 보면,

// https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/8/html/using_selinux/managing-confined-and-unconfined-users_using-selinux

By default, all Linux users in Red Hat Enterprise Linux, including users with administrative privileges, are mapped to the unconfined SELinux user unconfined_u. You can improve the security of the system by assigning users to SELinux confined users.

...[생략]...

Confined users are restricted by SELinux rules explicitly defined in the current SELinux policy. Unconfined users are subject to only minimal restrictions by SELinux.


unconfined_u 사용자로 적용되지 않은 경우라면 SELinux 정책에 따라 제한이 걸린다고 하니 일단은 sudo/systemd로 실행하는 것에 대한 차이는 설명이 됩니다.




자, 그렇다면 이제 할 일은 system_u로 실행된 프로세스가 어떤 권한을 필요로 하는지 알아내야 하는데요, 바로 이런 정보를 SELinux의 audit log에서 확인할 수 있습니다.

$ sudo cat /var/log/audit/audit.log | grep -m 1 denied
...[생략]...
type=AVC msg=audit(1736234798.810:41215): avc:  denied  { map_create } for  pid=2227 comm="my-test-app" scontext=system_u:system_r:unconfined_service_t:s0 tcontext=system_u:system_r:unconfined_service_t:s0 tclass=bpf permissive=0

이 외에도 audit log를 정제해서 보여주는 다양한 SELinux 도구들이 있으므로 이를 활용해도 됩니다.

// audit.log 파일에서 검색 힌트를 얻을 수 있는데,
// 문제가 되는 유형은 comm="my-test-app" and msg type == "avc"이고,
// --start 옵션으로 특정 시간 이후에 생성된 로그로 제한할 수 있습니다.

$ sudo ausearch --start 01/09/2025 00:00:00 -m avc --raw --comm my-test-app
...[생략]...
type=AVC msg=audit(1736408283.758:43684): avc:  denied  { map_create } for  pid=14028 comm="my-test-app" scontext=system_u:system_r:unconfined_service_t:s0 tcontext=system_u:system_r:unconfined_service_t:s0 tclass=bpf permissive=0

$ sealert -l "*"
...[생략]...
SELinux is preventing my-test-app from map_create access on the bpf labeled unconfined_service_t.

*****  Plugin catchall (100. confidence) suggests   **************************

If you believe that my-test-app should be allowed map_create access on bpf labeled unconfined_service_t by default.
Then you should report this as a bug.
You can generate a local policy module to allow this access.
Do
allow this access for now by executing:
# ausearch -c 'my-test-app' --raw | audit2allow -M my-se-policy
# semodule -i my-se-policy.pp


Additional Information:
Source Context                system_u:system_r:unconfined_service_t:s0
Target Context                system_u:system_r:unconfined_service_t:s0
Target Objects                Unknown [ bpf ]
Source                        my-test-app
Source Path                   my-test-app
Port                          
Host                          centos7
Source RPM Packages
Target RPM Packages
Policy RPM                    selinux-policy-3.13.1-229.el7_6.12.noarch selinux-
                              policy-3.13.1-268.el7_9.2.noarch
Selinux Enabled               True
Policy Type                   targeted
Enforcing Mode                Enforcing
Host Name                     centos7
Platform                      Linux centos7 5.1.15-1.el7.elrepo.x86_64 #1 SMP
                              Tue Jun 25 20:52:45 EDT 2019 x86_64 x86_64
Alert Count                   3
First Seen                    2025-01-09 19:36:13 KST
Last Seen                     2025-01-09 19:37:50 KST
Local ID                      c15423a2-e00f-470a-9f9e-67a3c93632d6

Raw Audit Messages
type=AVC msg=audit(1736408270.825:43673): avc:  denied  { map_create } for  pid=13979 comm="my-test-app" scontext=system_u:system_r:unconfined_service_t:s0 tcontext=system_u:system_r:unconfined_service_t:s0 tclass=bpf permissive=0


Hash: my-test-app,unconfined_service_t,unconfined_service_t,bpf,map_create

sealert의 경우 첫 번째 라인에 나오는 메시지가 모든 것을 설명하는데요,

SELinux is preventing my-test-app from map_create access on the bpf labeled unconfined_service_t.

즉, unconfined_service_t로 분류된 comm="my-test-app" 프로세스가 bpf에 대한 map_create 접근을 시도했지만, SELinux 정책에 의해 거부당했다는 것을 의미합니다.

자, 이제 원인을 알았으니 SELinux 정책을 수정해야 하는데요, 재미있게도 audit 로그 파일의 내용을 기반으로 정책을 만들어 주는 audit2allow 도구를 제공하고 있어 이 단계를 쉽게 해결할 수 있습니다.

// Carbon Black Cloud: How to allow BPF event collection on SELinux
// https://knowledge.broadcom.com/external/article/292463/carbon-black-cloud-how-to-allow-bpf-even.html

$ sudo ausearch -c 'my-test-app' --raw  | audit2allow -M my-se-policy
******************** IMPORTANT ***********************
To make this policy package active, execute:

semodule -i my-se-policy.pp

// 혹은, 특정 시간을 지정해서,
$ sudo ausearch --start 01/13/2025 00:00:00 -c 'my-test-app' --raw  | audit2allow -M my-se-policy

위의 명령어를 실행하면 my-se-policy.pp, my-se-policy.te 2개의 파일이 생성되는데요, te(type enforcement) 파일이 텍스트 유형의 설정 파일이고 그것을 컴파일한 결과물이 pp(policy package)입니다.

te 파일을 보면,

$ cat my-se-policy.te

module my-se-policy 1.0;  # 이곳의 모듈 이름은 반드시 파일명과 일치해야 합니다.

require {
        type unconfined_service_t;
        class bpf map_create;
}

#============= unconfined_service_t ==============
allow unconfined_service_t self:bpf map_create;

대략 bpf에 대한 map_create 접근을 unconfined_service_t로 분류된 프로세스에 허용하는 설정임을 짐작게 합니다. 따라서 별다르게 수정할 것이 없다면 이대로 SELinux 정책에 다음과 같은 명령어로 반영하면 됩니다.

$ sudo semodule -i my-se-policy.pp

$ sudo semodule -l | grep my-se-policy
my-se-policy    1.0

// 만약 정책을 삭제하고 싶다면,
$ sudo semodule -r my-se-policy
libsemanage.semanage_direct_remove_key: Removing last my-se-policy module (no other my-se-policy module exists at another priority).

끝입니다, 이후 systemd로 등록된 데몬을 실행하면 오류 없이 잘 동작합니다. ^^




혹시나 te 파일을 수정했다면 다시 컴파일해서 pp 파일을 생성해야 합니다. 이를 위해 make 명령어를 사용할 수 있는데요,

$ make -f /usr/share/selinux/devel/Makefile my-se-policy.pp
Compiling targeted my-se-policy module
/usr/bin/checkmodule:  loading policy configuration from tmp/my-se-policy.tmp
/usr/bin/checkmodule:  policy configuration loaded
/usr/bin/checkmodule:  writing binary representation (version 19) to tmp/my-se-policy.mod
Creating targeted my-se-policy.pp policy package
rm tmp/my-se-policy.mod.fc tmp/my-se-policy.mod

위의 명령어는 현재 디렉터리에서 (pp 파일명으로 지정한) my-se-policy.te 파일을 찾아 빌드해 pp 파일을 생성합니다. 또 다른 방법으로는, pp 파일을 make가 아닌 SELinux 도구로 생성하는 방법이 있는데요,

$ checkmodule -M -m -o my-se-policy.mod my-se-policy.te
checkmodule:  loading policy configuration from my-se-policy.te
checkmodule:  policy configuration loaded
checkmodule:  writing binary representation (version 19) to my-se-policy.mod

위와 같이 실행하면 my-se-policy.mod 파일이 생성되는데, 이것으로부터 pp 파일을 생성할 수 있습니다.

$ semodule_package -o my-se-policy.pp -m my-se-policy.mod

아마도, C/C++ 언어와 같은 빌드 단계로 따지자면 te 파일은 소스코드, mod 파일은 오브젝트 파일, pp 파일은 실행 모듈에 해당하는 것 같습니다.




참고로, SELinux가 적용된 환경이어도 관련 도구들이 없는 경우가 많을 것입니다. 따라서, 각각의 도구에 따라 관련 패키지를 별도로 설치해야 합니다.

$ semanage
-bash: semanage: command not found

// semanage command not found in CentOS 7 / 6 & RHEL 7 / 6 – Quick Fix
// https://www.itzgeek.com/how-tos/linux/centos-how-tos/semanage-command-not-found-in-centos-7-rhel-7-quick-fix.html

$ sudo yum -y install policycoreutils-python

$ seinfo -u
-bash: seinfo: command not found

$ sudo yum -y install setools-console

$ sealert -l "*"
-bash: sealert: command not found

$ sudo yum -y install setroubleshoot

$ sepolicy generate --init /usr/bin/remon_app/my-test-appion-linux-amd64
-bash: sepolicy: command not found

$ sudo yum -y install selinux-policy-devel




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 1/14/2025]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




1  2  3  [4]  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13843정성태12/13/20244380오류 유형: 938. Docker container 내에서 빌드 시 error MSB3021: Unable to copy file "..." to "...". Access to the path '...' is denied.
13842정성태12/12/20244521디버깅 기술: 205. Windbg - KPCR, KPRCB
13841정성태12/11/20244854오류 유형: 937. error MSB4044: The "ValidateValidArchitecture" task was not given a value for the required parameter "RemoteTarget"
13840정성태12/11/20244428오류 유형: 936. msbuild - Your project file doesn't list 'win' as a "RuntimeIdentifier"
13839정성태12/11/20244850오류 유형: 936. msbuild - error CS1617: Invalid option '12.0' for /langversion. Use '/langversion:?' to list supported values.
13838정성태12/4/20244594오류 유형: 935. Windbg - Breakpoint 0's offset expression evaluation failed.
13837정성태12/3/20245049디버깅 기술: 204. Windbg - 윈도우 핸들 테이블 (3) - Windows 10 이상인 경우
13836정성태12/3/20244610디버깅 기술: 203. Windbg - x64 가상 주소를 물리 주소로 변환 (페이지 크기가 2MB인 경우)
13835정성태12/2/20245054오류 유형: 934. Azure - rm: cannot remove '...': Directory not empty
13834정성태11/29/20245281Windows: 275. C# - CUI 애플리케이션과 Console 윈도우 (Windows 10 미만의 Classic Console 모드인 경우) [1]파일 다운로드1
13833정성태11/29/20244964개발 환경 구성: 737. Azure Web App에서 Scale-out으로 늘어난 리눅스 인스턴스에 SSH 접속하는 방법
13832정성태11/27/20244900Windows: 274. Windows 7부터 도입한 conhost.exe
13831정성태11/27/20244381Linux: 111. eBPF - BPF_MAP_TYPE_PERF_EVENT_ARRAY, BPF_MAP_TYPE_RINGBUF에 대한 다양한 용어들
13830정성태11/25/20245179개발 환경 구성: 736. 파이썬 웹 앱을 Azure App Service에 배포하기
13829정성태11/25/20245135스크립트: 67. 파이썬 - Windows 버전에서 함께 설치되는 py.exe
13828정성태11/25/20244438개발 환경 구성: 735. Azure - 압축 파일을 이용한 web app 배포 시 디렉터리 구분이 안 되는 문제파일 다운로드1
13827정성태11/25/20245088Windows: 273. Windows 환경의 파일 압축 방법 (tar, Compress-Archive)
13826정성태11/21/20245317닷넷: 2313. C# - (비밀번호 등의) Console로부터 입력받을 때 문자열 출력 숨기기(echo 끄기)파일 다운로드1
13825정성태11/21/20245649Linux: 110. eBPF / bpf2go - BPF_RINGBUF_OUTPUT / BPF_MAP_TYPE_RINGBUF 사용법
13824정성태11/20/20244742Linux: 109. eBPF / bpf2go - BPF_PERF_OUTPUT / BPF_MAP_TYPE_PERF_EVENT_ARRAY 사용법
13823정성태11/20/20245277개발 환경 구성: 734. Ubuntu에 docker, kubernetes (k3s) 설치
13822정성태11/20/20245147개발 환경 구성: 733. Windbg - VirtualBox VM의 커널 디버거 연결 시 COM 포트가 없는 경우
13821정성태11/18/20245076Linux: 108. Linux와 Windows의 프로세스/스레드 ID 관리 방식
13820정성태11/18/20245232VS.NET IDE: 195. Visual C++ - C# 프로젝트처럼 CopyToOutputDirectory 항목을 추가하는 방법
13819정성태11/15/20244477Linux: 107. eBPF - libbpf CO-RE의 CONFIG_DEBUG_INFO_BTF 빌드 여부에 대한 의존성
13818정성태11/15/20245258Windows: 272. Windows 11 24H2 - sudo 추가
1  2  3  [4]  5  6  7  8  9  10  11  12  13  14  15  ...