Role-Based Access Control
Role-Based Access Control (RBAC)
lakeFS Team과 lakeFS Enterprise에서 사용할 수 있어요. 무료 체험을 시작하거나 문의하세요.
본문
RBAC 모델
리소스 접근은 AWS IAM과 매우 비슷한 방식으로 관리돼요.
시스템에는 다섯 가지 기본 구성 요소가 있어요:
-
사용자(Users) - 시스템에 접근해 사용하는 엔티티를 나타내요. 사용자는 인증을 위해 하나 이상의 자격 증명(Access Credentials) 을 부여받아요.
-
액션(Actions) - 시스템 내의 논리적 액션을 나타내요. 파일 읽기, 저장소 생성 등이 있어요.
-
리소스(Resources) - 시스템 내 특정 리소스를 나타내는 고유 식별자예요. 저장소, 객체, 사용자 등이 있어요.
-
정책(Policies) - 액션 집합, 리소스, 그리고 효과를 나타내요: 주어진 리소스에 대해 이 액션들이
allowed인지denied인지여요. -
그룹(Groups) - 이름 붙은 사용자 컬렉션이에요. 사용자는 여러 그룹에 속할 수 있어요.
접근 제어는 정책을 사용자에게 직접 붙이거나, 그 사용자가 속한 그룹에 붙여서 이루어져요.
인가 프로세스
시스템의 모든 액션 - API 요청, UI 상호작용, S3 게이트웨이 호출, CLI 명령이든 - 은 하나 이상의 리소스에 대해 액션 집합이 허용되어야 해요.
사용자가 그 액션을 수행하려고 요청하면 다음 프로세스가 진행돼요:
-
인증(Authentication) - 요청에 담긴 자격 증명을 평가해 사용자의 아이덴티티를 추출해요.
-
액션 권한 해석 - lakeFS가 이 요청에 필요한 허용된 액션과 리소스의 집합을 계산해요.
-
유효 정책 해석 - 사용자의 정책(직접 연결됐거나 그룹 멤버십을 통해)이 계산돼요.
-
정책/권한 평가 - lakeFS가 주어진 사용자 정책과 요청 액션을 비교해 요청이 계속될 수 있는지 판단해요.
정책 우선순위
사용자나 그룹에 연결된 각 정책은 Effect를 가져요 - allow 또는 deny예요. 요청 평가 중에는 deny가 다른 모든 allow 정책보다 우선해요.
이 덕분에 정책을 조합할 수 있어요. 예를 들어 사용자에게 매우 관대한 정책을 연결한 뒤, deny 규칙으로 그 사용자가 할 수 있는 일을 선택적으로 제한할 수 있어요.
정책 조건
lakeFS 정책은 정책 명령문이 언제 유효한지에 대한 추가 제어를 제공하는 선택적 조건을 지원해요.
조건은 요청 컨텍스트를 기반으로 참이나 거짓으로 평가되는 조건 연산자를 사용해 지정해요. 조건이 거짓으로 평가되면 그 정책 명령문은 적용되지 않아요.
조건 구조
조건은 선택적인 condition 필드로 정책 명령문에 추가돼요:
{
"statement": [
{
"action": ["fs:ReadObject"],
"effect": "allow",
"resource": "arn:lakefs:fs:::repository/myrepo/object/*",
"condition": {
"IpAddress": {
"SourceIp": ["203.0.113.0/24", "198.51.100.25/32"]
}
}
}
]
}
지원되는 조건 연산자
IpAddress / NotIpAddress
IpAddress 조건 연산자는 클라이언트의 소스 IP 주소를 하나 이상의 IP 주소나 CIDR 블록과 매칭해요. NotIpAddress 조건 연산자는 클라이언트의 소스 IP 주소가 지정된 IP 주소나 CIDR 블록 어느 것과도 일치하지 않을 때 매칭돼요(IpAddress의 부정).
지원되는 필드:
SourceIp- 요청을 만드는 클라이언트의 IP 주소
값 형식:
- 단일 IP 주소:
"203.0.113.5" - CIDR 표기법:
"203.0.113.0/24" - IP와/또는 CIDR의 배열:
["203.0.113.0/24", "198.51.100.25/32"]
예제 - 특정 IP 범위에서만 접근 허용:
{
"statement": [
{
"action": ["fs:*"],
"effect": "allow",
"resource": "*",
"condition": {
"IpAddress": {
"SourceIp": ["10.0.0.0/8", "172.16.0.0/12"]
}
}
}
]
}
예제 - 특정 IP에서의 접근 거부:
{
"statement": [
{
"action": ["fs:*"],
"effect": "deny",
"resource": "*",
"condition": {
"IpAddress": {
"SourceIp": ["192.0.2.0/24"]
}
}
}
]
}
예제 - 허용 범위에 없는 IP에서의 접근 거부:
{
"statement": [
{
"action": ["fs:*"],
"effect": "deny",
"resource": "*",
"condition": {
"NotIpAddress": {
"SourceIp": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
}
}
}
]
}
IP 주소 추출:
SourceIp가 평가하는 주소는 lakeFS가 요청에 대해 해석한 클라이언트 IP로, Client IP resolution 아래에서 설정돼요.
Warning
AWS PrivateLink에서는 요청이 lakeFS에 도착하기 전에 소비자의 주소가 대체될 수 있어요. 이 경우 어떤 lakeFS 설정으로도 그 주소를 복원할 수 없고, SourceIp 조건은 의도한 제한을 표현할 수 없어요. PrivateLink 경로에서 SourceIp에 의존하기 전에 감사 로그에 source_ip가 무엇을 기록하는지 확인하세요.
StringEquals / StringNotEquals
StringEquals 조건 연산자는 컨텍스트 값을 하나 이상의 정확한 문자열과 매칭해요. StringNotEquals 조건 연산자는 컨텍스트 값이 지정된 문자열 어느 것과도 같지 않을 때 매칭돼요(StringEquals의 부정).
StringLike / StringNotLike
StringLike 조건 연산자는 컨텍스트 값을 하나 이상의 와일드카드 패턴과 매칭해요. StringNotLike 조건 연산자는 컨텍스트 값이 지정된 패턴 어느 것과도 일치하지 않을 때 매칭돼요(StringLike의 부정).
와일드카드 문자: *는 0개 이상의 문자와 일치하고, ?는 정확히 하나의 문자와 일치해요.
저장소를 위한 속성 기반 접근 제어
lakeFS Team과 lakeFS Enterprise에서 사용할 수 있어요. 무료 체험을 시작하거나 문의하세요.
역할 기반 접근을 넘어, lakeFS는 속성 기반 접근 제어(ABAC) 를 지원해요: 접근 중인 저장소의 속성을 기준으로 접근을 게이트하는 정책 조건이에요. 덕분에 저장소마다 별도의 정책을 관리하지 않고도 데이터 거버넌스 정책을 자동으로 강제할 수 있어요 - 예를 들어 PII나 프로덕션 저장소에 대한 접근을 제한하는 식이에요.
ABAC 조건은 저장소의 사용자 정의 메타데이터로 필터링할 수 있어요. 저장소에 키-값 태그를 붙이고(예: env=staging, team=data-platform, classification=pii) 정책 조건에서 그 태그를 참조하면 돼요.
저장소 메타데이터 관리
lakeFS REST API로 저장소에 메타데이터를 설정(머지) 해요. 요청에 포함되지 않은 키는 유지돼요 - 교체가 아니라 머지예요:
curl -X POST https://<lakefs-host>/api/v1/repositories/<repo>/metadata \
-H "Content-Type: application/json" \
-u ${LAKECTL_CREDENTIALS_ACCESS_KEY_ID}:${LAKECTL_CREDENTIALS_SECRET_ACCESS_KEY} \
-d '{"metadata": {"env": "staging", "team": "data-platform", "classification": "pii"}}'
메타데이터 읽기로 무엇이 설정됐는지 확인해요:
curl -X POST https://$LAKECTL_SERVER_ENDPOINT/api/v1/repositories/<repo>/metadata \
-u ${LAKECTL_CREDENTIALS_ACCESS_KEY_ID}:${LAKECTL_CREDENTIALS_SECRET_ACCESS_KEY} \
특정 메타데이터 키 삭제:
curl -X DELETE https://<lakefs-host>/api/v1/repositories/<repo>/metadata \
-H "Content-Type: application/json" \
-u ${LAKECTL_CREDENTIALS_ACCESS_KEY_ID}:${LAKECTL_CREDENTIALS_SECRET_ACCESS_KEY} \
-d '{"keys": ["classification"]}'
조건 키 형식
lakefs:RepositoryMetadata/<metadata-key>
<metadata-key>는 저장소 메타데이터의 키에 직접 매핑돼요. 매칭은 대소문자를 구분해요 - lakefs:RepositoryMetadata/env는 메타데이터 키 env만 매칭해요.
예제
env=staging 태그가 붙은 저장소에만 읽기 접근 허용
{
"statement": [
{
"action": ["fs:ReadObject", "fs:ListObjects", "fs:ReadRepository"],
"effect": "allow",
"resource": "arn:lakefs:fs:::repository/*",
"condition": {
"StringLike": {
"lakefs:RepositoryMetadata/env": ["staging"]
}
}
}
]
}
PII 저장소 접근 거부, 광범위한 allow와 결합
가장 흔한 ABAC 패턴은 사용자에게 광범위한 역할(예: FSReadAll)을 부여한 뒤, 민감한 저장소에 대한 접근을 거부하는 추가 정책을 연결하는 거예요. deny가 allow보다 우선하기 때문에, 사용자가 어떤 다른 정책을 가지고 있든 제한이 유지된다는 게 보장돼요.
{
"statement": [
{
"action": ["fs:*"],
"effect": "deny",
"resource": "arn:lakefs:fs:::repository/*",
"condition": {
"StringLike": {
"lakefs:RepositoryMetadata/classification": ["pii"]
}
}
}
]
}
이 정책을 FSReadAll(또는 다른 광범위한 정책)과 함께 연결하면: 사용자는 classification=pii 태그가 붙은 저장소를 제외한 모든 저장소에 접근할 수 있어요.
여러 메타데이터 키 결합
조건 블록에 여러 키가 있으면 명령문이 적용되려면 모든 키가 일치해야 해요. 단일 키 안의 여러 값은 OR로 평가돼요(어떤 하나가 일치하면 충분):
{
"statement": [
{
"action": ["fs:ReadObject", "fs:ListObjects"],
"effect": "allow",
"resource": "arn:lakefs:fs:::repository/*",
"condition": {
"StringLike": {
"lakefs:RepositoryMetadata/team": ["ml"],
"lakefs:RepositoryMetadata/env": ["staging", "dev"],
"lakefs:RepositoryMetadata/classification": ["public"]
}
}
}
]
}
이것은 team=ml 이고 (env=staging 또는 env=dev) 이고 classification=public인 저장소에만 접근을 부여해요.
동작 참고 사항
-
저장소에 메타데이터가 없거나, 요청한 키가 없으면 조건은
false로 평가되고 그 명령문은 적용되지 않아요. -
조건 키는 대소문자를 구분해요:
lakefs:RepositoryMetadata/ENV는 메타데이터 키env와 매칭되지 않아요.
객체를 위한 속성 기반 접근 제어
lakeFS는 객체에 대한 속성 기반 접근 제어(ABAC) 도 지원해요: 저장소 전체가 아니라 객체 자체에 붙은 사용자 정의 메타데이터를 기준으로 접근을 게이트하는 정책 조건이에요. 덕분에 저장소 전체 태그보다 더 정밀하게 접근을 한정할 수 있어요 - 예를 들어 classification=public 태그가 붙은 객체만 매칭하고 같은 저장소의 나머지는 그대로 두는 식이에요.
객체 수준 ABAC는 읽기 연산에만 적용돼요 - GetObject, HeadObject, StatObject, GetUnderlyingProperties의 fs:ReadObject, 그리고 객체 복사 연산의 읽기 쪽이에요. 쓰기나 삭제에는 적용되지 않고, fs:ListObjects는 나열 결과를 객체 메타데이터로 필터링하지 않아요(presigned 나열이 객체별 조건과 어떻게 상호작용하는지는 아래 동작 참고 사항을 보세요).
객체 메타데이터 설정하기
객체 메타데이터는 업로드 시점에 x-lakefs-meta-* 요청 헤더로 설정해요; 각 헤더는 객체의 하나의 메타데이터 키가 돼요(키는 소문자화돼요):
curl -X POST "https://<lakefs-host>/api/v1/repositories/<repo>/branches/<branch>/objects?path=<path>" \
-H "x-lakefs-meta-classification: public" \
-H "x-lakefs-meta-owner: data-platform" \
-u ${LAKECTL_CREDENTIALS_ACCESS_KEY_ID}:${LAKECTL_CREDENTIALS_SECRET_ACCESS_KEY} \
--data-binary @localfile
이렇게 설정한 메타데이터는 StatObject와 GetObject 응답의 metadata 필드에, 그리고 요청이 user_metadata=false로 설정하지 않는 한 ListObjects 결과에 반환돼요. S3 게이트웨이는 x-lakefs-meta-* 대신 표준 x-amz-meta-* 헤더 컨벤션을 사용하며, GetObject/HeadObject 응답에 같은 방식으로 반환돼요. S3 게이트웨이 메타데이터 키는 X-Amz-Meta- 프리픽스와 소문자화된 키 부분으로 저장된다는 점에 주의하세요(예. X-Amz-Meta-classification). 그래서 매칭 조건 키는 lakefs:ObjectMetadata/X-Amz-Meta-classification이지 위 REST API 컨벤션에서 얻는 베어 키가 아니에요.
조건 키 형식
lakefs:ObjectMetadata/<metadata-key>
<metadata-key>는 객체 메타데이터의 키에 직접 매핑돼요. 매칭은 대소문자를 구분해요 - lakefs:ObjectMetadata/classification은 메타데이터 키 classification만 매칭해요.
객체 메타데이터 조건은 다른 어떤 정책 조건과도 같은 조건 연산자를 받아들여요 - StringEquals, StringNotEquals, StringLike, StringNotLike.
lakeFS가 내부적으로 관리하는 메타데이터 키(mtime 추적, symlink 대상, POSIX 권한, 얕은 복사 출처)는 여러분의 태그와 같은 메타데이터 맵에 살아 있지만 lakefs:ObjectMetadata/로는 절대 노출되지 않아요 - 여러분이 직접 설정한 메타데이터만 조건에서 사용할 수 있어요.
예제
classification=public 태그가 붙은 객체에만 읽기 접근 허용
{
"statement": [
{
"action": ["fs:ReadObject"],
"effect": "allow",
"resource": "arn:lakefs:fs:::repository/myrepo/object/*",
"condition": {
"StringEquals": {
"lakefs:ObjectMetadata/classification": ["public"]
}
}
}
]
}
contains_pii=true 태그가 붙은 객체의 읽기 접근 거부, 광범위한 allow와 결합
{
"statement": [
{
"action": ["fs:ReadObject"],
"effect": "deny",
"resource": "arn:lakefs:fs:::repository/myrepo/object/*",
"condition": {
"StringEquals": {
"lakefs:ObjectMetadata/contains_pii": ["true"]
}
}
}
]
}
이 정책을 더 광범위한 읽기 정책과 함께 연결하면: 사용자는 contains_pii=true 태그가 붙은 객체를 제외한 myrepo의 모든 객체를 읽을 수 있어요. 이 형태는 호출자가 태그가 붙은 객체와 존재하지 않는 객체를 구별할 수 있게 한다는 점에 주의하세요. 아래 동작 참고 사항을 보세요.
저장소와 객체 메타데이터 조건 결합
저장소 수준과 객체 수준 ABAC 조건은 같은 명령문에서 함께 사용할 수 있어요 - 지정된 모든 키가 일치해야 해요:
{
"statement": [
{
"action": ["fs:ReadObject"],
"effect": "allow",
"resource": "arn:lakefs:fs:::repository/*/object/*",
"condition": {
"StringEquals": {
"lakefs:RepositoryMetadata/env": ["staging"],
"lakefs:ObjectMetadata/classification": ["public"]
}
}
}
]
}
동작 참고 사항
-
객체 수준 ABAC는 읽기에 한정돼요.
fs:ListObjects는 객체 메타데이터 조건을 평가하지 않아요 - 어떤 객체별 조건과 무관하게, 나열은 호출자가 저장소 수준 접근 권한을 가진 모든 객체를 반환해요.presign=true로 나열하는 것이 예외예요: 각 결과의 presigned URL은fs:ReadObject아래에서 발급되기 때문에, 조건이 거부한 객체도 나열에는 나타나지만 presigned URL은 받지 못해요. -
객체에 메타데이터가 없거나 요청한 키가 없으면 조건은
false로 평가되고 그 명령문은 적용되지 않아요. 빈 문자열 값으로 존재하는 키는 다르게 취급돼요: 그것은 평범한 매칭 대상이라서,"StringEquals": {"lakefs:ObjectMetadata/tag": [""]}는tag가 빈 값으로 설정된 객체와 매칭돼요. -
거부가 객체의 존재를 드러내는지는 정책 형태에 달려 있어요. 조건이 붙은 allow 아래에서는, 존재하지 않는 객체가 조건을 만족할 메타데이터가 없어서 호출자가 읽을 수 없는 객체와 똑같이 거부돼요. 광범위한 allow에 조건이 붙은 deny가 짝을 이루면, deny는 존재하지 않는 객체와 매칭될 수 없어서 존재하지 않는 경로는 not-found(
404, S3 게이트웨이에서는NoSuchKey)를 반환하고 거부된 경로는 access-denied를 반환해요. 그러면 객체가 존재하고 거부된 값을 가진다는 사실이 함께 드러나요. 존재가 드러나면 안 되는 곳에서는 조건이 붙은 allow를 사용하세요. -
조건 키는 대소문자를 구분해요:
lakefs:ObjectMetadata/CLASSIFICATION은 메타데이터 키classification과 매칭되지 않아요. -
객체 수준 ABAC는 읽기 액션을 위해 설계됐어요. 쓰기 액션(예:
fs:WriteObject)을lakefs:ObjectMetadata/*로 조건화하는 것은 지원되는 조합이 아니에요. S3 게이트웨이의 복사 경로(x-amz-copy-source를 실은PUT)에서는 목적지-쓰기와 소스-읽기 권한이 한 세트의 속성으로 함께 검사되기 때문에, 거기서 사용된 쓰기 쪽 객체 메타데이터 조건은 목적지의 메타데이터가 아니라 복사 소스의 메타데이터로 평가돼요. -
S3 게이트웨이 복사 경로에서 저장소 속성은 목적지 저장소에서 나와요. 복사 소스가 다른 저장소에 있다면,
lakefs:RepositoryMetadata/*조건은 소스가 아니라 목적지 저장소의 메타데이터로 검사돼요. 저장소 밖으로의 복사를 막으려면 저장소 메타데이터 조건 대신 리소스 ARN을 사용하세요.
리소스 명명 - ARN
lakeFS는 ARN identifier를 사용해요 - AWS가 사용하는 것과 구조가 매우 비슷해요. ARN의 리소스 세그먼트는 와일드카드를 지원해요: 0개 이상의 문자에는 *를, 정확히 하나의 문자에는 ?를 사용해요.
다음은 lakeFS 내 유효한 ARN의 몇 가지 예제와 의미예요:
| ARN | Meaning |
|---|---|
| arn:lakefs:auth:::user/jane.doe | A specific user |
| arn:lakefs:auth:::user/* | All users |
| arn:lakefs:fs:::repository/myrepo/* | All resources under myrepo |
| arn:lakefs:fs:::repository/myrepo/object/foo/bar/baz | A single object ARN |
| arn:lakefs:fs:::repository/myrepo/object/* | All objects in myrepo |
| arn:lakefs:fs:::repository/* | All repositories |
| arn:lakefs:fs:::* | All resources under the fs ARN prefix |
| arn:lakefs:catalog:::namespace/{repositoryId}/{namespace} | An Iceberg namespace |
| arn:lakefs:catalog:::table/{repositoryId}/{namespace}/{table} | An Iceberg table |
| arn:lakefs:catalog:::view/{repositoryId}/{namespace}/{view} | An Iceberg view |
| arn:lakefs:audit:::log | The lakeFS audit log |
Iceberg 카탈로그 ARN은 브랜치에 독립적이라는 점에 주의하세요. 브랜치는 ARN의 일부가 아니에요. 네임스페이스, 테이블, 뷰에 대한 접근을 부여하는 정책은 저장소의 모든 브랜치에서 그 리소스에 적용돼요.
추가로, 현재 사용자의 ID는 ${user} 플레이스홀더로 런타임에 ARN 안에 보간돼요.
정책을 테넌트에 한정하기
ARN의 다섯 번째 콜론 구분 필드는 account 세그먼트인데, 위 예제들에서는 모두 비어 있어요. fs: ARN에서 이 세그먼트는 테넌트의 이름을 담아서, 정책을 한 테넌트의 데이터에 고정할 수 있어요. 동작은 다음과 같아요:
| ARN | Meaning |
|---|---|
| arn:lakefs:fs:::repository/myrepo | myrepo in every tenant, because the segment is omitted |
| arn:lakefs:fs::*:repository/myrepo | myrepo in every tenant, stated explicitly |
| arn:lakefs:fs::team-a:repository/myrepo | myrepo in the tenant team-a only |
| arn:lakefs:fs::root:repository/myrepo | myrepo in the reserved root tenant only |
세그먼트를 생략하면 root 테넌트가 아니라 모든 테넌트를 의미해요. 테넌트가 도입되기 전에 작성된 정책이 그때와 똑같이 동작하는 이유가 이것 때문이에요. root를 세그먼트에 쓰는 것은 root 테넌트에만 적용되고 다른 어느 테넌트에도 적용되지 않는 부여를 구체적으로 원할 때뿐이에요. 정책을 테넌트에 고정하는 것은 부여를 좁히는 것이지 그 자체로 접근을 주는 건 아니라는 점을 기억하세요. 테넌트의 데이터에 도달하려면 그 테넌트의 멤버십도 필요하거든요.
account 세그먼트가 테넌트 의미를 지니는 것은 fs: ARN에서만이에요. 테넌트 자체는 auth: 리소스로, ARN이 arn:lakefs:auth:::tenant/team-a처럼 리소스 경로를 통해 테넌트를 식별하고, account 세그먼트는 거기서 비어 있어요.
이 덕분에 특정 리소스 하위 집합에만 영향을 주는 세밀한 정책을 만들 수 있어요.
ARN과 액션의 전체 레퍼런스는 아래를 참고하세요.
액션과 권한
액션의 전체 목록과 필요한 권한은 다음 표를 참고하세요:
| Action name | required action | Resource | API endpoint | S3 gateway operation |
|---|---|---|---|---|
| List Repositories | fs:ListRepositories | * | GET /repositories | ListBuckets |
| Get Repository | fs:ReadRepository | arn:lakefs:fs:::repository/{repositoryId} | GET /repositories/{repositoryId} | HeadBucket |
| Run Metadata Search Query | fs:ReadRepository | arn:lakefs:fs:::repository/{repositoryId} (every repository the query references) | POST /mds/query | - |
| Get Metadata Search Query Result | fs:ReadRepository | arn:lakefs:fs:::repository/{repositoryId} (every repository the query references) | GET /mds/query/{query_id}/result | - |
| Get Commit | fs:ReadCommit | arn:lakefs:fs:::repository/{repositoryId} | GET /repositories/{repositoryId}/commits/{commitId} | - |
| Create Commit | fs:CreateCommit | arn:lakefs:fs:::repository/{repositoryId}/branch/{branchId} | POST /repositories/{repositoryId}/branches/{branchId}/commits | - |
| Get Commit log | fs:ReadBranch | arn:lakefs:fs:::repository/{repositoryId}/branch/{branchId} | GET /repositories/{repositoryId}/branches/{branchId}/commits | - |
| Create Repository | fs:CreateRepository | arn:lakefs:fs:::repository/{repositoryId} | POST /repositories | - |
| Namespace Attach to Repository | fs:AttachStorageNamespace | arn:lakefs:fs:::namespace/{storageNamespace} | POST /repositories | - |
| Import From Source | fs:ImportFromStorage | arn:lakefs:fs:::namespace/{storageNamespace} | POST /repositories/{repositoryId}/branches/{branchId}/import | - |
| Cancel Import | fs:ImportCancel | arn:lakefs:fs:::repository/{repositoryId}/branch/{branchId} | DELETE /repositories/{repositoryId}/branches/{branchId}/import | - |
| Delete Repository | fs:DeleteRepository | arn:lakefs:fs:::repository/{repositoryId} | DELETE /repositories/{repositoryId} | - |
| List Branches | fs:ListBranches | arn:lakefs:fs:::repository/{repositoryId} or arn:lakefs:fs:::repository/{repositoryId}/branch/{branchId} (details) | GET /repositories/{repositoryId}/branches | ListObjects/ListObjectsV2 (with delimiter = / and empty` prefix) |
| Get Branch | fs:ReadBranch | arn:lakefs:fs:::repository/{repositoryId}/branch/{branchId} | GET /repositories/{repositoryId}/branches/{branchId} | - |
| Create Branch | fs:CreateBranch | arn:lakefs:fs:::repository/{repositoryId}/branch/{branchId} | POST /repositories/{repositoryId}/branches | - |
| Delete Branch | fs:DeleteBranch | arn:lakefs:fs:::repository/{repositoryId}/branch/{branchId} | DELETE /repositories/{repositoryId}/branches/{branchId} | - |
| Merge branches | fs:CreateCommit | arn:lakefs:fs:::repository/{repositoryId}/branch/{destinationBranchId} | POST /repositories/{repositoryId}/refs/{sourceBranchId}/merge/{destinationBranchId} | - |
| Diff branch uncommitted changes | fs:ListObjects | arn:lakefs:fs:::repository/{repositoryId} | GET /repositories/{repositoryId}/branches/{branchId}/diff | - |
| Diff refs | fs:ListObjects | arn:lakefs:fs:::repository/{repositoryId} | GET /repositories/{repositoryId}/refs/{leftRef}/diff/{rightRef} | - |
| Stat object | fs:ReadObject | arn:lakefs:fs:::repository/{repositoryId}/object/{objectKey} | GET /repositories/{repositoryId}/refs/{ref}/objects/stat | HeadObject |
| Get Object | fs:ReadObject | arn:lakefs:fs:::repository/{repositoryId}/object/{objectKey} | GET /repositories/{repositoryId}/refs/{ref}/objects | GetObject |
| List Objects | fs:ListObjects | arn:lakefs:fs:::repository/{repositoryId} | GET /repositories/{repositoryId}/refs/{ref}/objects/ls | ListObjects, ListObjectsV2 (no delimiter, or "/" + non-empty` prefix) |
| Upload Object | fs:WriteObject | arn:lakefs:fs:::repository/{repositoryId}/object/{objectKey} | POST /repositories/{repositoryId}/branches/{branchId}/objects | PutObject, CreateMultipartUpload, UploadPart`, CompleteMultipartUpload |
| Stage Object | fs:WriteObject, and fs:ImportFromStorage for an address outside the repository's storage namespace | arn:lakefs:fs:::repository/{repositoryId}/object/{objectKey} and arn:lakefs:fs:::namespace/{physicalAddress} | PUT /repositories/{repositoryId}/branches/{branchId}/objects | - |
| Get Physical Address | fs:WriteObject | arn:lakefs:fs:::repository/{repositoryId}/object/{objectKey} | GET /repositories/{repositoryId}/branches/{branchId}/staging/backing | - |
| Link Physical Address | fs:WriteObject, and fs:ImportFromStorage for an address outside the repository's data prefix | arn:lakefs:fs:::repository/{repositoryId}/object/{objectKey} and arn:lakefs:fs:::namespace/{physicalAddress} | PUT /repositories/{repositoryId}/branches/{branchId}/staging/backing | - |
| Delete Object | fs:DeleteObject | arn:lakefs:fs:::repository/{repositoryId}/object/{objectKey} | DELETE /repositories/{repositoryId}/branches/{branchId}/objects | DeleteObject, DeleteObjects`, AbortMultipartUpload |
| Revert Branch | fs:RevertBranch | arn:lakefs:fs:::repository/{repositoryId}/branch/{branchId} | PUT /repositories/{repositoryId}/branches/{branchId} | - |
| Get Branch Protection Rules | branches:GetBranchProtectionRules | arn:lakefs:fs:::repository/{repositoryId} | GET /repositories/{repository}/branch_protection | - |
| Set Branch Protection Rules | branches:SetBranchProtectionRules | arn:lakefs:fs:::repository/{repositoryId} | POST /repositories/{repository}/branch_protection | - |
| Delete Branch Protection Rules | branches:SetBranchProtectionRules | arn:lakefs:fs:::repository/{repositoryId} | DELETE /repositories/{repository}/branch_protection | - |
| Create User | auth:CreateUser | arn:lakefs:auth:::user/{userId} | POST /auth/users | - |
| List Users | auth:ListUsers | * | GET /auth/users | - |
| Get User | auth:ReadUser | arn:lakefs:auth:::user/{userId} | GET /auth/users/{userId} | - |
| Delete User | auth:DeleteUser | arn:lakefs:auth:::user/{userId} | DELETE /auth/users/{userId} | - |
| Get Group | auth:ReadGroup | arn:lakefs:auth:::group/{groupId} | GET /auth/groups/{groupId} | - |
| List Groups | auth:ListGroups | * | GET /auth/groups | - |
| Create Group | auth:CreateGroup | arn:lakefs:auth:::group/{groupId} | POST /auth/groups | - |
| Delete Group | auth:DeleteGroup | arn:lakefs:auth:::group/{groupId} | DELETE /auth/groups/{groupId} | - |
| List Policies | auth:ListPolicies | * | GET /auth/policies | - |
| Create Policy | auth:CreatePolicy | arn:lakefs:auth:::policy/{policyId} | POST /auth/policies | - |
| Update Policy | auth:UpdatePolicy | arn:lakefs:auth:::policy/{policyId} | POST /auth/policies | - |
| Delete Policy | auth:DeletePolicy | arn:lakefs:auth:::policy/{policyId} | DELETE /auth/policies/{policyId} | - |
| Get Policy | auth:ReadPolicy | arn:lakefs:auth:::policy/{policyId} | GET /auth/policies/{policyId} | - |
| List Group Members | auth:ReadGroup | arn:lakefs:auth:::group/{groupId} | GET /auth/groups/{groupId}/members | - |
| Add Group Member | auth:AddGroupMember | arn:lakefs:auth:::group/{groupId} | PUT /auth/groups/{groupId}/members/{userId} | - |
| Remove Group Member | auth:RemoveGroupMember | arn:lakefs:auth:::group/{groupId} | DELETE /auth/groups/{groupId}/members/{userId} | - |
| List User Credentials | auth:ListCredentials | arn:lakefs:auth:::user/{userId} | GET /auth/users/{userId}/credentials | - |
| Create User Credentials | auth:CreateCredentials | arn:lakefs:auth:::user/{userId} | POST /auth/users/{userId}/credentials | - |
| Delete User Credentials | auth:DeleteCredentials | arn:lakefs:auth:::user/{userId} | DELETE /auth/users/{userId}/credentials/{accessKeyId} | - |
| Get User Credentials | auth:ReadCredentials | arn:lakefs:auth:::user/{userId} | GET /auth/users/{userId}/credentials/{accessKeyId} | - |
| List User Groups | auth:ReadUser | arn:lakefs:auth:::user/{userId} | GET /auth/users/{userId}/groups | - |
| List User Policies | auth:ReadUser | arn:lakefs:auth:::user/{userId} | GET /auth/users/{userId}/policies | - |
| Attach Policy To User | auth:AttachPolicy | arn:lakefs:auth:::user/{userId} | PUT /auth/users/{userId}/policies/{policyId} | - |
| Detach Policy From User | auth:DetachPolicy | arn:lakefs:auth:::user/{userId} | DELETE /auth/users/{userId}/policies/{policyId} | - |
| List Group Policies | auth:ReadGroup | arn:lakefs:auth:::group/{groupId} | GET /auth/groups/{groupId}/policies | - |
| Attach Policy To Group | auth:AttachPolicy | arn:lakefs:auth:::group/{groupId} | PUT /auth/groups/{groupId}/policies/{policyId} | - |
| Detach Policy From Group | auth:DetachPolicy | arn:lakefs:auth:::group/{groupId} | DELETE /auth/groups/{groupId}/policies/{policyId} | - |
| Attach External Principal to a User | auth:CreateUserExternalPrincipal | arn:lakefs:auth:::user/{userId} | POST /auth/users/{userId}/external/principals | - |
| Delete External Principal Attachment from a User | auth:DeleteUserExternalPrincipal | arn:lakefs:auth:::user/{userId} | DELETE /auth/users/{userId}/external/principals | - |
| Get the User attached to an External Principal | auth:ReadExternalPrincipal | arn:lakefs:auth:::externalPrincipal/{principalId} | GET /auth/external/principals | - |
| List Tenants | auth:ListTenants | arn:lakefs:auth:::tenant/{tenantName} (admitted per record) | GET /auth/tenants | - |
| Get Tenant | auth:ReadTenant | arn:lakefs:auth:::tenant/{tenantName} | GET /auth/tenants/{tenantName} | - |
| Create Tenant | auth:CreateTenant | arn:lakefs:auth:::tenant/{tenantName} | POST /auth/tenants | - |
| Update Tenant | auth:UpdateTenant | arn:lakefs:auth:::tenant/{tenantName} | PATCH /auth/tenants/{tenantName} | - |
| Delete Tenant | auth:DeleteTenant | arn:lakefs:auth:::tenant/{tenantName} | DELETE /auth/tenants/{tenantName} | - |
| List Tenant Users | auth:ReadTenant | arn:lakefs:auth:::tenant/{tenantName} | GET /auth/tenants/{tenantName}/users | - |
| List Tenant Groups | auth:ReadTenant | arn:lakefs:auth:::tenant/{tenantName} | GET /auth/tenants/{tenantName}/groups | - |
| Attach User To Tenant | auth:AttachTenantUser | arn:lakefs:auth:::tenant/{tenantName} | PUT /auth/tenants/{tenantName}/users/{userId} | - |
| Detach User From Tenant | auth:DetachTenantUser | arn:lakefs:auth:::tenant/{tenantName} | DELETE /auth/tenants/{tenantName}/users/{userId} | - |
| Attach Group To Tenant | auth:AttachTenantGroup | arn:lakefs:auth:::tenant/{tenantName} | PUT /auth/tenants/{tenantName}/groups/{groupId} | - |
| Detach Group From Tenant | auth:DetachTenantGroup | arn:lakefs:auth:::tenant/{tenantName} | DELETE /auth/tenants/{tenantName}/groups/{groupId} | - |
| Read Storage Config | fs:ReadConfig | * | GET /config/storage | - |
| Get Garbage Collection Rules | retention:GetGarbageCollectionRules | arn:lakefs:fs:::repository/{repositoryId} | GET /repositories/{repositoryId}/gc/rules | - |
| Set Garbage Collection Rules | retention:SetGarbageCollectionRules | arn:lakefs:fs:::repository/{repositoryId} | POST /repositories/{repositoryId}/gc/rules | - |
| Prepare Garbage Collection Commits | retention:PrepareGarbageCollectionCommits | arn:lakefs:fs:::repository/{repositoryId} | POST /repositories/{repositoryId}/gc/prepare_commits | - |
| Get Object Lifecycle Rules | retention:GetObjectLifecycleRules | arn:lakefs:fs:::repository/{repositoryId} | GET /repositories/{repository}/settings/object_lifecycle | - |
| Set Object Lifecycle Rules | retention:SetObjectLifecycleRules | arn:lakefs:fs:::repository/{repositoryId} | PUT, DELETE /repositories/{repository}/settings/object_lifecycle | - |
| Get Branch Lifecycle Policies | branches:GetBranchLifecyclePolicies | arn:lakefs:fs:::repository/{repositoryId} | GET /repositories/{repository}/settings/branch_lifecycle/policies | - |
| Set Branch Lifecycle Policies | branches:SetBranchLifecyclePolicies | arn:lakefs:fs:::repository/{repositoryId} | PUT, DELETE /repositories/{repository}/settings/branch_lifecycle/policies | - |
| List Repository Action Runs | ci:ReadAction | arn:lakefs:fs:::repository/{repositoryId} | GET /repositories/{repository}/actions/runs | - |
| Get Action Run | ci:ReadAction | arn:lakefs:fs:::repository/{repositoryId} | GET /repositories/{repository}/actions/runs/{run_id} | - |
| List Action Run Hooks | ci:ReadAction | arn:lakefs:fs:::repository/{repositoryId} | GET /repositories/{repository}/actions/runs/{run_id}/hooks | - |
| Get Action Run Hook Output | ci:ReadAction | arn:lakefs:fs:::repository/{repositoryId} | GET /repositories/{repository}/actions/runs/{run_id}/hooks/{hook_run_id}/output | - |
| Get Pull Request | pr:ReadPullRequest | arn:lakefs:fs:::repository/{repositoryId} | GET /repositories/{repository}/pulls/{pull_request} | - |
| Create Pull Request | pr:WritePullRequest | arn:lakefs:fs:::repository/{repositoryId} | POST /repositories/{repository}/pulls | - |
| Update Pull Request | pr:WritePullRequest | arn:lakefs:fs:::repository/{repositoryId} | PATCH /repositories/{repository}/pulls/{pull_request} | - |
| Merge Pull Request | pr:WritePullRequest + Merge Branches | arn:lakefs:fs:::repository/{repositoryId} | PUT /repositories/{repository}/pulls/{pull_request}/merge | - |
| List Pull Requests | pr:ListPullRequests | arn:lakefs:fs:::repository/{repositoryId} | GET /repositories/{repository}/pulls | - |
| List Namespaces | catalog:ListNamespaces | arn:lakefs:catalog:::namespace/{repositoryId}/{namespace} | GET /iceberg/api/v1/{prefix}/namespaces | - |
| Get Namespace | catalog:GetNamespace | arn:lakefs:catalog:::namespace/{repositoryId}/{namespace} | GET /iceberg/api/v1/{prefix}/namespaces/{namespace} | - |
| Create Namespace | catalog:CreateNamespace | arn:lakefs:catalog:::namespace/{repositoryId}/{namespace} | POST /iceberg/api/v1/{prefix}/namespaces | - |
| Update Namespace | catalog:UpdateNamespace | arn:lakefs:catalog:::namespace/{repositoryId}/{namespace} | POST /iceberg/api/v1/{prefix}/namespaces/{namespace}/properties | - |
| Delete Namespace | catalog:DeleteNamespace | arn:lakefs:catalog:::namespace/{repositoryId}/{namespace} | DELETE /iceberg/api/v1/{prefix}/namespaces/{namespace} | - |
| List Tables | catalog:ListTables | arn:lakefs:catalog:::namespace/{repositoryId}/{namespace} | GET /iceberg/api/v1/{prefix}/namespaces/{namespace}/tables | - |
| Create Table | catalog:CreateTable | arn:lakefs:catalog:::table/{repositoryId}/{namespace}/{table} | POST /iceberg/api/v1/{prefix}/namespaces/{namespace}/tables | - |
| Read Table | catalog:ReadTable | arn:lakefs:catalog:::table/{repositoryId}/{namespace}/{table} | GET /iceberg/api/v1/{prefix}/namespaces/{namespace}/tables/{table} | - |
| Update Table | catalog:UpdateTable | arn:lakefs:catalog:::table/{repositoryId}/{namespace}/{table} | POST /iceberg/api/v1/{prefix}/namespaces/{namespace}/tables/{table} | - |
| Delete Table | catalog:DeleteTable | arn:lakefs:catalog:::table/{repositoryId}/{namespace}/{table} | DELETE /iceberg/api/v1/{prefix}/namespaces/{namespace}/tables/{table} | - |
| Vend Read Table Data | catalog:ReadTableData | arn:lakefs:catalog:::table/{repositoryId}/{namespace}/{table} | GET /iceberg/api/v1/{prefix}/namespaces/{namespace}/tables/{table}/credentials (also vended inline via X-Iceberg-Access-Delegation, see Credentials Vending) | - |
| Vend Write Table Data | catalog:WriteTableData | arn:lakefs:catalog:::table/{repositoryId}/{namespace}/{table} | GET /iceberg/api/v1/{prefix}/namespaces/{namespace}/tables/{table}/credentials (also vended inline via X-Iceberg-Access-Delegation, see Credentials Vending) | - |
| List Views | catalog:ListViews | arn:lakefs:catalog:::namespace/{repositoryId}/{namespace} | GET /iceberg/api/v1/{prefix}/namespaces/{namespace}/views | - |
| Create View | catalog:CreateView | arn:lakefs:catalog:::view/{repositoryId}/{namespace}/{view} | POST /iceberg/api/v1/{prefix}/namespaces/{namespace}/views | - |
| Read View | catalog:ReadView | arn:lakefs:catalog:::view/{repositoryId}/{namespace}/{view} | GET /iceberg/api/v1/{prefix}/namespaces/{namespace}/views/{view} | - |
| Update View | catalog:UpdateView | arn:lakefs:catalog:::view/{repositoryId}/{namespace}/{view} | POST /iceberg/api/v1/{prefix}/namespaces/{namespace}/views/{view} | - |
| Delete View | catalog:DeleteView | arn:lakefs:catalog:::view/{repositoryId}/{namespace}/{view} | DELETE /iceberg/api/v1/{prefix}/namespaces/{namespace}/views/{view} | - |
| Read Audit Log | audit:ReadAuditLog | arn:lakefs:audit:::log | Iceberg catalog read operations targeting the lakefssystem repository | - |
| Write Audit Log | audit:WriteAuditLog | arn:lakefs:audit:::log | Iceberg catalog write operations targeting the lakefssystem repository | - |
| Login as Organization Admin (Cloud only) | admin:Login | * | POST /admin/login (part of lakefs cloud, not lakefs endpoint) | - |
| List Datasets | dataset:ListDatasets | arn:lakefs:dataset:::dataset/{datasetId} | GET /datasets | - |
| Read Dataset | dataset:ReadDataset | arn:lakefs:dataset:::dataset/{datasetId} | GET /datasets/{dataset} | - |
| Create Dataset | dataset:CreateDataset | arn:lakefs:dataset:::dataset/{datasetId} | POST /datasets | - |
| Update Dataset | dataset:UpdateDataset | arn:lakefs:dataset:::dataset/{datasetId} | PUT /datasets/{dataset} | - |
| Delete Dataset | dataset:DeleteDataset | arn:lakefs:dataset:::dataset/{datasetId} | DELETE /datasets/{dataset} | - |
| List Dataset Metadata Keys | dataset:ListMetadataKeys | arn:lakefs:dataset:::dataset/{datasetId} | GET /datasets/metadata | - |
| Read Dataset Metadata Key | dataset:ReadMetadataKey | arn:lakefs:dataset:::dataset/* | GET /datasets/metadata/{key} | - |
| Create Dataset Metadata Key | dataset:CreateMetadataKey | arn:lakefs:dataset:::dataset/* | POST /datasets/metadata | - |
| Update Dataset Metadata Key | dataset:UpdateMetadataKey | arn:lakefs:dataset:::dataset/* | PUT /datasets/metadata/{key} | - |
| Delete Dataset Metadata Key | dataset:DeleteMetadataKey | arn:lakefs:dataset:::dataset/* | DELETE /datasets/metadata/{key} | - |
어떤 API는 두 개 이상의 액션을 요구할 수 있어요. 예를 들어 저장소를 만들려면(POST /repositories), 저장소 이름에 대한 fs:CreateRepository 권한과 사용되는 스토리지 네임스페이스에 대한 fs:AttachStorageNamespace 권한이 모두 필요해요.
fs:ImportFromStorage는 스토리지 위치에 대한 읽기 접근을 부여해요
어떤 스토리지 위치에 fs:ImportFromStorage를 보유한 사용자는 lakeFS가 그 아래의 어떤 객체든 제공하게 만들 수 있어요: import는 그곳에서 발견한 객체에 엔트리를 만들고, stageObject와 linkPhysicalAddress는 그 아래의 어떤 물리적 주소든 받아들여요. 그 권한이 없으면 stageObject는 저장소 자체의 스토리지 네임스페이스 안의 주소만 받아들이고, linkPhysicalAddress는 getPhysicalAddress가 쓰려는 경로에 발급한 저장소의 데이터 프리픽스 아래 주소만 받아들여요. 따라서 다른 저장소가 사용하는 스토리지에 이 액션을 부여하면, 경로 범위의 fs:ReadObject 명령문이나 객체 수준 ABAC 조건과 무관하게 그들의 객체가 노출돼요. 그래서 이 액션의 리소스 ARN은 사용자가 임포트하도록 의도된 위치로 한정하세요.
ListBranches의 브랜치별 스코핑
fs:ListBranches는 repo ARN(arn:lakefs:fs:::repository/{repositoryId}, 저장소의 모든 브랜치 승인)이나 브랜치별 ARN(arn:lakefs:fs:::repository/{repositoryId}/branch/{branchId}, 일치하는 브랜치만 승인)을 받아들여요. 그래서 정책이 나열을 브랜치 하위 집합으로 한정할 수 있어요. 나열은 먼저 그 저장소를 나열할 인가를 요구한 다음, 여러분이 볼 수 있는 브랜치를 필터링해요: repo ARN의 Deny(또는 저장소에 스코프된 Allow가 없음)는 연산 수준의 거부이고, 브랜치 ARN의 Deny는 일치하는 브랜치만 가려요.
| Policy shape | Result |
|---|---|
| Allow on repo ARN | every branch admitted |
| Allow on branch ARN matching X | branch X admitted, others hidden |
| Allow on repo ARN + Deny on branch ARN matching X | branch X hidden, the rest admitted |
| No Allow scoped to this repository (none at all, or only on a different one) | request returns 401 (REST) / 403 AccessDenied (S3) |
Allow는 가산적이에요 - 더 좁은 Allow를 더 넓은 것 옆에 추가한다고 결과 집합이 조여지지 않아요. 사용자가 보는 것을 제한하려면, 넓은 Allow를 좁은 것으로 교체하거나, 넓은 Allow를 유지하면서 가리고 싶은 하위 집합에 Deny를 추가하세요.
사용자를 한 팀의 브랜치로 한정하기:
{
"Effect": "Allow",
"Action": ["fs:ListBranches", "fs:ReadBranch"],
"Resource": "arn:lakefs:fs:::repository/myrepo/branch/team-a-*"
}
하위 집합을 제외한 모든 브랜치 허용(repo에 넓은 allow, 가릴 패턴에 targeted deny):
[
{
"Effect": "Allow",
"Action": ["fs:ListBranches"],
"Resource": "arn:lakefs:fs:::repository/myrepo"
},
{
"Effect": "Deny",
"Action": ["fs:ListBranches"],
"Resource": "arn:lakefs:fs:::repository/myrepo/branch/secret-*"
}
]
참고.
-
나열이 필터로 승인되는 브랜치가 없는 저장소를 나열할 권한을 가진 사용자 - 예를 들어
…/branch/team-z-*에 대한 allow인데 매칭되는 브랜치가 없는 경우 - 는 빈 목록과 함께200 OK를 받아요. -
401/403응답은 저장소가 존재하는지 여부와 무관하게 동일해요. 그래서 나열을 저장소 이름 발견에 사용할 수 없어요. -
유일한 allow가 저장소의 기본 브랜치와 매칭되지 않는 브랜치별 ARN뿐인 사용자는 나열에서 기본 브랜치를 볼 수 없어요(
GetRepository의default_branch는 여전히 그 이름을 보고해요). -
S3 게이트웨이의 버킷 루트 나열(
aws s3 ls s3://{repo}/)은 같은 평가를 사용하고, 브랜치 나열 케이스에 대해서는 별도의fs:ListObjects권한을 요구하지 않아요.
Listing visibility is not access control
fs:ListBranches의 스코핑은 나열에 어떤 브랜치가 나타나는지만 제어해요 - 그것들을 읽거나 쓰는 것을 제한하지 않아요. 접근은 브랜치별, 객체별 액션(fs:ReadBranch, fs:ReadObject, fs:WriteObject, …)에 의해 독립적으로 거버넌스돼요: 브랜치를 나열할 수 없는 사용자도 그 액션들을 보유하고 있다면 읽거나 커밋할 수 있어요. fs:ListBranches는 발견 가능성(discoverability)에 사용하고, 접근을 한정하려면 브랜치별 액션도 스코프하세요.
사전 구성 정책
다음 정책(Policies)은 초기 설정 과정에서 생성돼요:
FSFullAccess
{
"statement": [
{
"action": [
"fs:*"
],
"effect": "allow",
"resource": "*"
}
]
}
FSReadAll
{
"statement": [
{
"action": [
"fs:List*",
"fs:Read*"
],
"effect": "allow",
"resource": "*"
}
]
}
FSReadWriteAll
{
"statement": [
{
"action": [
"fs:Read*",
"fs:List*",
"fs:WriteObject",
"fs:DeleteObject",
"fs:RevertBranch",
"fs:CreateBranch",
"fs:CreateTag",
"fs:DeleteBranch",
"fs:DeleteTag",
"fs:CreateCommit"
],
"effect": "allow",
"resource": "*"
}
]
}
AuthFullAccess
{
"statement": [
{
"action": [
"auth:*"
],
"effect": "allow",
"resource": "*"
}
]
}
AuthManageOwnCredentials
{
"statement": [
{
"action": [
"auth:CreateCredentials",
"auth:DeleteCredentials",
"auth:ListCredentials",
"auth:ReadCredentials"
],
"effect": "allow",
"resource": "arn:lakefs:auth:::user/${user}"
}
]
}
RepoManagementFullAccess
{
"statement": [
{
"action": [
"ci:*"
],
"effect": "allow",
"resource": "*"
},
{
"action": [
"retention:*"
],
"effect": "allow",
"resource": "*"
}
]
}
RepoManagementReadAll
{
"statement": [
{
"action": [
"ci:Read*"
],
"effect": "allow",
"resource": "*"
},
{
"action": [
"retention:Get*"
],
"effect": "allow",
"resource": "*"
}
]
}
AdminFullAccess (lakeFS Cloud 전용)
{
"statement": [
{
"action": [
"admin:*"
],
"effect": "allow",
"resource": "*"
}
]
}
AuditLogRead
lakeFS 감사 로그에 대한 읽기 접근을 부여해요. 기본으로 Admins 그룹에 연결돼요. 감사 로그 가시성을 위임하려면 다른 그룹이나 사용자에게 연결하세요.
{
"statement": [
{
"action": [
"audit:ReadAuditLog"
],
"effect": "allow",
"resource": "arn:lakefs:audit:::log"
}
]
}
DatasetsFullAccess
모든 데이터셋 연산에 대한 전체 접근을 부여해요. Datasets을 참고하세요.
{
"statement": [
{
"action": [
"dataset:*"
],
"effect": "allow",
"resource": "arn:lakefs:dataset:::dataset/*"
}
]
}
DatasetsReadWriteAll
데이터셋에 대한 목록, 읽기, 생성, 업데이트, 삭제 접근과 메타데이터 키 읽기 접근을 부여해요.
{
"statement": [
{
"action": [
"dataset:ListDatasets",
"dataset:ReadDataset",
"dataset:CreateDataset",
"dataset:UpdateDataset",
"dataset:DeleteDataset",
"dataset:ListMetadataKeys",
"dataset:ReadMetadataKey"
],
"effect": "allow",
"resource": "arn:lakefs:dataset:::dataset/*"
}
]
}
DatasetsReadAll
데이터셋과 메타데이터 키에 대한 읽기 전용 접근을 부여해요.
{
"statement": [
{
"action": [
"dataset:ListDatasets",
"dataset:ReadDataset",
"dataset:ListMetadataKeys",
"dataset:ReadMetadataKey"
],
"effect": "allow",
"resource": "arn:lakefs:dataset:::dataset/*"
}
]
}
DatasetsManageMetadataKeys
데이터셋 메타데이터 키 정의에 대한 전체 CRUD를 부여해요.
{
"statement": [
{
"action": [
"dataset:ListMetadataKeys",
"dataset:ReadMetadataKey",
"dataset:CreateMetadataKey",
"dataset:UpdateMetadataKey",
"dataset:DeleteMetadataKey"
],
"effect": "allow",
"resource": "arn:lakefs:dataset:::dataset/*"
}
]
}
테넌트 관리 정책
테넌트 관리는 테넌트 ARN에 대한 auth:*Tenant* 액션으로 거버넌스되며, 아래 두 정책은 그것을 활용하는 레시피예요. 위에 나열된 정책들과 달리 둘 다 설정 과정에서 생성되지 않기 때문에, 필요한 쪽을 직접 작성해야 해요. 테넌트별 부여는 적용될 테넌트를 지정해야 하고, ${user}가 현재 사용자를 대신하는 것처럼 "이 사용자가 관리하는 테넌트"를 대신하는 플레이스홀더는 없어서, 테넌트별 위임은 테넌트당 정책 하나가 돼요.
이 정책들은 테넌트 관리만 거버넌스한다는 점에 주의하세요. 테넌트의 저장소에 도달하려면 추가로 그 테넌트의 멤버십이 필요해서, 데이터도 다루는 관리자는 다른 사용자처럼 테넌트에 연결되어야 해요.
TenantsAdmin
설치 환경의 모든 테넌트에 대한 관리 권한을 부여해요. 생성과 삭제를 포함해요. 기본 관리자의 AuthFullAccess가 이미 이 모든 것을 커버하기 때문에, auth:*의 나머지를 넘겨주지 않고 설치 전체 테넌트 관리를 누군가에게 넘기고 싶을 때 TenantsAdmin을 찾으세요:
{
"statement": [
{
"action": [
"auth:*Tenant*"
],
"effect": "allow",
"resource": "arn:lakefs:auth:::tenant/*"
}
]
}
모든 테넌트를 부여하는 것은 그들을 한꺼번에 커버하는 연결(attachment)을 부여하는 유일한 방법이기도 해요. 독립형 가비지 컬렉션에서 사용하는 것 같은 설치 전체 아이덴티티가 이후에 생성된 테넌트를 포함해 모든 테넌트에 도달하는 방식이에요.
TenantAdmin-{tenant}
단일 테넌트의 관리 권한을 부여해요. 팀이 자기 테넌트를 운영하게 하려는 정책으로, 팀의 그룹에 연결하면 돼요. 의도적으로 auth:CreateTenant와 auth:DeleteTenant는 생략해서 테넌트 라이프사이클은 설치 관리자에게 남겨두고, 리소스에 테넌트를 지정해서 테넌트별로 별도의 사본이 존재하게 해요. 이름은 lakeFS가 파싱하는 게 아니라 긴 정책 목록을 읽기 좋게 유지하는 컨벤션이에요:
{
"statement": [
{
"action": [
"auth:ReadTenant",
"auth:ListTenants",
"auth:UpdateTenant",
"auth:AttachTenantUser",
"auth:DetachTenantUser",
"auth:AttachTenantGroup",
"auth:DetachTenantGroup"
],
"effect": "allow",
"resource": "arn:lakefs:auth:::tenant/team-a"
}
]
}
그 정책을 그룹에 연결하면 위임이 완성되고, 결과 관리자는 자기 테넌트의 멤버십과 설명을 관리할 수 있어요.
테넌트 나열은 각 레코드를 개별적으로 승인해요. 그래서 나열에는 그들의 정책이 허용하는 테넌트만 포함되고, 그 집합 밖의 어떤 테넌트든 접근이 거부됐다고 보고하는 대신 존재하지 않는 것처럼 응답해요.
따라서 위임된 관리자는 API로 이 설치가 어떤 다른 테넌트를 가지고 있는지 알아낼 수 없어요.
추가 정책
사용자 접근을 더 제한하기 위해 추가 정책을 만들 수 있어요. 웹 UI나 lakectl auth 명령으로 정책을 만드세요.
특정 저장소에 대한 읽기/쓰기 접근을 정의하는 예제예요:
{
"statement": [
{
"action": [
"fs:ReadRepository",
"fs:ReadCommit",
"fs:ListBranches",
"fs:ListTags",
"fs:ListObjects"
],
"effect": "allow",
"resource": "arn:lakefs:fs:::repository/<repository-name>"
},
{
"action": [
"fs:RevertBranch",
"fs:ReadBranch",
"fs:CreateBranch",
"fs:DeleteBranch",
"fs:CreateCommit"
],
"effect": "allow",
"resource": "arn:lakefs:fs:::repository/<repository-name>/branch/*"
},
{
"action": [
"fs:ListObjects",
"fs:ReadObject",
"fs:WriteObject",
"fs:DeleteObject"
],
"effect": "allow",
"resource": "arn:lakefs:fs:::repository/<repository-name>/object/*"
},
{
"action": [
"fs:ReadTag",
"fs:CreateTag",
"fs:DeleteTag"
],
"effect": "allow",
"resource": "arn:lakefs:fs:::repository/<repository-name>/tag/*"
},
{
"action": ["fs:ReadConfig"],
"effect": "allow",
"resource": "*"
}
]
}
다중 리소스 명령문
lakeFS는 단일 RBAC 명령문에 여러 리소스를 지정하는 것을 지원해요. 이는 lakeFS Cloud에서, 또는 lakeFS v1.54.0과 Fluffy v0.12.0부터 시작하는 lakeFS Enterprise에서 사용할 수 있어요. 단일 리소스 외에, 리소스 필드는 JSON 인코딩된 리소스 목록을 나타내는 문자열을 포함할 수 있어요.
{
"statement": [
{
"action": [
"fs:Read*"
],
"effect": "allow",
"resource": "[\"arn:lakefs:fs:::repository/repo1\",\"arn:lakefs:fs:::repository/repo2\"]"
}
]
}
목록은 JSON 문자열로 올바르게 인코딩되어야 해요: 각 리소스를 따옴표로 묶고, 그 따옴표를 보여진 대로 이스케이프하세요. 그렇지 않으면 정책을 파싱할 수 없어요.
Python SDK로 다중 리소스 정책 만들기
Python SDK로 다중 리소스 정책을 만드는 방법이에요:
import lakefs_sdk
from lakefs_sdk.client import LakeFSClient
from lakefs_sdk import models
configuration = lakefs_sdk.Configuration(
host=lakefsEndPoint,
username=lakefsAccessKey,
password=lakefsSecretKey,
)
clt = LakeFSClient(configuration)
clt.auth_api.create_policy(
policy=models.Policy(
id='FSReadTwoRepos',
statement=[models.Statement(
effect="deny",
resource=json.dumps(["arn:lakefs:fs:::repository/repo1","arn:lakefs:fs:::repository/repo2"]),
action=["fs:ReadRepository"],
),
]
)
)
사전 구성 그룹
lakeFS에는 네 개의 사전 구성 그룹이 있어요:
-
Admins
-
SuperUsers
-
Developers
-
Viewers
이 그룹들에는 다음 정책이 부여돼 있어요:
| Policy | Admins | SuperUsers | Developers | Viewers |
|---|---|---|---|---|
| FSFullAccess | ✅ | ✅ | ||
| AuthFullAccess | ✅ | |||
| RepoManagementFullAccess | ✅ | |||
| AuthManageOwnCredentials | ✅ | ✅ | ✅ | |
| RepoManagementReadAll | ✅ | ✅ | ||
| FSReadWriteAll | ✅ | |||
| FSReadAll | ✅ | |||
| AuditLogRead | ✅ | |||
| AdminFullAccess (Cloud only) | ✅ |
플러그형 인증과 인가
인가와 인증은 lakeFS에서 플러그형이에요.
lakeFS가 원격 인증 서버에 연결되어 있다면(또는 lakeFS Cloud를 사용 중이라면) 역할 기반 접근 제어 UI를 사용할 수 있어요.
자체 관리 lakeFS에서 RBAC를 사용한다면 lakeFS 설정 요소 auth.ui_config.rbac을 external로 설정해야 해요.
엔터프라이즈(유료) lakeFS 솔루션은 auth.ui_config.rbac을 internal로 설정해야 해요.
더 알아보기 (Learn more)
공식 문서의 원문은 https://docs.lakefs.io/security/rbac/ 에서 확인할 수 있어요.