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

비밀번호

댓글 작성자
 




... 106  107  108  109  110  [111]  112  113  114  115  116  117  118  119  120  ...
NoWriterDateCnt.TitleFile(s)
11150정성태2/21/201719353.NET Framework: 645. Visual Studio Fakes 기능에서 Shim... 클래스가 생성되지 않는 경우 [5]
11149정성태2/21/201723058오류 유형: 378. A 64-bit test cannot run in a 32-bit process. Specify platform as X64 to force test run in X64 mode on X64 machine.
11148정성태2/20/201721975.NET Framework: 644. AppDomain에 대한 단위 테스트 시 알아야 할 사항
11147정성태2/19/201721207오류 유형: 377. Windows 10에서 Fake 어셈블리를 생성하는 경우 빌드 시 The type or namespace name '...' does not exist in the namespace 컴파일 오류 발생
11146정성태2/19/201719901오류 유형: 376. Error VSP1033: The file '...' does not contain a recognized executable image. [2]
11145정성태2/16/201721356.NET Framework: 643. 작업자 프로세스(w3wp.exe)가 재시작되는 시점을 알 수 있는 방법 - 두 번째 이야기 [4]파일 다운로드1
11144정성태2/6/201724764.NET Framework: 642. C# 개발자를 위한 Win32 DLL export 함수의 호출 규약 (부록 1) - CallingConvention.StdCall, CallingConvention.Cdecl에 상관없이 왜 호출이 잘 될까요?파일 다운로드1
11143정성태2/5/201722112.NET Framework: 641. [Out] 형식의 int * 인자를 가진 함수에 대한 P/Invoke 호출 방법파일 다운로드1
11142정성태2/5/201730136.NET Framework: 640. 닷넷 - 배열 크기의 한계 [2]파일 다운로드1
11141정성태1/31/201724423.NET Framework: 639. C# 개발자를 위한 Win32 DLL export 함수의 호출 규약 (4) - CLR JIT 컴파일러의 P/Invoke 호출 규약 [1]파일 다운로드1
11140정성태1/27/201720170.NET Framework: 638. RSAParameters와 RSA파일 다운로드1
11139정성태1/22/201722898.NET Framework: 637. C# 개발자를 위한 Win32 DLL export 함수의 호출 규약 (3) - x64 환경의 __fastcall과 Name mangling [1]파일 다운로드1
11138정성태1/20/201721196VS.NET IDE: 113. 프로젝트 생성 시부터 "Enable the Visual Studio hosting process" 옵션을 끄는 방법 - 두 번째 이야기 [3]
11137정성태1/20/201719858Windows: 135. AD에 참여한 컴퓨터로 RDP 연결 시 배경 화면을 못 바꾸는 정책
11136정성태1/20/201719064오류 유형: 375. Hyper-V 내에 구성한 Active Directory 환경의 시간 구성 방법 - 두 번째 이야기
11135정성태1/20/201720055Windows: 134. Windows Server 2016의 작업 표시줄에 있는 시계가 사라졌다면? [1]
11134정성태1/20/201727450.NET Framework: 636. System.Threading.Timer를 이용해 타이머 작업을 할 때 유의할 점 [5]파일 다운로드1
11133정성태1/20/201723597.NET Framework: 635. C# 개발자를 위한 Win32 DLL export 함수의 호출 규약 (2) - x86 환경의 __fastcall [1]파일 다운로드1
11132정성태1/19/201735112.NET Framework: 634. C# 개발자를 위한 Win32 DLL export 함수의 호출 규약 (1) - x86 환경에서의 __cdecl, __stdcall에 대한 Name mangling [1]파일 다운로드1
11131정성태1/13/201724002.NET Framework: 633. C# - IL 코드 분석을 위한 팁 [2]
11130정성태1/11/201724538.NET Framework: 632. x86 실행 환경에서 SECURITY_ATTRIBUTES 구조체를 CreateEvent에 전달할 때 예외 발생파일 다운로드1
11129정성태1/11/201728904.NET Framework: 631. async/await에 대한 "There Is No Thread" 글의 부가 설명 [9]파일 다운로드1
11128정성태1/9/201723297.NET Framework: 630. C# - Interlocked.CompareExchange 사용 예제 [3]파일 다운로드1
11127정성태1/8/201722917기타: 63. (개발자를 위한) Visual Studio의 "with MSDN" 라이선스 설명
11126정성태1/7/201727620기타: 62. Edge 웹 브라우저의 즐겨찾기(Favorites)를 편집/백업/복원하는 방법 [1]파일 다운로드1
11125정성태1/7/201724481개발 환경 구성: 310. IIS - appcmd.exe를 이용해 특정 페이지에 클라이언트 측 인증서를 제출하도록 설정하는 방법
... 106  107  108  109  110  [111]  112  113  114  115  116  117  118  119  120  ...