HiveServer2 가 LDAP 인증을 쓰는 환경에서 아래 오류가 나면 자격 증명이 LDAP 서버에서 거부된 것이다.
PLAIN auth failed: javax.security.sasl.AuthenticationException: Error validating LDAP user
[LDAP: error code 49 - 80090308: LdapErr: DSID-0C09050F, comment: AcceptSecurityContext error, data 52e, v4563]
error code 49 는 인증 실패 전체를 가리키고, 실제 이유는 Active Directory 가 붙여 주는 data 값에 있다.
| data | 뜻 |
|---|---|
| 525 | 해당 사용자가 없음 |
| 52e | 사용자는 있으나 비밀번호가 틀림 |
| 530 | 허용되지 않은 시간대의 로그인 |
| 531 | 허용되지 않은 워크스테이션 |
| 532 | 비밀번호 만료 |
| 533 | 계정 비활성 |
| 701 | 계정 만료 |
| 773 | 비밀번호 변경 필요 |
| 775 | 계정 잠김 |
52e 인데 비밀번호가 맞다고 확신한다면, 비밀번호가 서버까지 제대로 전달되지 않았을 가능성을 먼저 의심한다. 클라이언트가 자격 증명을 엉뚱한 파라미터 이름으로 넘기면 빈 값으로 bind 되어 같은 오류가 난다.
Apache Hive JDBC 드라이버는 user · password 라는 JDBC 속성을 쓴다. UID · PWD 는 Simba 계열 ODBC · JDBC 드라이버의 이름이라 Apache 드라이버에서는 무시된다. 아래처럼 dict 로 UID · PWD 를 넘기면 사용자명과 비밀번호가 전달되지 않는다.
# 전달되지 않는다
conn = jaydebeapi.connect(driver, url, {'UID': 'cdp_adm', 'PWD': '${PASSWORD}'}, jars=jar)
리스트로 넘기면 순서대로 사용자·비밀번호가 된다. 이 방식이 가장 확실하다.
import os
import jaydebeapi
jar_dir = '/tmp/hive-jdbc/'
jars = [jar_dir + f for f in os.listdir(jar_dir) if f.endswith('.jar')]
conn = jaydebeapi.connect(
'org.apache.hive.jdbc.HiveDriver',
'jdbc:hive2://hs2.example.com:10000/default',
['cdp_adm', os.environ['HIVE_PASSWORD']],
jars,
)
cur = conn.cursor()
cur.execute('SHOW DATABASES')
rows = cur.fetchall()
cols = [c[0] for c in cur.description]
cur.close()
conn.close()
Apache Hive JDBC URL 에는 auth=LDAP 같은 옵션이 없다. 인증 방식은 서버의 hive.server2.authentication 이 정하고, 클라이언트는 사용자·비밀번호만 넘긴다. URL 에서 의미 있는 값은 전송 방식과 TLS 쪽이다.
jdbc:hive2://hs2.example.com:10000/default
jdbc:hive2://hs2.example.com:10001/default;transportMode=http;httpPath=cliservice
jdbc:hive2://hs2.example.com:10001/default;transportMode=http;httpPath=cliservice;ssl=true;sslTrustStore=/path/truststore.jks
HTTP 모드(기본 10001)와 binary 모드(기본 10000)는 포트가 다르다. 포트와 transportMode 가 어긋나면 인증 이전 단계에서 끊긴다.
java.lang.NoClassDefFoundError: org/apache/hive/service/rpc/thrift/TCLIService$Iface
hive-jdbc-<버전>.jar 하나만 클래스패스에 넣어서 나는 오류다. 이 jar 는 Thrift · Hadoop 공통 라이브러리를 포함하지 않는다. 의존성을 모두 담은 hive-jdbc-<버전>-standalone.jar 를 쓰거나, Hive 클라이언트 lib 디렉터리의 jar 를 통째로 클래스패스에 넣는다.
os.environ['CLASSPATH'] = os.environ.get('CLASSPATH', '') + ':/tmp/hive-jdbc-standalone.jar'
os.environ["CLASSPATH"] 를 바로 읽으면 변수가 없을 때 KeyError 가 나므로 get 으로 기본값을 준다.
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder" 경고는 로깅 바인딩이 없다는 뜻일 뿐, 접속 실패와 무관하다.
서버 쪽 설정이 의심스러우면 LDAP 관련 값부터 본다.
<property><name>hive.server2.authentication</name><value>LDAP</value></property>
<property><name>hive.server2.authentication.ldap.url</name><value>ldaps://ad.example.com:636</value></property>
<property><name>hive.server2.authentication.ldap.baseDN</name><value>dc=example,dc=com</value></property>
Active Directory 라면 baseDN 대신 hive.server2.authentication.ldap.Domain 을 써서 user@domain 형태로 bind 하게 하는 구성이 흔하다. 어느 쪽이든 HiveServer2 로그에 실제 bind DN 이 남으므로, 클라이언트가 보낸 사용자명이 기대한 형태인지 그 로그로 확인하는 것이 가장 빠르다.