LLM WikiAccess-protected knowledge portal

WIKI

보안과 접근 제어: LDAP, OAuth2, 파인-그레인드 ACL

Trino 보안이 어려운 이유 Trino는 MySQL, Iceberg, Kafka, PostgreSQL 등 여러 데이터소스를 단일 SQL로 접근하는 쿼리 페더레이션 엔진이다. 데이터가 한 곳에 있지 않고 여러 시스템에 분산돼 있다는 것은 곧, 보안 제어를 어느 한 데이터베이스 레이어에서만 걸 수 없다는 뜻이다. Trino의 보안은 크게 두 가지 질문으로 나뉜다. 인증 Authentication 이 사용자가 정말 그 사람인가?

경로human/study/content/query-federation/04-trino-security-access-control.md
카테고리Study
태그#access #control #federation #infra #mysql #security #study #trino

Trino 보안이 어려운 이유

Trino는 MySQL, Iceberg, Kafka, PostgreSQL 등 여러 데이터소스를 단일 SQL로 접근하는 쿼리 페더레이션 엔진이다. 데이터가 한 곳에 있지 않고 여러 시스템에 분산돼 있다는 것은 곧, 보안 제어를 어느 한 데이터베이스 레이어에서만 걸 수 없다는 뜻이다.

Trino의 보안은 크게 두 가지 질문으로 나뉜다.

둘은 독립적으로 구성하지만, 인증이 먼저 통과되어야 인가가 의미를 갖는다. 그리고 둘 모두 TLS 위에서만 제대로 동작한다.


모든 보안의 전제 조건: TLS

Trino는 LDAP, OAuth2, PASSWORD 인증을 사용할 때 반드시 TLS(HTTPS)를 활성화해야 한다. 평문 HTTP 환경에서는 비밀번호와 토큰이 네트워크에 노출된다.

config.properties에 다음을 추가한다.

http-server.https.enabled=true
http-server.https.port=8443
http-server.https.keystore.path=/etc/trino/keystore.jks
http-server.https.keystore.key=keystore_password

클러스터 내부 워커-코디네이터 통신에도 공유 시크릿(internal communication secret)을 설정해야 한다.

internal-communication.shared-secret=<무작위 긴 문자열>

이 두 설정 없이 PASSWORD 계열 인증을 활성화하면 Trino가 시작을 거부한다.


인증: LDAP

LDAP 인증은 기업 내 Active Directory나 OpenLDAP 서버를 Trino의 사용자 검증 백엔드로 사용한다. 사용자가 CLI나 JDBC로 접속할 때 입력한 비밀번호를 LDAP 서버에 바인드 시도해 검증한다.

클라이언트 CLI / JDBC / BI HTTPS user / password 코디네이터 PasswordAuthenticator password-authenticator .properties → ldap TLS 종단 LDAP bind ldaps://636 LDAP 서버 AD / OpenLDAP 인증 성공/실패 세션 부여 또는 거부 TLS 없이 실행하면 Trino가 시작을 거부함 — ldaps:// 사용이 기본 권장. 그룹 필터링 시 bind-dn/bind-password가 추가로 필요. 여러 LDAP 서버를 나열하면 순서대로 시도하며 첫 번째 성공한 서버 결과를 사용.
LDAP 인증 흐름

설정 파일

etc/config.properties (코디네이터)

http-server.authentication.type=PASSWORD

etc/password-authenticator.properties

password-authenticator.name=ldap
ldap.url=ldaps://ldap.company.com:636
ldap.ssl.truststore.path=/etc/trino/ldap-server.pem
ldap.user-bind-pattern=uid=${USER},ou=users,dc=company,dc=com

${USER}는 로그인 시 입력한 사용자 이름으로 치환된다.

그룹 멤버십 제한

특정 그룹에 속한 사용자만 접속하게 하려면 서비스 계정으로 LDAP를 검색해야 한다.

ldap.bind-dn=cn=trino-svc,ou=service,dc=company,dc=com
ldap.bind-password=<서비스계정비밀번호>
ldap.user-base-dn=ou=users,dc=company,dc=com
ldap.group-auth-pattern=(&(objectClass=groupOfNames)(cn=trino-users)(member=uid=${USER},ou=users,dc=company,dc=com))

LDAP 검색 필터는 LDAP 서버 스키마에 맞춰야 한다. objectClass나 그룹 속성명이 서버마다 다르므로 ldapsearch로 먼저 확인하는 것이 좋다.


인증: OAuth2 / OIDC

OAuth2 인증은 외부 IdP(Identity Provider — Keycloak, Okta, Google, Azure AD 등)를 통해 사용자 인증을 위임한다. 브라우저 기반 Web UI 로그인에 특히 유용하며, PKCE(Proof Key for Code Exchange) 흐름을 통해 CLI/JDBC도 지원한다.

동작 흐름

브라우저
Web UI 요청
코디네이터
302 Redirect
IdP 로그인
Keycloak/Okta
인증 코드
/oauth2/callback
토큰 교환
Access + ID Token
코디네이터 config.properties
http-server.authentication.type
=oauth2
authentication.oauth2.issuer
=https://idp/realm/company
authentication.oauth2.client-id
=trino
authentication.oauth2.client-secret
=<secret>
OAuth2 OIDC 로그인 흐름

설정

etc/config.properties

http-server.authentication.type=oauth2
http-server.https.enabled=true
http-server.https.port=8443

http-server.authentication.oauth2.issuer=https://keycloak.company.com/realms/data
http-server.authentication.oauth2.client-id=trino
http-server.authentication.oauth2.client-secret=<클라이언트시크릿>

OIDC 호환 IdP는 <issuer>/.well-known/openid-configuration에 메타데이터를 게시한다. Trino는 이 엔드포인트를 자동으로 읽어 authorization_endpoint, token_endpoint, jwks_uri 등을 가져온다. 따라서 개별 URL을 다 입력할 필요가 없다.

리프레시 토큰 활성화

http-server.authentication.oauth2.refresh-tokens=true
http-server.authentication.oauth2.scopes=openid,offline_access

offline_access 스코프를 요청해야 IdP가 리프레시 토큰을 발급한다. 리프레시 토큰이 없으면 액세스 토큰 만료 시 재인증이 필요하다.

LDAP vs OAuth2 선택 기준

기준LDAPOAuth2/OIDC
기존 인프라Active Directory가 있는 경우SSO/IdP가 이미 있는 경우
브라우저 로그인지원 (기본 인증)지원 (리다이렉트 흐름)
MFA 지원어려움IdP에서 자연스럽게 지원
비밀번호 관리LDAP 서버IdP (Keycloak, Okta 등)
CLI 지원기본 지원PKCE 흐름 필요

인가: 파일 기반 접근 제어 (Fine-Grained ACL)

Trino는 인증된 사용자가 어떤 카탈로그, 스키마, 테이블, 컬럼에 접근할 수 있는지를 JSON 파일로 정의한다.

설정 활성화

etc/access-control.properties

access-control.name=file
security.config-file=/etc/trino/access-control/rules.json

rules.json 경로는 절대 경로 또는 코디네이터 기준 상대 경로다.

규칙 구조

{
  "catalogs": [
    {
      "user": "admin",
      "catalog": ".*",
      "allow": "all"
    },
    {
      "group": "data-engineers",
      "catalog": "iceberg",
      "allow": "read-only"
    },
    {
      "catalog": ".*",
      "allow": "none"
    }
  ],
  "schemas": [
    {
      "user": "analyst_.*",
      "catalog": "iceberg",
      "schema": "warehouse",
      "owner": false
    }
  ],
  "tables": [
    {
      "user": "reporting_user",
      "catalog": "iceberg",
      "schema": "warehouse",
      "table": "orders",
      "privileges": ["SELECT"]
    }
  ]
}

규칙은 위에서 아래로 첫 번째 매칭 규칙이 적용된다. 마지막에 "allow": "none" 같은 기본 거부 규칙을 두지 않으면 의도치 않게 모든 사용자가 접근할 수 있게 된다.

컬럼 마스킹과 행 필터링

테이블 규칙에서 특정 컬럼을 숨기거나 마스킹할 수 있다.

"tables": [
  {
    "user": "analyst",
    "catalog": "iceberg",
    "schema": "warehouse",
    "table": "customers",
    "privileges": ["SELECT"],
    "columns": [
      {
        "name": "email",
        "allow": false
      },
      {
        "name": "phone",
        "mask": "regexp_replace(phone, '\\d{4}$', '****')",
        "mask_environment": {
          "user": "data_masking_user"
        }
      }
    ],
    "filter": "region = 'KR'",
    "filter_environment": {
      "user": "row_filter_user"
    }
  }
]

mask는 SQL 표현식으로, 원본 컬럼값을 인자로 받아 결과를 반환한다. filter는 WHERE 조건처럼 동작해 조건에 맞는 행만 보인다.


인가: Open Policy Agent (OPA)

파일 기반 ACL은 규칙이 수백 개로 늘어나면 관리가 어렵다. OPA(Open Policy Agent)는 Rego 언어로 정책을 정의하고, HTTP API로 Trino에서 인가 결정을 위임받는다. 2024년 Trino 438부터 공식 내장됐다.

클라이언트
SQL 요청
Trino 코디네이터
OPA 플러그인
OPA 서버
http://opa:8181
Rego 정책
allow: true/false
OPA가 처리하는 인가 요청 예시
카탈로그 접근
허용/거부
테이블 SELECT
허용/거부
컬럼 마스킹
표현식 반환
행 필터
SQL WHERE 반환
Trino + OPA 인가 흐름

설정

etc/access-control.properties

access-control.name=opa
opa.policy.uri=http://opa-server:8181/v1/data/trino/allow

컬럼 마스킹과 행 필터링 엔드포인트를 따로 지정할 수도 있다.

opa.policy.row-filters-uri=http://opa-server:8181/v1/data/trino/rowFilters
opa.policy.column-masking-uri=http://opa-server:8181/v1/data/trino/columnMasks

간단한 Rego 정책 예시

package trino

import future.keywords.in

default allow = false

# admin 그룹은 모든 작업 허용
allow {
    input.context.identity.groups[_] == "admin"
}

# data-engineers는 iceberg 카탈로그 읽기 허용
allow {
    input.context.identity.groups[_] == "data-engineers"
    input.action.operation in {"SelectFromColumns", "FilterColumns"}
    input.action.resource.table.catalogName == "iceberg"
}

OPA는 Trino에서 보내는 JSON 입력(input)을 평가하고 allow: true/false를 반환한다.

파일 기반 ACL vs OPA 선택 기준

기준파일 기반 ACLOPA
규칙 수수십 개 이하수백~수천 개
동적 업데이트파일 교체 후 재로드API/Git 기반 정책 배포
기업 전체 정책 통합어려움여러 시스템에서 동일 OPA 재사용
감사(Audit) 로깅없음OPA Decision Log로 기록
복잡도낮음Rego 학습 필요

다중 인증 방식 조합

Trino는 여러 인증 방식을 동시에 활성화할 수 있다.

# LDAP과 PASSWORD FILE을 함께 사용
http-server.authentication.type=PASSWORD,OAUTH2

이 경우 브라우저 접속은 OAuth2 흐름을, CLI/JDBC는 PASSWORD(LDAP 또는 파일) 인증을 사용할 수 있다. 인증 방식은 요청의 Authorization 헤더 형식에 따라 자동으로 선택된다.


보안 설정 체크리스트

  1. TLS가 활성화됐는지, 유효한 인증서가 사용됐는지 확인한다.
  2. 내부 통신 시크릿(internal-communication.shared-secret)을 설정한다.
  3. 기본 접근 제어 규칙에 "allow": "none"(전체 거부) 폴백을 추가한다.
  4. 컬럼 마스킹과 행 필터 SQL 표현식을 최소 권한 사용자로 실행되도록 mask_environment를 설정한다.
  5. OPA 사용 시 OPA 서버 가용성 모니터링을 추가한다 — OPA가 응답하지 않으면 Trino 전체 쿼리가 차단된다.
  6. LDAP 비밀번호는 설정 파일에 평문으로 남지 않도록 환경 변수나 시크릿 매니저를 활용한다.
  7. 정기적으로 SHOW GRANTSSHOW COLUMNS FROM <table> 로 컬럼 접근 상태를 확인한다.

References