Microsoft MVP성태의 닷넷 이야기
Linux: 112. Linux - 데몬을 위한 SELinux 보안 정책 설정 [링크 복사], [링크+제목 복사],
조회: 3631
글쓴 사람
정성태 (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

비밀번호

댓글 작성자
 




... 31  32  33  34  35  36  37  38  39  40  41  42  43  44  [45]  ...
NoWriterDateCnt.TitleFile(s)
12810정성태8/27/202115472.NET Framework: 1107. .NET Core/5+에서 동적 컴파일한 C# 코드를 (Breakpoint도 활용하며) 디버깅하는 방법 - #line 지시자파일 다운로드1
12809정성태8/26/202115441.NET Framework: 1106. .NET Core/5+에서 C# 코드를 동적으로 컴파일/사용하는 방법 [1]파일 다운로드1
12808정성태8/25/202117071오류 유형: 758. go: ...: missing go.sum entry; to add it: go mod download ...
12807정성태8/25/202117863.NET Framework: 1105. C# 10 - (9) 비동기 메서드가 사용할 AsyncMethodBuilder 선택 가능파일 다운로드1
12806정성태8/24/202114410개발 환경 구성: 601. PyCharm - 다중 프로세스 디버깅 방법
12805정성태8/24/202116117.NET Framework: 1104. C# 10 - (8) 분해 구문에서 기존 변수의 재사용 가능파일 다운로드1
12804정성태8/24/202116287.NET Framework: 1103. C# 10 - (7) Source Generator V2 APIs
12803정성태8/23/202116791개발 환경 구성: 600. pip cache 디렉터리 옮기는 방법
12802정성태8/23/202117212.NET Framework: 1102. .NET Conf Mini 21.08 - WinUI 3 따라해 보기 [1]
12801정성태8/23/202116797.NET Framework: 1101. C# 10 - (6) record class 타입의 ToString 메서드를 sealed 처리 허용파일 다운로드1
12800정성태8/22/202117179개발 환경 구성: 599. PyCharm - (반대로) 원격 프로세스가 PyCharm에 디버그 연결하는 방법
12799정성태8/22/202117451.NET Framework: 1100. C# 10 - (5) 속성 패턴의 개선파일 다운로드1
12798정성태8/21/202118810개발 환경 구성: 598. PyCharm - 원격 프로세스를 디버그하는 방법
12797정성태8/21/202116183Windows: 197. TCP의 MSS(Maximum Segment Size) 크기는 고정된 것일까요?
12796정성태8/21/202117176.NET Framework: 1099. C# 10 - (4) 상수 문자열에 포맷 식 사용 가능파일 다운로드1
12795정성태8/20/202117495.NET Framework: 1098. .NET 6에 포함된 신규 BCL API - 스레드 관련
12794정성태8/20/202116872스크립트: 23. 파이썬 - WSGI를 만족하는 최소한의 구현 코드 및 PyCharm에서의 디버깅 방법 [1]
12793정성태8/20/202117660.NET Framework: 1097. C# 10 - (3) 개선된 변수 초기화 판정파일 다운로드1
12792정성태8/19/202118758.NET Framework: 1096. C# 10 - (2) 전역 네임스페이스 선언파일 다운로드1
12791정성태8/19/202115590.NET Framework: 1095. C# COM 개체를 C++에서 사용하는 예제 [3]파일 다운로드1
12790정성태8/18/202119534.NET Framework: 1094. C# 10 - (1) 구조체를 생성하는 record struct파일 다운로드1
12789정성태8/18/202118194개발 환경 구성: 597. PyCharm - 윈도우 환경에서 WSL을 이용해 파이썬 앱 개발/디버깅하는 방법
12788정성태8/17/202115733.NET Framework: 1093. C# - 인터페이스의 메서드가 다형성을 제공할까요? (virtual일까요?)파일 다운로드1
12787정성태8/17/202116114.NET Framework: 1092. (책 내용 수정) "4.5.1.4 인터페이스"의 "인터페이스와 다형성"
12786정성태8/16/202118045.NET Framework: 1091. C# - Python range 함수 구현 (2) INumber<T>를 이용한 개선 [1]파일 다운로드1
12785정성태8/16/202116505.NET Framework: 1090. .NET 6 Preview 7에 추가된 숫자 형식에 대한 제네릭 연산 지원 [1]파일 다운로드1
... 31  32  33  34  35  36  37  38  39  40  41  42  43  44  [45]  ...