AWS IAM 역할로 lakeFS 인증하기
lakeFS Team과 AWS에서 동작하는 lakeFS Enterprise에서 사용할 수 있어요. 무료 체험을 시작하거나 문의하세요.
본문
개요
lakeFS는 정적인 lakeFS access key와 secret key 대신 AWS IAM 역할로 프로그래매틱하게 사용자를 인증하는 것을 지원해요. 이 방식을 사용하면 IAM principal ARN을 lakeFS 사용자에 바인딩할 수 있어요. 하나의 lakeFS 사용자에는 여러 AWS principal ARN이 연결될 수 있어요. 클라이언트가 AWS 세션으로 lakeFS 서버에 인증하면, 클라이언트가 수행하는 액션은 그 ARN에 연결된 사용자를 대신해 수행돼요.
세션 이름 사용하기
바인딩된 ARN은 SessionName이 있든 없든 단일 lakeFS 사용자에 연결될 수 있고, 서로 다른 사용자를 제공할 수 있어요. 예를 들어 다음 매핑을 보세요:
| Principal ARN | lakeFS 사용자 |
|---|---|
| arn:aws:sts::123456:assumed-role/Dev | foo |
| arn:aws:sts::123456:assumed-role/Dev/[email protected] | john |
바인딩된 ARN이 arn:aws:sts::123456:assumed-role/Dev/<SessionName>이라면, AWS 계정 123456에서 Dev 역할을 assume하는 어떤 principal이든 로그인할 수 있어요. SessionName이 [email protected]이면 lakeFS는 john 사용자의 토큰을 반환해요.
AWS 인증 동작 방식
AWS STS API에는 클라이언트의 아이덴티티를 검증할 수 있게 해주는 sts:GetCallerIdentity 메서드가 있어요. 클라이언트는 AWS Signature v4 알고리즘으로 GetCallerIdentity 쿼리에 서명해 lakeFS 서버로 보내요.
GetCallerIdentity 쿼리는 네 가지 정보로 구성돼요: 요청 URL, 요청 바디, 요청 헤더, 요청 메서드. AWS 시그니처는 이 필드들 위에서 계산돼요. lakeFS 서버는 이 정보로 쿼리를 재구성해 AWS STS 서비스로 전달해요. STS 서비스의 응답에 따라 서버가 클라이언트를 인증해요.
특히 주목할 점은, 클라이언트가 AWS STS API 엔드포인트와 통신하기 위해 자체적으로 네트워크 수준 접근이 필요 없다는 거예요. 요청에 서명할 자격 증명에 접근할 수 있으면 충분해요. 반면 lakeFS 서버는 STS 엔드포인트로 요청을 보내기 위해 네트워크 수준 접근이 필요해요.
서명된 각 AWS 요청은 현재 타임스탬프를 포함해 리플레이 공격 위험을 완화해요. 또한 lakeFS는 다양한 종류의 리플레이 공격(예: 개발 lakeFS 인스턴스에서 훔친 서명된 GetCallerIdentity 요청으로 프로덕션 lakeFS 인스턴스에 인증하는 것)에 대비해 추가 헤더 X-LakeFS-Server-ID(기본으로 추가됨)의 존재를 요구할 수 있어요.
또한 Amazon은 GetCallerIdentity 호출 주변에 어떤 종류의 인가도 포함하지 않는 것으로 보인다는 점도 중요해요. 예를 들어 자격 증명에 모든 접근이 MFA 인증을 요구하는 IAM 정책이 있어도, 비(非)MFA 인증 자격 증명은 이 방식으로 lakeFS에 인증할 수 있어요.
서버 설정
lakeFS Cloud
호스티드 서비스에서 AWS上에서 실행되는 설치 환경은 외부 AWS 인증이 이미 활성화된 상태로 제공돼요. 따라서 이 섹션은 적용되지 않고 변경할 서버 설정도 없어요. 사용자에게 IAM 역할을 연결하려면 조직의 구독에 IAM 역할 인증 기능이 필요해요. 클라이언트는 설치 환경의 정식(canonical) 호스트명을 통해 로그인해야 해요. 커스텀 도메인이나 PrivateLink 엔드포인트로 도착한 요청은 시그니처 검증에 실패해요.
Info
lakeFS Helm chart는 버전 1.5.0부터 아래 설정을 지원해요
lakeFS Enterprise에서 AWS IAM 인증을 활성화하려면:
-
lakeFS 설정에서 external principals를 활성화해요
-
외부 AWS 인증 설정을 구성해요
auth.allowed_authentication_methods가 설정되어 있다면 IAM 인증이 동작하려면 external_principal을 포함해야 해요.
Helm 설정 (values.yaml):
ingress:
enabled: true
ingressClassName: <class-name>
hosts:
- host: <lakefs.ingress.domain>
paths:
- /
lakefsConfig: |
auth:
# Configure external AWS authentication
external_aws_auth:
enabled: true
# the maximum age in seconds for the GetCallerIdentity request
#get_caller_identity_max_age: 60
# headers that must be present by the client when doing login request
required_headers:
# same host as the lakeFS server ingress
X-LakeFS-Server-ID: <lakefs.ingress.domain>
Note
기본적으로 lakeFS 클라이언트는 STS를 위한 초기 로그인 요청에 X-LakeFS-Server-ID: <lakefs.ingress.domain> 파라미터를 추가해요.
직접 설정 파일 (lakefs.yaml):
auth:
external_aws_auth:
enabled: true
# Optional: max age for GetCallerIdentity requests (default: 24h)
get_caller_identity_max_age: 3600
# Required headers for login requests
required_headers:
X-LakeFS-Server-ID: <lakefs.ingress.domain>
# Optional headers that may be present
optional_headers:
X-Custom-Header: value
lakeFS에서 IAM 역할 관리하기
관리란 lakeFS에 인증할 수 있도록 허용된 IAM 역할의 관리를 말해요. 사용자에게 IAM 역할을 연결하거나 떼는 작업, 사용자에 연결된 역할 나열, 역할에 연결된 사용자 나열 같은 연산이 있어요. 이는 lakectl, lakeFS External Principals API, 또는 생성된 클라이언트로 수행할 수 있어요.
lakectl 사용하기
# Attach IAM role to a lakeFS user (omit --id to use current user)
lakectl auth users aws-iam attach [--id <lakefs-user>] \
--principal-id 'arn:aws:sts::<id>:assumed-role/<role A>/<optional session name>'
lakectl auth users aws-iam attach [--id <lakefs-user>] \
--principal-id 'arn:aws:sts::<id>:assumed-role/<role B>'
# Detach an IAM role from a user (omit --id to use current user)
lakectl auth users aws-iam detach [--id <lakefs-user>] \
--principal-id 'arn:aws:sts::<id>:assumed-role/<role A>'
# List IAM roles attached to a user (omit --id to use current user)
lakectl auth users aws-iam list [--id <lakefs-user>]
# Lookup which lakeFS users are attached to an IAM role
lakectl auth users aws-iam lookup --principal-id 'arn:aws:sts::<id>:assumed-role/<role>'
Python SDK 사용하기
import lakefs_sdk as lakefs
configuration = lakefs.Configuration(host = "...",username="...",password="...")
username = "<lakefs-user>"
api = lakefs.ApiClient(configuration)
auth_api = lakefs.AuthApi(api)
# attach the role(s)to a lakeFS user
auth_api.create_user_external_principal(
user_id=username, principal_id='arn:aws:sts::<id>:assumed-role/<role A>/<optional session name>')
auth_api.create_user_external_principal(
user_id=username, principal_id='arn:aws:sts::<id>:assumed-role/<role B>')
# list the roles attached to the user
resp = auth_api.list_user_external_principals(user_id=username)
for p in resp.results:
# do something
Java SDK 사용하기
생성된 AuthApi는 Python SDK와 동일한 external-principal 엔드포인트를 노출해요. 호출은 SDK의 fluent builder 패턴을 사용하고 .execute()로 마무리돼요.
import io.lakefs.clients.sdk.ApiClient;
import io.lakefs.clients.sdk.AuthApi;
import io.lakefs.clients.sdk.model.ExternalPrincipal;
import io.lakefs.clients.sdk.model.ExternalPrincipalList;
ApiClient apiClient = new ApiClient();
apiClient.setBasePath("https://lakefs.example.com/api/v1");
apiClient.setUsername("<access-key-id>");
apiClient.setPassword("<secret-access-key>");
AuthApi authApi = new AuthApi(apiClient);
String userId = "<lakefs-user>";
// attach the role(s) to a lakeFS user
authApi.createUserExternalPrincipal(userId,
"arn:aws:sts::<id>:assumed-role/<role A>/<optional session name>")
.execute();
authApi.createUserExternalPrincipal(userId,
"arn:aws:sts::<id>:assumed-role/<role B>")
.execute();
// list the roles attached to the user
ExternalPrincipalList resp = authApi.listUserExternalPrincipals(userId).execute();
for (ExternalPrincipal p : resp.getResults()) {
// do something
}
// detach a role from the user
authApi.deleteUserExternalPrincipal(userId,
"arn:aws:sts::<id>:assumed-role/<role A>")
.execute();
Note
관리자 호출 자체는 정적 lakeFS 자격 증명(위와 같이)이나 AWS IAM 역할로 인증할 수 있어요. 같은 ApiClient에 AWSIAMAuthProvider를 설치하려면 Login with Java를 참고하세요.
lakeFS API 토큰 얻기
lakeFS 로그인은 클라이언트가 서명한 GetCallerIdentity 요청으로 로그인 API를 호출해 수행돼요. 현재 로그인 연산은 다음에서 기본 지원돼요:
-
lakectl
-
lakeFS Hadoop FileSystem 버전 0.2.4, Spark usage 참고
-
python
-
Java
-
lakeFS Mount
login 엔드포인트로 lakeFS에 인증하는 다른 사용 사례는 요청 입력을 직접 만들어야 해요.
lakectl로 로그인하기
사전 요구 사항
-
lakeFS가 외부 principal 인증을 허용하도록 설정되어야 해요. 관련 IAM 역할이 적절한 lakeFS 사용자에 연결되어 있어야 해요.
-
lakectl이 IAM 인증을 사용하도록 설정되어야 해요.
lakectl 설정
Deprecated
lakeFS Community와 함께 배포되는 lakectl은 기존 설정 파일이 계속 로드되도록 아래 키들을 받아들이지만, lakeFS Community는 그 키들이 호출하는 login 엔드포인트를 제공하지 않기 때문에 무시하고 경고를 로그로 남겨요. AWS IAM으로 인증하려면 lakeFS Enterprise에 포함된 lakectl이 필요해요.
IAM 인증을 사용하려면 다음 설정 필드를 사용할 수 있어요:
-
credentials.provider.type(string: '')-aws_iam으로 설정하면aws_iam블록을 기대하고 IAM 사용을 시도해요. -
credentials.provider.aws_iam.token_ttl_second(duration: 60m)- 선택 사항: lakeFS 토큰 지속 시간. -
credentials.provider.aws_iam.url_presign_ttl_seconds(duration: 15m)- 선택 사항: AWS STS의 presigned URL 검증 지속 시간. -
credentials.provider.aws_iam.refresh_interval(duration: 15m)- 선택 사항: lakeFS Mount가 현재 토큰 대신 새 세션 토큰을 가져오려고 시도하기 전 토큰 만료까지의 시간. -
credentials.provider.aws_iam.token_request_headers: lakeFS 서버에 설정된 대로 AWS STS 요청에 서명되어야 하는 필수 헤더와 값들의 맵. 아무 것도 설정하지 않으면 기본 동작은x-lakefs-server-id:<lakeFS host>를 추가하는 거예요. lakeFS 서버가 어떤 헤더도 요구하지 않는다면(덜 안전함) 설정에서{}빈 맵을 지정해 이를 비울 수 있어요.
이 설정 필드들은 .lakectl.yaml로 설정할 수 있어요:
Example
credentials:
provider:
type: aws_iam # Required
aws_iam:
token_ttl_seconds: 60m # Optional, default: 1h
url_presign_ttl_seconds: 15m # Optional, default: 15m
refresh_interval: 5m # Optional, default: 5m
token_request_headers: # Optional, if omitted then will set x-lakefs-server-id: <lakeFS host> by default, to override default set to '{}'
# x-lakefs-server-id: <lakeFS host> Added by default if token_request_headers is not set
custome-key: custome-val
server:
endpoint_url: <lakeFS endpoint url>
토큰 캐싱
lakectl의 IAM 인증을 최적화하기 위해 간단한 토큰 캐싱 메커니즘이 도입됐어요. lakeFS용 JWT를 얻기 위해 AWS에 로그인 요청을 수행하는 대신, lakectl은 토큰을 $HOMEDIR/.lakectl/cache/lakectl_token_cache.json에 저장해요. 이 토큰은 쓰인 후 1시간까지의 다음 lakectl 연산에 사용돼요. 그 이후에는 lakectl이 새 토큰을 가져오려고 시도해요.
Note
캐시 구현은 여러 AWS 프로필을 지원하지 않아요. 프로필을 전환할 때와 일반적인 문제 해결 시 먼저 캐시를 삭제해 보세요.
python으로 로그인하기
사전 요구 사항
-
lakeFS가 외부 principal 인증을 허용하도록 설정되어야 하고, 사용되는 IAM 역할이 관련 lakeFS 사용자에 연결되어야 해요.
-
Python SDK는 assumed role로 lakeFS 클라이언트를 생성하려면 추가 패키지 설치가 필요해요. 필요한 패키지를 설치하려면 다음 명령을 실행해요:
pip install "lakefs[aws-iam]"
외부 principal로 lakeFS에 인증하는 방법은 두 가지예요:
- 다른 인증 플로우가 제공되지 않고,
.lakectl.yaml에서credentials.provider.type설정이aws_iam으로 되어 있으면, 클라이언트는 머신의 AWS 역할을 사용해 lakeFS와 인증해요:
credentials:
provider:
type: aws_iam
aws_iam:
token_ttl_seconds: 3600 # TTL for the temporary token (default: 3600)
url_presign_ttl_seconds: 60 # TTL for presigned URLs (default: 60)
token_request_headers: # Optional headers for token requests
HeaderName: HeaderValue
또는 환경 변수 사용:
export LAKECTL_CREDENTIALS_PROVIDER_TYPE="aws_iam"
export LAKECTL_CREDENTIALS_PROVIDER_AWS_IAM_TOKEN_TTL_SECONDS="3600"
export LAKECTL_CREDENTIALS_PROVIDER_AWS_IAM_PRESIGNED_URL_TTL_SECONDS="60"
export LAKECTL_CREDENTIALS_PROVIDER_AWS_IAM_TOKEN_REQUEST_HEADERS='{"HeaderName":"HeaderValue"}'
클라이언트를 사용하려면 그냥 import lakefs하고 평소처럼 사용하면 돼요:
import lakefs
for branch in lakefs.repository("example-repo").branches():
print(branch)
Warning IAM provider 설정을 사용하는 것은 lakectl 명령줄 도구에서는 동작하지 않으며, 실행을 막아요.
- 원하는 역할로 boto3 세션을 초기화하고
lakefs.client.frow_aws_role을 호출해 assumed role로 lakeFS 클라이언트를 생성해요:
import lakefs
import boto3
session = boto3.Session()
my_client = lakefs.client.from_aws_role(session=session, ttl_seconds=7200, host="<lakefs-host>")
# list repositories
repos = lakefs.repositories(client=my_client)
for r in repos:
print(r)
Java로 로그인하기
사전 요구 사항
-
lakeFS가 외부 principal 인증을 허용하도록 설정되어야 하고, 사용되는 IAM 역할이 관련 lakeFS 사용자에 연결되어야 해요.
-
lakeFS Java SDK는
software.amazon.awssdk:sts를 선택적(optional) 의존성으로 선언해요. 그래서 IAM 인증을 사용하는 애플리케이션은 명시적으로 추가해야 해요:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>sts</artifactId>
<version>2.33.12</version>
</dependency>
사용법
io.lakefs.clients.sdk.auth.iam 패키지는 ApiClient에 OkHttp 인터셉터를 설치해요. 이 인터셉터는 sts:GetCallerIdentity 요청을 presign하고, 로그인 API를 통해 lakeFS JWT로 교환하고, 토큰을 캐싱하고, 만료되기 전에 투명하게 갱신해요. AWS 자격 증명, 리전, STS 엔드포인트는 주변 환경(기본 자격 증명 프로바이더 체인, 기본 리전 프로바이더 체인)에서 해석돼요.
import io.lakefs.clients.sdk.ApiClient;
import io.lakefs.clients.sdk.RepositoriesApi;
import io.lakefs.clients.sdk.auth.iam.AWSIAMAuthProvider;
import io.lakefs.clients.sdk.auth.iam.IAMAuthParams;
ApiClient apiClient = new ApiClient();
apiClient.setBasePath("https://lakefs.example.com/api/v1");
IAMAuthParams params = IAMAuthParams.builder("lakefs.example.com").build();
AWSIAMAuthProvider.install(apiClient, params);
RepositoriesApi repos = new RepositoriesApi(apiClient);
IAMAuthParams.builder(...)에 전달한 lakefsHost 값은 서명된 GetCallerIdentity 요청에 X-LakeFS-Server-ID 헤더로 자동 추가되며, 서버의 기본 required_headers와 일치해요.
설정
IAMAuthParams.builder(...)는 Python SDK와 lakectl과 동일한 튜닝 가능 항목을 노출해요:
| 빌더 메서드 | 설명 | 기본값 |
|---|---|---|
| urlPresignTtl | lakeFS가 STS로 전달하기 전 presigned GetCallerIdentity URL의 유효 기간. | Duration.ofMinutes(1) |
| tokenTtl | 요청하는 lakeFS JWT의 수명. | Duration.ofHours(1) |
| refreshInterval | 토큰 만료 전 구간. 이 안에 가로챈 다음 요청이 동기 재로그인을 트리거해요. 반드시 tokenTtl보다 엄격히 작아야 해요. | Duration.ofMinutes(5) |
| tokenRequestHeaders | 서명된 STS 요청에 포함할 추가 헤더. X-LakeFS-Server-ID 헤더는 자동으로 추가돼요. | empty |
import java.time.Duration;
import java.util.Collections;
IAMAuthParams params = IAMAuthParams.builder("lakefs.example.com")
.urlPresignTtl(Duration.ofMinutes(2))
.tokenTtl(Duration.ofHours(2))
.refreshInterval(Duration.ofMinutes(10))
.tokenRequestHeaders(Collections.singletonMap("X-Custom-Header", "value"))
.build();
AWSIAMAuthProvider.install(apiClient, params);
Note
프로바이더는 토큰을 메모리에만 유지해요 - 디스크 캐시는 없어요. 오래 실행되는 JVM 프로세스에서는 대개 이것이 원하는 방식이에요. 프로세스가 자주 재시작되고 초기 로그인 왕복을 건너뛰고 싶다면 Persisting tokens across restarts를 참고하세요.
재시작 간 토큰 유지하기
AuthenticationToken은 두 필드를 가져요 - token(JWT)과 tokenExpiration(Unix epoch 초) - 그래서 원하는 방식으로 유지할 수 있어요. SDK에는 Gson이 포함돼 있어서 가장 간단한 방법은 모델을 직접 직렬화하는 거예요. install(...)이 반환한 프로바이더를 잡아두고, 종료 시 현재 토큰을 저장하고, 시작 시 initialToken(...)으로 복원(rehydrate)해요:
import com.google.gson.Gson;
import io.lakefs.clients.sdk.model.AuthenticationToken;
// On startup — rehydrate (if a cached token exists) and install.
AuthenticationToken restored = null;
if (Files.exists(cachePath)) {
restored = new Gson().fromJson(Files.readString(cachePath), AuthenticationToken.class);
}
AWSIAMAuthProvider provider = AWSIAMAuthProvider.builder()
.apiClient(apiClient)
.params(params)
.initialToken(restored) // null is fine — first call will log in
.install();
// ... use apiClient as normal ...
// On shutdown — save the current token.
AuthenticationToken current = provider.getCurrentToken();
if (current != null) {
Files.writeString(cachePath, new Gson().toJson(current));
}
프로바이더는 캐시된 토큰이 만료의 refreshInterval 범위 안에 들어올 때까지 그 토큰을 제공하고, 그다음 가로챈 요청이 동기 재로그인을 트리거해요. 여러분의 환경이 요구하는 보호 장치(파일 모드, OS 키체인, 시크릿 매니저)는 직접 적용하세요 - JWT는 bearer 자격 증명이에요.
더 알아보기 (Learn more)
공식 문서의 원문은 https://docs.lakefs.io/security/external-principals-aws/ 에서 확인할 수 있어요.