SELinux 정책 작성

Android 오픈소스 프로젝트(AOSP)는 모든 Android 기기에서 공통으로 사용되는 애플리케이션 및 서비스를 위한 안정적인 기본 정책을 제공합니다. AOSP 참여자들이 이 정책을 정기적으로 개선합니다. 핵심 정책은 기기 내 최종 정책의 약 90~95%를 차지하며 기기별 맞춤설정은 나머지 5~10%를 차지합니다. 본 문서에서는 기기별 맞춤설정, 기기별 정책 작성법 및 이 과정에서 주의해야 할 사항을 중점적으로 설명합니다.

기기 불러오기

기기별 정책을 작성할 때는 다음 단계를 따르세요.

허용 모드에서 실행

기기가 허용 모드이면 거부가 로그되지만 시행되지는 않습니다. 허용 모드는 다음 두 가지 이유로 중요합니다.

  • 허용 모드는 정책 불러오기가 다른 초기 기기 불러오기 작업을 지연시키지 않도록 합니다.
  • 시행된 거부는 다른 거부를 가릴 수 있습니다. 예를 들어 파일 액세스 권한에는 일반적으로 디렉터리 검색, 파일 열기, 파일 읽기가 포함되어 있습니다. 시행 모드에서는 디렉터리 검색 거부만 발생할 수 있습니다. 반면 허용 모드는 발생한 모든 거부를 보여 줍니다.

기기를 허용 모드로 설정하는 가장 간단한 방법은 커널 명령줄을 이용하는 것입니다. 기기의 BoardConfig.mk 파일에 다음을 추가합니다. platform/device/<vendor>/<target>/BoardConfig.mk. 이 명령줄을 수정한 후 make cleanmake bootimage를 차례로 실행하고 새 부팅 이미지를 플래시합니다.

그리고 다음을 통해 허용 모드 상태인지 확인합니다.

adb shell getenforce

전역 허용 모드는 2주 정도 지속되는 것이 적당합니다. 대다수의 거부가 해결되면 시행 모드로 되돌려서 버그가 발생하는 대로 처리합니다. 계속 거부가 발생하는 도메인이나 한창 개발 중인 서비스인 경우 잠시 허용 모드로 전환할 수는 있지만 가능한 한 빨리 시행 모드로 다시 전환하세요.

시행 모드 조기 적용

시행 모드에서는 거부가 로그로 남는 동시에 시행됩니다. 가능한 한 일찍 기기를 시행 모드로 설정하는 것이 가장 좋습니다. 기기별 정책을 만들고 시행할 때까지 기다리다 보면 흔히 제품에 버그가 생기고 사용자 환경이 저하되는 결과를 초래합니다. dogfood에 참여할 수 있을 만큼 일찍 시작하여 실제 환경에서 모든 기능을 전체적으로 테스트할 수 있도록 하세요. 일찍 시작하면 보안 문제를 설계 결정에 반영할 수도 있습니다. 반대로 확인된 거부만을 바탕으로 권한을 부여하는 것은 안전하지 않은 접근방식입니다. 이때를 활용하여 기기의 보안 감사를 실행하고 허용되어서는 안 되는 동작이 발생하는 버그를 신고하세요.

기존 정책 삭제

새 기기의 기기별 정책을 작성할 때 다음과 같은 이유로 처음부터 새로 만드는 편이 좋습니다.

핵심 서비스의 거부 해결

핵심 서비스에서 발생한 거부는 일반적으로 파일에 라벨을 지정해 해결합니다. 예:

avc: denied { open } for pid=1003 comm=”mediaserver” path="/dev/kgsl-3d0”
dev="tmpfs" scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0
tclass=chr_file permissive=1
avc: denied { read write } for pid=1003 name="kgsl-3d0" dev="tmpfs"
scontext=u:r:mediaserver:s0
tcontext=u:object_r:device:s0 tclass=chr_file permissive=1

/dev/kgsl-3d0에 적절한 라벨을 지정해 완전히 해결되었습니다. 이 예에서 tcontextdevice입니다. 이는 더 구체적인 라벨이 지정되지 않는 한 /dev의 모든 사항에 'device' 라벨이 할당되는 기본 컨텍스트를 나타냅니다. 여기에서 단순히 audit2allow의 출력을 사용하게 되면 부정확하고 지나치게 관대한 규칙이 됩니다.

이러한 문제를 해결하려면 파일에 더 구체적인 라벨, 즉 이 경우에는 gpu_device를 지정합니다. 핵심 정책의 mediaserver에는 이미 gpu_device 액세스에 필요한 권한이 있으므로 추가 권한이 필요하지 않습니다.

핵심 정책에 사전 정의된 유형으로 라벨이 지정되어야 하는 기타 기기별 파일은 다음과 같습니다.

일반적으로 기본 라벨에 권한을 부여하는 것은 옳지 않습니다. 대부분의 권한은 neverallow 규칙에 따라 금지되지만 명시적으로 금지되지 않은 경우에도 특정 라벨을 제공하는 것이 좋습니다.

새로운 서비스 라벨 지정 및 거부 해결

init에서 실행된 서비스는 자체 SELinux 도메인에서 실행되어야 합니다. 다음 예는 "foo" 서비스를 자체 SELinux 도메인에 두고 권한을 부여합니다.

기기의 init.device.rc 파일에서 다음으로 서비스가 실행됩니다.

service foo /system/bin/foo
    class core
  1. 새 도메인 'foo'를 만듭니다.

    다음과 같은 내용으로 device/manufacturer/device-name/sepolicy/foo.te 파일을 만듭니다.

    # foo service
    type foo, domain;
    type foo_exec, exec_type, file_type;
    
    init_daemon_domain(foo)
    

    이것이 foo SELinux 도메인의 초기 템플릿입니다. 여기에 실행 파일이 실행하는 특정 작업을 바탕으로 한 규칙을 추가할 수 있습니다.

  2. /system/bin/foo 라벨

    다음을 device/manufacturer/device-name/sepolicy/file_contexts에 추가합니다.

    /system/bin/foo   u:object_r:foo_exec:s0
    

    이렇게 하면 실행 파일에 적절한 라벨이 지정되어서 SELinux가 적합한 도메인에서 서비스를 실행할 수 있습니다.

  3. 부팅 및 시스템 이미지를 빌드하고 플래시합니다.
  4. 도메인의 SELinux 규칙을 수정합니다.

    거부 이력을 이용하여 필수 권한을 결정합니다. audit2allow 도구에서 적절한 가이드라인을 찾을 수 있지만 정책 작성에만 활용하세요. 출력된 내용을 복사해서 사용해서는 안 됩니다.

시행 모드로 전환

허용 모드에서 문제를 해결하는 것도 괜찮지만 가능한 한 빨리 시행 모드로 전환하여 계속 유지하는 편이 좋습니다.

일반적인 실수

다음은 기기별 정책을 작성할 때 흔히 발생하는 실수를 해결하는 방법입니다.

부정형의 남용

다음 규칙 예는 창문을 열어 둔 채 현관문을 잠그는 것과 비슷합니다.

allow { domain -untrusted_app } scary_debug_device:chr_file rw_file_perms

서드 파티 앱을 제외한 모든 사용자가 디버그 기기에 액세스하게 하겠다는 의도는 명확합니다.

하지만 이 규칙에는 몇 가지 결함이 있습니다. untrusted_app의 제외 대상은 모든 앱이 isolated_app 도메인에서 선택적으로 서비스를 실행할 수 있으므로 쉽게 우회할 수 있습니다. 마찬가지로 타사 앱의 새 도메인이 AOSP에 추가되면 scary_debug_device에 액세스할 수도 있습니다. 이 규칙은 지나치게 관대합니다. 대부분의 도메인에서는 이 디버깅 도구에 액세스하여 얻는 이점이 없습니다. 따라서 이 규칙은 액세스가 필요한 도메인만 허용되도록 다시 작성되어야 합니다.

프로덕션의 디버깅 기능

디버그 기능과 정책은 프로덕션 빌드에서 빠져야 합니다.

가장 간단한 대안은 SELinux가 adb rootadb shell setenforce 0과 같은 eng/userdebug 빌드에서 사용 중지된 경우에만 디버그 기능을 허용하는 것입니다.

또 다른 안전한 방법은 userdebug_or_eng 구문에 디버그 권한을 포함하는 것입니다.

정책의 규모 증가 추세

실제 현장의 SEAndroid 정책(SEAndroid Policies in the Wild)에서는 기기 정책 맞춤설정의 증가 추세와 관련해 우려를 표현하고 있습니다. 기기별 정책은 기기에서 실행되는 모든 정책의 5~10%를 차지해야 합니다. 맞춤설정이 20% 이상이라면 과도한 권한이 있는 도메인과 유효하지 않은 정책이 포함되어 있다고 거의 확신할 수 있습니다.

불필요하게 큰 정책은 다음과 같은 문제를 일으킵니다.

  • 정책이 램디스크에 배치될 때와 커널 메모리에 로드될 때 메모리에 두 번 액세스해야 합니다.
  • 더 큰 부팅 이미지가 필요하여 디스크 공간이 낭비됩니다.
  • 런타임 정책 조회 시간에 영향을 미칩니다.

다음 예에서 제조업체별 정책이 기기 내 정책의 50%와 40%에 이르는 기기 2개를 확인할 수 있습니다. 아래에서 보다시피 정책을 다시 작성하여 기능을 잃지 않고도 보안이 크게 개선되었습니다. AOSP 기기 Shamu와 Flounder는 비교를 위해 포함했습니다.

그림 1: 보안 감사 후 기기별 정책 규모 비교

그림 1. 보안 감사 후 기기별 정책 규모 비교

두 경우 모두 정책의 규모와 권한 개수가 크게 감소했습니다. 정책 규모가 감소한 이유는 거의 전적으로 불필요한 권한을 제거했기 때문이며 그중 대부분은 audit2allow가 만들어 낸 후 정책에 무분별하게 추가된 규칙일 가능성이 높습니다. 또한 두 기기 모두 유효하지 않은 도메인 문제가 있었습니다.

dac_override 권한 부여

dac_override 거부는 문제를 일으킨 프로세스가 잘못된 Unix user/group/world 권한을 가진 파일에 액세스하려고 시도한다는 의미입니다. dac_override 권한을 부여하는 것은 대부분 적절한 해결책이 아닙니다. 대신 파일 또는 프로세스의 Unix 권한을 변경합니다. init, vold, installd 같은 일부 도메인은 다른 프로세스의 파일에 액세스하기 위해 Unix 파일 권한을 재정의하는 기능이 필요합니다. 자세한 설명은 댄 월시의 블로그를 참조하세요.