기본 개요
리소스와 소유권
- 리눅스는 다중 사용자 운영체제로 유닉스로부터 사용자 개념을 물려받았다.
- 각 사용자 계정은 실행 파일, 파일, 장치, 기타 리눅스 자산에 접근할 수 있는 사용자 ID와 연결된다.
사용자
- 프로세스를 실행하고 파일을 소유한다. 프로세스는 커널이 메인 메모리에 로드해 실행하는 일종의 프로그램이다.
파일
- 소유자가 존재한다. 기본적으로는 파일을 만든 사용자가 파일을 소유한다.
프로세스
- 의사소통과 지속성을 위해 파일을 사용한다. 사용자도 파일을 간접적으로 사용하기도 하지만 그러려면 프로세스를 통해야 한다.
샌드박스
- 샌드박스라는 용어의 정의는 다소 모호해서, 제일(jail)에서 컨테이너, 가상머신에 이르기까지 커널이나 사용자 영역에서 관리할 수 있는 다양한 방법을 가리킬 수 있다.
- 일반적으로 샌드박스에서 실행되는 항목이 있다면 감시 메커니즘은 샌드박스 프로세스와 호스팅 환경 간에 어느정도 격리를 시행한다.
- 뒷부분에서 더 자세히 언급한다.
접근 제어 유형
- 개념적으로 다양한 접근 제어 유형이 있으나 리눅스 관점에서 가장 중요하고 관련성이 높은 두 가지는 임의 접근 제어(discretionary)와 강제 접근 제어(mandatory) 이다.
- 임의 접근 제어(DAC): 사용자의 신분을 기반으로 리소스에 대한 접근을 제한할 수 있다. 여기서 임의라 함은 특정 권한을 가진 사용자가 이를 다른 사용자에게 전달할수 있음을 의미한다.
- 강제 접근 제어(MAC) : 보안 수준을 나타내는 계층 모델을 기반으로 한다. 사용자에게는 허용 등급이 할당되고 리소스에는 보안 레이블이 할당된다. 사용자는 자신의 허용 등급 이하로 설정된 리소스에만 접근할 수 있다.
- 관리자가 모든 권한을 설정하여 엄격하고 배타적으로 접근을 제어한다. 즉, 사용자는 리소스를 소유하고 있어도 권한을 스스로 설정할 수 없다.
- SE 리눅스가 가장 잘 알려진 리눅스용 강제 접근 제어 구현 모듈이다.
- 우분투에서는 앱 아머(App Armor)가 널리 쓰인다.
사용자
- 리눅스에서는 목적 또는 의도적인 사용 관점에서 사용자 계정을 두 가지 유형으로 구분한다.
- 시스템 사용자 또는 시스템 계정 : 일반적으로 프로그램(daemon)은 이런 유형의 계정을 사용하여 백그라운드 프로세스를 실행한다.
- 일반 사용자 : 일반적으로 셸을 통해 리눅스를 대화식으로 사용하는 인간 사용자 유형
- 리눅스는 UID를 통해 사용자를 식별하며, 사용자는 GID를 통해 식별 되나는 하나 이상의 그룹에 속한다.
- root 라고 하는 UID 0를 사진 특별한 사용자는 제한이 적용되지 않는다.
- 일반적인 경우 root 사용자로 작업하는 것은 피해야 한다.
- 권한이 너무 많기 때문에 시스템을 파괴하기 쉽다.
- 각각의 리눅스 배포판 마다 UID 범위를 관리하는 방법을 결정하는 고유한 방법이 존재한다.
- 예를 들어 systemd 기반 배보판에는 아래와 같은 규칙이 있다
- UID 0 : root 사용자
- UID 1~999 : 시스템 사용자에게 지정됨
- UID 65534 : nobody 사용자.
- 나머지 UID : 일반 사용자
로컬에서 사용자 관리하기
- 첫 번째 옵션은 사용자를 로컬에서 관리하는 것이다.
- 즉, 시스템에 로컬로 저장된 정보만 사용되며 사용자 관련 정보는 시스템 네트워크에서 공유되지 않는다.
- 리눅스는 파일 기반 인터페이스를 사용하여 사용자 관리를 구현한다.
|목적|파일| |—|—| |사용자 데이터베이스|/etc/passwd| |그룹 데이터베이스|/etc/group| |사용자 비밀번호|/etc/shadow| |그룹 비밀번호|/etc/gshadow|
- /etc/passwd 에는 아래와 같은 내용이 저장되어 있다
1 2 3 4 5 6
root:x:0:0:root:/root:/usr/local/bin/fish daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin
- 보다시피 왼쪽에서부터 사용자 이름, 사용자의 비밀 번호(x는 비밀번호가 /etc/shadow에 저장됨을 의미), UID, GID, 이름 또는 전화번호, 사용자의 홈 디렉터리, 사용할 로그인 셸 이다.
중앙집중식 사용자 관리
- 전문적인 설정으로 사용자를 관리해야 하는 시스템이나 서버가 둘 이상인 경우, 로컬에서 사용자를 관리하는 방식은 구식이다.
- 이런 경우 중앙에서 사용자를 관리하지만 하나의 특정 시스템에 로컬로 적용할 수 있는 관리 방식이 필요하다.
- 요구 사항과 예산에 따라 사용 가능한 몇 가지 접근 방식이 있다.
- 디렉터리 기반 : LDAP는 수십년된 프로토콜 제품군으로 현재 IETF에서 공식화되어 있으며, IP를 통해 분산 디렉터리에 접근하고 유지 관리하는 방법을 정의힌다.
- 예를 들어 Keycloak 같은 프로젝트를 이용해 LDAP 서버를 직접 실행할 수도 있고, 애저 액티브 디렉터리(Azure Active Directory)같은 클라우드 공급자의 제품을 사용할 수도 있다.
- 네트워크 이용 : 커버로스(Kerberos)를 사용하면 네트워크로 사용자를 인증할 수 있다.
- 구성 관리 시스템 사용 : Ansible, Chef, Puppet, SaltStack 등의 시스템들을 사용에 여러 컴퓨터에서 일관되게 사용자를 생성할 수 있다.
- 디렉터리 기반 : LDAP는 수십년된 프로토콜 제품군으로 현재 IETF에서 공식화되어 있으며, IP를 통해 분산 디렉터리에 접근하고 유지 관리하는 방법을 정의힌다.
권한
파일 권한
- 파일 권한은 ‘리소스에 접근’한다는 리눅스 개념의 핵심이다.
- 리눅스는 모든 것이 파일이기 때문이다.
- 다음과 같이 좁은 범위부터 넓은 범위까지 세 가지 유형/범위의 권한이 있다
- 사용자 : 파일 소유자
- 그룹 : 하나 이상의 구성원
- 나머지 : 그 밖의 모두
- 또한 세가지 접근 유형이 존재한다.
- 읽기(r) : 일반 파일의 경우 사용자가 파일 내용을 볼 수 있다. 디렉터리의 경우 사용자는 디렉터리에 있는 파일의 이름을 볼 수 있다.
- 쓰기(w) : 일반 파일의 경우 사용자가 파일을 수정하고 삭제할 수 있다. 디렉터리의 경우 사용자가 디렉터리에서 파일을 만들고 이름을 바꾸고 삭제할 수 있다.
- 실행(x) : 이 옵션을 켜면 사용자가 읽기 권한도 가지고 있는 경우 파일을 실행할 수도 있다. 디렉터리의 경우 사용자가 디렉터리의 파일 정보에 접근할 수 있도록 허용해 사실상 사용자가 디렉토리로 이동(cd)하거나 내용을 나열(ls)할 수 있게 만들어준다.
- 그 밖의 파일 접근 속성
- s : 실행 파일에 적용되는 setuid/setgid 권한이다. 이를 실행하는 사용자는 파일 소유자 또는 그룹의 유효한 권한을 상속 받는다.
- t : 디렉터리에만 관련된 스티키 비트(sticky bit)이다. 이 옵션이 설정되면 해당 디렉토리/파일을 소유한 사용자가 아닌 비 root 사용자가 파일을 삭제하지 못하게 방지한다.
- chattr 를 통해 사용할 수 있는 리눅스의 특수 설정 또한 존재한다.
파일의 권한은
. rwx rwx rwx
로 출력된다. 왼쪽부터 띄어쓰기를 구분으로 파일 형식, 파일 소유자 권한, 그룹 권한, 나머지 권한이다.- 이러한 권한은
chmod
로 변경 가능하다. 원하는 권한 설정을 명시적으로 지정하거나(예:664), 단축어를 사용할 수 있다(+x) chown
,chgrp
를 통해 소유권을 변경할 수도 있다.
프로세스 권한
credentials
메뉴얼 페이지의 설명에 따르면 런타임권한과 관련된 다양한 사용자ID가 있다- RUID(Real UID) : 프로세스를 시작한 사용자의 UID이다. 즉 인간 사용자 관점에서의 프로세스 소유권을 나타낸다.
- 프로세스 자체에서
getuid
를 통해 RUID를 얻을 수 있으며, - 셸에서
stat -c "%u %g" /proc/$pid/
를 통해 쿼리할 수 있다
- 프로세스 자체에서
- EUID : 리눅스 커널은 메시지 대기열과 같은 공유 리소스에 접근 할 때 EUID(Effective user ID)를 사용해 프로세스가 갖는 권한을 결정한다.
- 전통적인 유닉스 시스템에서는 EUID가 파일 접근에도 사용된 반면, 리눅스는 파일 접근 권한에 전용 파일 시스템 UID를 사용했었다.
geteuid
를 통해 EUID를 얻을 수 있다
- 저장된 SUID : 프로세스가 실제 UID와 저장된 SUID 사이에서 유효 UID를 전환함으로써 권한을 가정할 수 있을 때 사용된다.
- 예를 들어 프로세스가 특정 네트워크 포트를 사용하도록 허용하려면
root
로 실행되는 것처럼 높은 권한이 필요하다. 프로세스는getresuid
를 통해 저장된 SUID를 가져올 수 있다.
- 예를 들어 프로세스가 특정 네트워크 포트를 사용하도록 허용하려면
- FUID : 리눅스 전용 ID로서 파일 접근 권한을 결정하는데 사용됐으며, 원래 파일 서버가 일반 사용자를 대행해서 동작하되 해당 사용자가 보내는 시그널로부터 프로세스를 격리하는 사용 사례를 지원하기 위해 도입됐다.
- 프로그램은 일반적으로 일반적으로 UID를 직접 조작하지 않는다.
- 커널은 EUID가 변경되는 시기를 추적하고 이에 따라 파일 시스템 UID를 자동으로 변경한다.
- 기술적으로 이 UID는 커널 2.0 이후로 더 이상 필요하지 않지만 호환성을 위해 여전히 지원된다.
- RUID(Real UID) : 프로세스를 시작한 사용자의 UID이다. 즉 인간 사용자 관점에서의 프로세스 소유권을 나타낸다.
- 처음에 자식 프로세스가
fork
를 통해 생성되면 이는 부모의 UID 복사본을 상속한다. - 또한
evecve
시스템 콜 중에는 프로세스의 RUID는 보존되지만 EUID와 저장된 SUID는 변경될 수 있다. - 이 외에도
chroot
를 비롯한 여러 샌드박스 기술같이 EUID에 영향을 끼치는 경우는 다양하다.
고급 권한 관리
캐퍼빌리티
- 유닉스 시스템의 전통을 이어받은 리눅스에서는 root 사용자가 프로세스를 실행할 때 아무런 제한이 없다. 즉 커널은 다음 두 가지 경우만 구분한다.
- EUID가 0인 권한 있는 프로세스는 커널 권한 검사를 후회한다.
- EUID가 0이 아닌, 권한이 없는 프로세스에 대해서는 위에 설명한 것과 같이 커널이 권한 검사를 수행한다.
- 캐퍼빌리티 시스템 콜이 도압되면서 이런 이분법적 세계관이 바뀌었다.
- 전통적으로
root
와 관련댔던 권한은 이제 개별 단위로 나뉘어서 스레드마다 독립적으로 할당할 수 있게 됐다. - 캐퍼빌리티는 일반적으로 시스템 수준의 작어벵만 관련된다.
- 즉, 대부분의 경우 이런 캐퍼빌리티에 반드시 의존하지 않아도 된다.
seccomp 프로필
- 이 샌드박스 기술의 기본 개념은
seccomp
라는 전용 시스템 콜을 사용하여 프로세스가 사용할 수 있는 시스템 콜을 제한할 수 있다는 것이다. seccomp
를 직접 관리하는 것이 불편할 수 있지만 큰 번거로움 없이 사용할 수 있는 방법도 있다.- 사용하면 전통적인 권한 위에 추가로 사용할 수 있는 유연한 메커니즘이 리눅스에 생긴다. ACL은 사용자의 그룹 내에 없는 사용자나 그룹에 권한을 부여할 수 있다는 점에서 전통적인 권한의 단점을 해결할 수 있다.
grep -i acl /boot/config*
명령을 사용해 배포판이 ACL을 지원하는지를 확인할 수 있다.- 파일시스템에 ACL을 사용하려면 마운트 시 acl 옵션을 사용해 활성화 해야 한다.
우수 사례
최소 권한
- 모든 사람과 프로세스는 주어진 작업을 수행하는 데 필요한 권한만 가져야 한다는 것이다.
setuid를 피하자
- setuid는 파괴력이 있기 때문에 공격자가 시스템을 장악하기 좋다. 따라서 setuid보다는 캐퍼빌리티를 활용하자
감사(auditing)
- 모든 작업들을 작업을 수행한 사람과 함께 변조할 수 없는 방식으로 결과 로그에 기록해야 한다. 이러한 읽기 전용 로그를 사용해 누가 언제 무엇을 했는지 확인할 수 있다.
정리
- 리눅스에서 실제 작업을 할 때는 사용자, 프로세스, 파일 간의 관계를 기억해야 한다.
- 이는 리눅스와 같은 다중 사용자 운영체제에서 안전하고 무사히 작업을 수행하고 손상을 방지하기 위해 매우 중요하다.