Microsoft MVP성태의 닷넷 이야기
Linux: 112. Linux - 데몬을 위한 SELinux 보안 정책 설정 [링크 복사], [링크+제목 복사],
조회: 3619
글쓴 사람
정성태 (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)
13162정성태11/15/202215708.NET Framework: 2069. .NET 7 - AOT(ahead-of-time) 컴파일 [1]
13161정성태11/14/202214361.NET Framework: 2068. C# - PublishSingleFile로 배포한 이미지의 역어셈블 가능 여부 (난독화 필요성) [4]
13160정성태11/11/202214311.NET Framework: 2067. C# - PublishSingleFile 적용 시 native/managed 모듈 통합 옵션
13159정성태11/10/202217636.NET Framework: 2066. C# - PublishSingleFile과 관련된 옵션 [3]
13158정성태11/9/202213326오류 유형: 826. Workload definition 'wasm-tools' in manifest 'microsoft.net.workload.mono.toolchain' [...] conflicts with manifest 'microsoft.net.workload.mono.toolchain.net7'
13157정성태11/8/202214365.NET Framework: 2065. C# - Mutex의 비동기 버전파일 다운로드1
13156정성태11/7/202215961.NET Framework: 2064. C# - Mutex와 Semaphore/SemaphoreSlim 차이점파일 다운로드1
13155정성태11/4/202214196디버깅 기술: 183. TCP 동시 접속 (연결이 아닌) 시도를 1개로 제한한 서버
13154정성태11/3/202214682.NET Framework: 2063. .NET 5+부터 지원되는 GC.GetGCMemoryInfo파일 다운로드1
13153정성태11/2/202215997.NET Framework: 2062. C# - 코드로 재현하는 소켓 상태(SYN_SENT, SYN_RECV)
13152정성태11/1/202214894.NET Framework: 2061. ASP.NET Core - DI로 추가한 클래스의 초기화 방법 [1]
13151정성태10/31/202214350C/C++: 161. Windows 11 환경에서 raw socket 테스트하는 방법파일 다운로드1
13150정성태10/30/202213244C/C++: 160. Visual Studio 2022로 빌드한 C++ 프로그램을 위한 다른 PC에서 실행하는 방법
13149정성태10/27/202213816오류 유형: 825. C# - CLR ETW 이벤트 수신이 GCHeapStats_V1/V2에 대해 안 되는 문제파일 다운로드1
13148정성태10/26/202213747오류 유형: 824. msbuild 에러 - error NETSDK1005: Assets file '...\project.assets.json' doesn't have a target for 'net5.0'. Ensure that restore has run and that you have included 'net5.0' in the TargetFramew
13147정성태10/25/202213013오류 유형: 823. Visual Studio 2022 - Unable to attach to CoreCLR. The debugger's protocol is incompatible with the debuggee.
13146정성태10/24/202214323.NET Framework: 2060. C# - Java의 Xmx와 유사한 힙 메모리 최댓값 제어 옵션 HeapHardLimit
13145정성태10/21/202214959오류 유형: 822. db2 - Password validation for user db2inst1 failed with rc = -2146500508
13144정성태10/20/202214654.NET Framework: 2059. ClrMD를 이용해 윈도우 환경의 메모리 덤프로부터 닷넷 모듈을 추출하는 방법파일 다운로드1
13143정성태10/19/202215602오류 유형: 821. windbg/sos - Error code - 0x000021BE
13142정성태10/18/202220399도서: 시작하세요! C# 12 프로그래밍
13141정성태10/17/202216137.NET Framework: 2058. [in,out] 배열을 C#에서 C/C++로 넘기는 방법 - 세 번째 이야기파일 다운로드1
13140정성태10/11/202215526C/C++: 159. C/C++ - 리눅스 환경에서 u16string 문자열을 출력하는 방법 [2]
13139정성태10/9/202213621.NET Framework: 2057. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프로부터 모든 닷넷 모듈을 추출하는 방법파일 다운로드1
13138정성태10/8/202216213.NET Framework: 2056. C# - await 비동기 호출을 기대한 메서드가 동기로 호출되었을 때의 부작용 [1]
13137정성태10/8/202214132.NET Framework: 2055. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프로부터 닷넷 모듈을 추출하는 방법
... [31]  32  33  34  35  36  37  38  39  40  41  42  43  44  45  ...