HDFS 권한 가이드

HDFS 권한 가이드 (Permissions Guide)

HDFS의 파일·디렉터리 권한 모델을 설명하는 문서예요. POSIX 모델과 많은 부분을 공유하며, 사용자 정체성, 그룹 매핑, 권한 검사, 슈퍼유저, ACL(접근 제어 목록)과 관련 설정 파라미터를 다룹니다.

출처: 문서

본문

개요 (Overview)

HDFS는 POSIX 모델과 많은 부분을 공유하는 파일·디렉터리 권한 모델을 구현합니다. 각 파일과 디렉터리는 소유자(owner)와 그룹(group)과 연관됩니다. 파일이나 디렉터리는 소유자 사용자, 그룹의 멤버인 다른 사용자, 그리고 그 밖의 모든 사용자에 대해 별도의 권한을 가집니다. 파일의 경우 읽기에는 r 권한이, 쓰기·append에는 w 권한이 필요해요. 디렉터리의 경우 내용 나열에는 r 권한이, 파일·디렉터리 생성·삭제에는 w 권한이, 디렉터리의 자식에 접근하려면 x 권한이 필요합니다.

POSIX 모델과 달리 실행 파일이라는 개념이 없으므로 파일에는 setuid·setgid 비트가 없어요. 디렉터리에도 단순화를 위해 setuid·setgid 비트가 없습니다. 스티키 비트(sticky bit)는 디렉터리에 설정할 수 있어서, 슈퍼유저·디렉터리 소유자·파일 소유자를 제외한 누구도 디렉터리 안의 파일을 삭제하거나 옮길 수 없게 합니다. 파일에 스티키 비트를 설정하는 것은 효과가 없어요. 종합적으로 파일이나 디렉터리의 권한을 그 mode라고 합니다. 일반적으로 Unix 관례에 따라 mode를 표현·표시하며, 이 문서에서도 8진수를 사용합니다. 파일·디렉터리가 생성되면 소유자는 클라이언트 프로세스의 사용자 정체성이고, 그룹은 부모 디렉터리의 그룹입니다(BSD 규칙).

HDFS는 또한 POSIX ACL(접근 제어 목록)을 선택적으로 지원해서 특정 명명된 사용자나 명명된 그룹에 대해 더 세밀한 규칙으로 파일 권한을 보강합니다. ACL은 이 문서에서 더 자세히 다룹니다.

HDFS에 접근하는 각 클라이언트 프로세스는 사용자 이름과 그룹 목록으로 구성된 두 부분 정체성을 가집니다. 클라이언트 프로세스가 접근하는 파일/디렉터리 foo에 대해 HDFS가 권한 검사를 해야 할 때마다,

  • 사용자 이름이 foo의 소유자와 일치하면 소유자 권한을 테스트합니다.
  • foo의 그룹이 그룹 목록의 어떤 멤버와 일치하면 그룹 권한을 테스트합니다.
  • 그렇지 않으면 foo의 other 권한을 테스트합니다.

권한 검사가 실패하면 클라이언트 연산이 실패합니다.

사용자 정체성 (User Identity)

Hadoop 0.22부터 Hadoop은 사용자 정체성을 결정하는 두 가지 다른 동작 모드를 지원하며, hadoop.security.authentication 속성으로 지정합니다.

  • simple — 이 모드에서 클라이언트 프로세스의 정체성은 호스트 운영체제가 결정합니다. Unix 계열 시스템에서 사용자 이름은 whoami에 해당합니다.
  • kerberos — Kerberos 동작에서 클라이언트 프로세스의 정체성은 Kerberos 자격 증명으로 결정됩니다. 예를 들어 Kerberos 환경에서 사용자는 kinit 유틸리티로 Kerberos 티켓 허용 티켓(TGT)을 얻고, klist로 현재 프린시펄을 확인할 수 있어요. Kerberos 프린시펄을 HDFS 사용자 이름으로 매핑할 때 primary를 제외한 모든 구성 요소는 버려집니다. 예를 들어 todd/[email protected] 프린시펄은 HDFS에서 단순 사용자 이름 todd로 동작합니다.

어떤 동작 모드든 사용자 정체성 메커니즘은 HDFS 자체 외부에 있습니다. HDFS 내에는 사용자 정체성을 만들거나, 그룹을 만들거나, 사용자 자격 증명을 처리하는 기능이 없어요.

그룹 매핑 (Group Mapping)

사용자 이름이 위에서 설명한 대로 결정되면 그룹 목록은 hadoop.security.group.mapping 속성이 구성하는 그룹 매핑 서비스로 결정됩니다. 자세한 내용은 Hadoop Groups Mapping을 참고하세요.

권한 검사 (Permission Checks)

각 HDFS 연산은 사용자가 특정 권한(READ, WRITE, EXECUTE의 일부 조합)을 가질 것을 요구하며, 이 권한은 파일 소유권, 그룹 멤버십 또는 other 권한을 통해 부여됩니다. 연산은 경로의 마지막 구성 요소뿐 아니라 여러 구성 요소에서 권한 검사를 수행할 수 있어요. 또한 일부 연산은 경로 소유자 검사에 의존합니다.

모든 연산은 순회 접근(traversal access)을 요구합니다. 순회 접근은 경로의 마지막 구성 요소를 제외한 모든 기존 구성 요소에 EXECUTE 권한을 요구해요. 예를 들어 /foo/bar/baz에 접근하는 연산은 /, /foo, /foo/bar에 EXECUTE 권한이 있어야 합니다.

다음 표는 HDFS가 경로의 각 구성 요소에 수행하는 권한 검사를 설명합니다.

  • Ownership(소유권): 호출자가 경로의 소유자인지 검사할지 여부. 일반적으로 소유권이나 권한 메타데이터를 바꾸는 연산은 호출자가 소유자임을 요구합니다.
  • Parent(부모): 요청 경로의 부모 디렉터리. 예: /foo/bar/baz의 부모는 /foo/bar.
  • Ancestor(조상): 요청 경로의 마지막 기존 구성 요소. 예: /foo/bar/baz에서 /foo/bar가 존재하면 조상 경로는 /foo/bar. /foo는 존재하지만 /foo/bar가 없으면 조상 경로는 /foo.
  • Final(최종): 요청 경로의 마지막 구성 요소. 예: /foo/bar/baz의 마지막 경로 구성 요소는 /foo/bar/baz.
  • Sub-tree(하위 트리): 디렉터리인 경로에 대해 디렉터리 자신과 그 모든 자식 하위 디렉터리를 재귀적으로 포함. 예: buz와 boo라는 2개 하위 디렉터리를 가진 /foo/bar/baz의 하위 트리는 /foo/bar/baz, /foo/bar/baz/buz, /foo/bar/baz/boo.
연산 Ownership Parent Ancestor Final Sub-tree
append NO N/A N/A WRITE N/A
concat NO [2] WRITE (소스) N/A READ (소스), WRITE (대상) N/A
create NO N/A WRITE WRITE [1] N/A
createSnapshot YES N/A N/A N/A N/A
delete NO [2] WRITE N/A N/A READ, WRITE, EXECUTE
deleteSnapshot YES N/A N/A N/A N/A
getAclStatus NO N/A N/A N/A N/A
getBlockLocations NO N/A N/A READ N/A
getContentSummary NO N/A N/A N/A READ, EXECUTE
getFileInfo NO N/A N/A N/A N/A
getFileLinkInfo NO N/A N/A N/A N/A
getLinkTarget NO N/A N/A N/A N/A
getListing NO N/A N/A READ, EXECUTE N/A
getSnapshotDiffReport NO N/A N/A READ READ
getStoragePolicy NO N/A N/A READ N/A
getXAttrs NO N/A N/A READ N/A
listXAttrs NO EXECUTE N/A N/A N/A
mkdirs NO N/A WRITE N/A N/A
modifyAclEntries YES N/A N/A N/A N/A
removeAcl YES N/A N/A N/A N/A
removeAclEntries YES N/A N/A N/A N/A
removeDefaultAcl YES N/A N/A N/A N/A
removeXAttr NO [2] N/A N/A WRITE N/A
rename NO [2] WRITE (소스) WRITE (대상) N/A N/A
renameSnapshot YES N/A N/A N/A N/A
setAcl YES N/A N/A N/A N/A
setOwner YES [3] N/A N/A N/A N/A
setPermission YES N/A N/A N/A N/A
setReplication NO N/A N/A WRITE N/A
setStoragePolicy NO N/A N/A WRITE N/A
setTimes NO N/A N/A WRITE N/A
setXAttr NO [2] N/A N/A WRITE N/A
truncate NO N/A N/A WRITE N/A
  • [1] create 중 최종 경로 구성 요소에 대한 WRITE 접근은 호출이 overwrite 옵션을 사용하고 그 경로에 이미 파일이 있을 때만 필요합니다.
  • [2] 부모 디렉터리에 WRITE 권한을 검사하는 모든 연산은 스티키 비트가 설정된 경우 소유권도 검사합니다.
  • [3] 파일을 소유한 사용자를 바꾸는 setOwner 호출은 HDFS 슈퍼유저 접근이 필요합니다. 그룹을 바꾸는 데는 HDFS 슈퍼유저 접근이 필요 없지만, 호출자가 파일의 소유자이고 지정된 그룹의 멤버여야 해요.

구현 이해하기 (Understanding the Implementation)

각 파일·디렉터리 연산은 전체 경로 이름을 NameNode에 전달하고, 권한 검사는 각 연산의 경로를 따라 적용됩니다. 클라이언트 프레임워크는 사용자 정체성을 NameNode와의 연결에 암시적으로 연관시켜 기존 클라이언트 API 변경 필요를 줄여줍니다. 파일에 대한 한 연산이 성공했더라도, 파일이나 경로의 어떤 디렉터리가 더 이상 존재하지 않으면 연산을 반복할 때 실패할 수 있다는 것은 항상 사실입니다. 예를 들어 클라이언트가 파일 읽기를 시작하면 파일의 첫 블록 위치를 알아내기 위해 NameNode에 첫 요청을 합니다. 추가 블록을 찾는 두 번째 요청은 실패할 수 있어요. 반면 파일을 삭제해도 이미 파일 블록을 아는 클라이언트의 접근은 취소되지 않습니다. 권한이 추가되면서 클라이언트의 파일 접근이 요청 사이에 철회될 수 있습니다. 마찬가지로 권한을 바꿔도 이미 파일 블록을 아는 클라이언트의 접근은 취소되지 않아요.

파일 시스템 API 변경 (Changes to the File System API)

경로 파라미터를 사용하는 모든 메서드는 권한 검사가 실패하면 AccessControlException을 던집니다.

새 메서드:

  • public FSDataOutputStream create(Path f, FsPermission permission, boolean overwrite, int bufferSize, short replication, long blockSize, Progressable progress) throws IOException;
  • public boolean mkdirs(Path f, FsPermission permission) throws IOException;
  • public void setPermission(Path p, FsPermission permission) throws IOException;
  • public void setOwner(Path p, String username, String groupname) throws IOException;
  • public FileStatus getFileStatus(Path f) throws IOException; — 추가로 경로와 연관된 사용자, 그룹, mode를 반환합니다.

새 파일·디렉터리의 mode는 구성 파라미터로 설정된 umask에 의해 제한됩니다. 기존 create(path, …)(permission 파라미터 없음)를 사용하면 새 파일의 mode는 0666 & ^umask입니다. 새 create(path, permission, …)(permission 파라미터 P 포함)를 사용하면 P & ^umask & 0666입니다. 기존 mkdirs(path)(permission 파라미터 없음)로 새 디렉터리를 만들면 0777 & ^umask입니다. 새 mkdirs(path, permission)(permission 파라미터 P 포함)를 사용하면 P & ^umask & 0777입니다.

애플리케이션 셸 변경 (Changes to the Application Shell)

새 연산:

  • chmod [-R] mode file ... — 파일의 owner나 슈퍼유저만 파일의 mode를 바꿀 수 있습니다.
  • chgrp [-R] group file ... — chgrp를 호출하는 사용자는 지정된 그룹에 속하고 파일의 소유자여야 하거나, 슈퍼유저여야 합니다.
  • chown [-R] [owner][:[group]] file ... — 파일의 owner는 슈퍼유저만 바꿀 수 있습니다.
  • ls file ...
  • lsr file ... — 출력이 owner, group, mode를 표시하도록 재포맷됩니다.

슈퍼유저 (The Super-User)

슈퍼유저는 NameNode 프로세스 자신과 같은 정체성을 가진 사용자입니다. 대략적으로, NameNode를 시작했다면 당신이 슈퍼유저입니다. 슈퍼유저는 무엇이든 할 수 있으며, 슈퍼유저에게는 권한 검사가 절대 실패하지 않아요. 누가 슈퍼유저였는지에 대한 영속적 개념은 없습니다. NameNode가 시작될 때 프로세스 정체성이 지금 누가 슈퍼유저인지 결정합니다. HDFS 슈퍼유저는 NameNode 호스트의 슈퍼유저일 필요가 없고, 모든 클러스터가 같은 슈퍼유저를 가질 필요도 없어요. 또한 개인 워크스테이션에서 HDFS를 실행하는 실험자는 어떤 구성도 없이 그 설치의 슈퍼유저가 됩니다.

추가로 관리자는 구성 파라미터로 구분된 특별한 그룹을 지정할 수 있습니다. 설정하면 이 그룹의 멤버도 슈퍼유저가 됩니다.

웹 서버 (The Web Server)

기본적으로 웹 서버의 정체성은 구성 파라미터입니다. 즉 NameNode는 실제 사용자의 정체성에 대한 개념이 없고, 웹 서버는 관리자가 선택한 사용자의 정체성(사용자와 그룹)을 가진 것처럼 동작합니다. 선택한 정체성이 슈퍼유저와 일치하지 않으면 네임스페이스의 일부가 웹 서버에 접근 불가할 수 있어요.

ACL (Access Control Lists)

전통적인 POSIX 권한 모델 외에도 HDFS는 POSIX ACL(접근 제어 목록)을 지원합니다. ACL은 사용자·그룹의 자연스러운 조직 계층과 다른 권한 요구사항을 구현하는 데 유용합니다. ACL은 파일의 소유자와 그룹뿐 아니라 특정 명명된 사용자나 명명된 그룹에 대해 다른 권한을 설정하는 방법을 제공합니다.

기본적으로 ACL 지원은 활성화되어 있고, NameNode는 ACL 생성을 허용합니다. ACL 지원을 비활성화하려면 NameNode 구성에서 dfs.namenode.acls.enabled를 false로 설정하세요.

ACL은 여러 ACL 엔트리로 구성됩니다. 각 ACL 엔트리는 특정 사용자나 그룹을 명명하고 그 사용자·그룹에 대해 읽기·쓰기·실행 권한을 부여하거나 거부합니다. 예:

user::rw-
user:bruce:rwx                  #effective:r--
group::r-x                      #effective:r--
group:sales:rwx                 #effective:r--
mask::r--
other::r--

ACL 엔트리는 유형, 선택적 이름, 권한 문자열로 구성됩니다. 표시 목적상 각 필드 사이의 구분자로 ':'가 사용됩니다. 이 예시 ACL에서 파일 소유자는 읽기-쓰기 접근, 파일 그룹은 읽기-실행, others는 읽기 접근을 가집니다. 지금까지는 파일의 권한 비트를 654로 설정한 것과 동일합니다.

추가로 명명된 사용자 bruce와 명명된 그룹 sales에 대한 2개의 확장 ACL 엔트리가 있으며, 둘 다 전체 접근이 부여됩니다. mask는 모든 명명된 사용자 엔트리와 명명된 그룹 엔트리, 그리고 명명되지 않은 그룹 엔트리에 부여된 권한을 필터링하는 특별한 ACL 엔트리입니다. 예시에서 mask는 읽기 권한만 가지며, 여러 ACL 엔트리의 유효 권한이 그에 따라 필터링된 것을 볼 수 있어요.

모든 ACL은 mask를 가져야 합니다. ACL을 설정할 때 사용자가 mask를 제공하지 않으면, mask에 필터링될 모든 엔트리의 권한 합집합을 계산해 mask를 자동으로 삽입합니다.

ACL이 있는 파일에 chmod를 실행하면 실제로는 mask의 권한이 바뀝니다. mask가 필터 역할을 하므로, 이는 그룹 엔트리만 바꾸고 다른 확장 ACL 엔트리를 놓칠 가능성 없이 모든 확장 ACL 엔트리의 권한을 효과적으로 제약합니다.

모델은 또한 권한 검사 중 적용할 규칙을 정의하는 "access ACL"과, 새 자식 파일·하위 디렉터리가 생성 시 자동으로 받는 ACL 엔트리를 정의하는 "default ACL"을 구분합니다. 예:

user::rwx
group::r-x
other::r-x
default:user::rwx
default:user:bruce:rwx          #effective:r-x
default:group::r-x
default:group:sales:rwx         #effective:r-x
default:mask::r-x
default:other::r-x

default ACL은 디렉터리만 가질 수 있습니다. 새 파일이나 하위 디렉터리가 생성되면 부모의 default ACL을 자신의 access ACL로 자동 복사합니다. 새 하위 디렉터리는 그것을 자신의 default ACL로도 복사합니다. 이렇게 default ACL은 새 하위 디렉터리가 생성됨에 따라 파일 시스템 트리의 임의 깊은 수준까지 복사됩니다.

새 자식의 access ACL의 정확한 권한 값은 mode 파라미터에 의해 필터링됩니다. 기본 umask 022를 고려하면 보통 새 디렉터리는 755, 새 파일은 644입니다. mode 파라미터는 명명되지 않은 사용자(파일 소유자), mask, other에 대해 복사된 권한 값을 필터링합니다. 이 특정 예시 ACL을 사용하고 mode가 755인 새 하위 디렉터리를 만들면, mode 필터링은 최종 결과에 영향이 없어요. 하지만 mode가 644인 파일 생성의 경우, mode 필터링으로 새 파일의 ACL이 명명되지 않은 사용자(파일 소유자)에게 읽기-쓰기, mask에 읽기, others에 읽기를 받습니다. 이 mask는 또한 명명된 사용자 bruce와 명명된 그룹 sales의 유효 권한이 읽기뿐임을 의미합니다.

복사는 새 파일·하위 디렉터리 생성 시점에 일어납니다. 이후 부모의 default ACL을 변경해도 기존 자식은 바뀌지 않아요.

default ACL은 명명되지 않은 사용자(파일 소유자), 명명되지 않은 그룹(파일 그룹), other 엔트리를 포함한 모든 최소 필수 ACL 엔트리를 가져야 합니다. default ACL을 설정할 때 사용자가 이 중 하나를 제공하지 않으면, access ACL에서(access ACL이 없으면 권한 비트에서) 해당 권한을 복사해 자동으로 엔트리를 삽입합니다. default ACL도 mask를 가져야 해요. 위에서 설명한 대로 mask가 지정되지 않으면 mask에 필터링될 모든 엔트리의 권한 합집합을 계산해 자동 삽입됩니다.

주어진 파일·디렉터리에 대해 무제한의 ACL 엔트리를 가질 수는 없습니다. 최대 수는 access 32개와 default 32개로 총 64개입니다.

ACL이 있는 파일을 고려할 때 권한 검사 알고리즘은 다음과 같이 바뀝니다.

  • 사용자 이름이 파일의 소유자와 일치하면 소유자 권한을 테스트합니다.
  • 사용자 이름이 명명된 사용자 엔트리 중 하나의 이름과 일치하면, 이 권한을 mask 권한으로 필터링해 테스트합니다.
  • 파일의 그룹이 그룹 목록의 어떤 멤버와 일치하고, mask로 필터링한 이 권한이 접근을 부여하면 이 권한을 사용합니다.
  • 그룹 목록의 멤버와 일치하는 명명된 그룹 엔트리가 있고, mask로 필터링한 이 권한이 접근을 부여하면 이 권한을 사용합니다.
  • 파일 그룹이나 어떤 명명된 그룹 엔트리가 그룹 목록의 멤버와 일치하지만 그 어떤 권한으로도 접근이 부여되지 않으면 접근이 거부됩니다.
  • 그렇지 않으면 파일의 other 권한을 테스트합니다.

모범 사례는 대부분의 권한 요구사항을 전통적인 권한 비트로 구현하고, 몇 가지 예외 규칙으로 권한 비트를 보강하기 위해 더 적은 수의 ACL을 정의하는 것입니다. ACL이 있는 파일은 권한 비트만 있는 파일보다 NameNode에서 메모리 추가 비용이 발생합니다.

ACL 파일 시스템 API (ACLs File System API)

새 메서드:

  • public void modifyAclEntries(Path path, List<AclEntry> aclSpec) throws IOException;
  • public void removeAclEntries(Path path, List<AclEntry> aclSpec) throws IOException;
  • public void public void removeDefaultAcl(Path path) throws IOException;
  • public void removeAcl(Path path) throws IOException;
  • public void setAcl(Path path, List<AclEntry> aclSpec) throws IOException;
  • public AclStatus getAclStatus(Path path) throws IOException;

ACL 셸 명령 (ACLs Shell Commands)

  • hdfs dfs -getfacl [-R] <path> — 파일·디렉터리의 접근 제어 목록(ACL)을 표시합니다. 디렉터리에 default ACL이 있으면 getfacl도 default ACL을 표시합니다.
  • hdfs dfs -setfacl [-R] [-b |-k -m |-x <acl_spec> <path>] |[--set <acl_spec> <path>] — 파일·디렉터리의 접근 제어 목록(ACL)을 설정합니다.
  • hdfs dfs -ls <args> — ls 출력은 ACL이 있는 파일·디렉터리의 권한 문자열에 '+' 문자를 붙입니다.

이 명령들의 전체 설명은 File System Shell 문서를 참고하세요.

구성 파라미터 (Configuration Parameters)

  • dfs.permissions.enabled = true — yes이면 여기 설명한 권한 시스템을 사용합니다. no이면 권한 검사가 꺼지지만 다른 모든 동작은 동일합니다. 한 값에서 다른 값으로 전환해도 파일·디렉터리의 mode, owner, group은 바뀌지 않아요. 권한이 켜져 있든 꺼져 있든 chmod, chgrp, chown, setfacl은 항상 권한을 검사합니다. 이 함수들은 권한 컨텍스트에서만 유용하므로 하위 호환성 문제가 없습니다. 또한 관리자가 일반 권한 검사를 켜기 전에 소유자와 권한을 안정적으로 설정할 수 있게 해 줍니다.
  • dfs.web.ugi = webuser,webgroup — 웹 서버가 사용할 사용자 이름. 슈퍼유저의 이름으로 설정하면 어떤 웹 클라이언트든 모든 것을 볼 수 있습니다. 다른 미사용 정체성으로 바꾸면 웹 클라이언트는 "other" 권한으로 보이는 것만 볼 수 있어요. 쉼표로 구분된 목록에 추가 그룹을 넣을 수 있습니다.
  • dfs.permissions.superusergroup = supergroup — 슈퍼유저 그룹의 이름.
  • fs.permissions.umask-mode = 0022 — 파일·디렉터리를 만들 때 사용하는 umask. 구성 파일에서는 10진수 18을 쓸 수 있습니다.
  • dfs.cluster.administrators = ACL-for-admins — ACL로 지정된 클러스터 관리자. HDFS에서 기본 서블릿 등에 접근할 수 있는 사람을 제어합니다.
  • dfs.namenode.acls.enabled = true — HDFS ACL(접근 제어 목록) 지원을 활성화하려면 true로 설정. 기본적으로 ACL은 활성화됨. ACL이 비활성화되면 NameNode는 ACL 설정 시도를 모두 거부합니다.
  • dfs.namenode.posix.acl.inheritance.enabled — POSIX 스타일 ACL 상속을 활성화하려면 true로 설정. 기본적으로 활성화됨. 활성화되고 create 요청이 호환 클라이언트에서 오면 NameNode는 부모 디렉터리의 default ACL을 create mode에 적용하고 클라이언트 umask를 무시합니다. default ACL이 없으면 클라이언트 umask를 적용합니다.

더 알아보기 (Learn more)