[Python] kerberos認証でHDFSへリクエストする


概要

pythonで、kerberos認証でhdfsに接続する方法についてです。

pythonのhdfsパッケージを利用してリクエストする方法と、
hdfsパッケージを使わずにrequestsパッケージでWebHDFS REST APIを叩く方法の2つを書きます。

  1. 事前準備(共通)
  2. hdfs + requests_kerberosを使う方法
  3. requestsパッケージでWebHDFS REST APIを叩く方法

以下の2つの環境で動作確認済です。

  • Python 2.7.5
  • Python 3.6.5

ケルベロス認証とは

手順に入る前に、ケルベロス認証について、以下のサイトがとても参考になるので貼っておきます。
http://www.infraexpert.com/study/security18.html

表をそのまま拝借しますが、以下の用語は覚えておいたほうがよいです。

ケルベロスの用語 説明
KDC(Key Distribution Center) サーバとユーザに関する信頼関係の情報を一括管理する中央データベース
AS(Authentication Server) 認証サーバ。ユーザからの認証を受け付けるサーバ
TGS(Ticket Granting Server) チケット発行サーバ。各サーバを利用するためのチケットを発行するサーバ
プリンシパル(principal) KDCが認証を行うユーザやサーバのこと
レルム(realm) 同じKDCの配下にあるシステムをグループとして定義する論理ネットワーク

事前準備(共通)

こちらは、どちらの方法でも必要になります。

以下のファイルが必要になります。

  • kerberos認証を利用するためのkeytabファイル
  • SSLのCA証明書が必要な場合はCA証明書(必要な場合のみ)

krb5のインストール

kerberosのインストールをします。

$ sudo yum install -y krb5-workstation
$ sudo yum install -y krb5-devel
$ sudo yum install -y krb5-libs

設定ファイル

/etc/krb5/krb5.confの設定を行います。

/etc/krb5.conf
[logging]
 # kdcのログ
 kdc = FILE:/var/log/krb5kdc.log
 # admin_serverのログ
 admin_server = FILE:/var/log/kadmind.log
 # その他のログ
 default = FILE:/var/log/krb5libs.log

# Kerberos 認証のデフォルト値を設定します。default_realm を設定する必要があります。
[libdefaults]
 default_realm = MYDOMAIN.COM
 dns_lookup_realm = false
 dns_lookup_kdc = false
 ticket_lifetime = 24h
 renew_lifetime = 7d
 forwardable = true

# 各 Kerberos レルムの KDC を設定します。1 つのレルムに複数の kdc を設定できます。
# デフォルトのポート 88 を使用する場合はポートの指定を省略できます。
# ドメイン名を大文字にしたものをレルム名にするのが慣例です。
[realms]
 MYDOMAIN.COM = {
  kdc = mykdc.mydomain.com
  # administrative server。通常はKerberosのマスターサーバー
  admin_server = mykdc.mydomain.com
  # プリンシパルとlocal user nameのマッピングルールを作れる
    auth_to_local = RULE:[1:$1@$0](.*@MYDOMAIN.COM)s/@.*//
 }

 # 複数ある場合は更に記述
 EXAMPLE.COM = {
  kdc = kerberos.example.com
  admin_server = kerberos.example.com
 }


# ドメインやホストをrealmと結びつける設定。
# Active Directory ドメインを Kerberos レルムにマップします。
[domain_realm]
 .example.com = MYDOMAIN.COM
 example.com = MYDOMAIN.COM

各項目の説明をだいたい書いてありますが、詳しくは以下のサイトを参考にしてください。

kinit(kerobers認証をする)

以下のコマンドを叩くと、kdc.conf ファイルに指定されている最長有効期限値 (max_life)の時間、kerberos認証されている状態が続きます。
定期的に実行するために、cronなどに設定しておくと便利です。

$ kinit -kt /secrets/keytab username

SSL CA証明書のパスを通す(必要な場合のみ)

接続にCA証明書が必要な場合は、以下の環境変数名でパスを設定しておく必要があります。

$ export REQUESTS_CA_BUNDLE=/etc/pki/tls/certs/ca-bundle.crt

これで事前準備は完了です。

hdfs + requests_kerberosを使う方法

hdfsパッケージのインストール

参考: http://hdfscli.readthedocs.io/en/latest/

hdfsパッケージ

$ pip install hdfs

kerberos認証に必要

$ pip install requests_kerberos

依存パッケージ

$ pip install requests

~/.hdfscli.cfgの設置

環境ごとに認証方式を変更したりできます。
以下の例では、prodではkerberos認証を使い、devでは使わない設定にしてあります。

参考: http://hdfscli.readthedocs.io/en/latest/api.html#module-hdfs.ext.kerberos

~/.hdfscli.cfg
# 全体的な設定
[global]
# defaultのalias
default.alias = dev
# kerberos moduleをautoloadする
autoload.modules = hdfs.ext.kerberos

# devの設定
[dev.alias]
# devのhdfsのurl
url = https://dev.namenode:port

# prodの設定
[prod.alias]
# prodのhdfsのurl
url = https://prod.namenode:port
# kerberos認証をする
client = KerberosClient
# pathを指定するときのTOP階層の定義
# この場合はhttps://prod.namenode:port/webhdfs/v1/がトップになる
root = /

サンプルコード

ディレクトリの中身を確認する処理を作ります。
以下のコードを実行することで、結果を得られます。

環境によってはSSLのSubjectAltNameWarningを表示され場合がありますが、一時的にwarningを非表示にするには、SSLのSubjectAltNameWarningを非表示にする場合は追記の部分をコメントインしてください。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import logging
import hdfs
from hdfs import Config

# SSLのSubjectAltNameWarningを非表示にする場合は追記
'''
import requests
from requests.packages.urllib3.exceptions import SubjectAltNameWarning
requests.packages.urllib3.disable_warnings(SubjectAltNameWarning)
'''

# logger
logger = logging.getLogger(__name__)
handler = logging.StreamHandler()
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)

try:
    '''
    https://prod.namenode:port/webhdfs/v1/ の下から定義する
    ~/.hdfscli.cfgのrootの定義によって変わる
    '''
    path = 'user/sample'

    '''
    ~/.hdfscli.cfgで定義した環境のdev, prodのどちらかを入力
    prodを指定した場合、これだけでkerberos認証になる
    '''
    client = Config().get_client('prod')
    result = client.list(path)
    logger.debug(result)

except hdfs.util.HdfsError as e:
    logger.error(str(e))

requestsパッケージでWebHDFS REST APIを叩く方法

こちらはhdfsパッケージは使わずに、requestsパッケージでWebHDFS REST APIを叩く方法です。

サンプルコード

以下もディレクトリの中身を確認する処理です。
こちらを実行することで結果が得られます。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import logging
import os
import requests
from requests_kerberos import HTTPKerberosAuth

# SSLのSubjectAltNameWarningを非表示にする場合は追記
'''
from requests.packages.urllib3.exceptions import SubjectAltNameWarning
requests.packages.urllib3.disable_warnings(SubjectAltNameWarning)
'''

# logger
logger = logging.getLogger(__name__)
handler = logging.StreamHandler()
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)


method = 'GET'
url = 'https://prod.namenode:port/webhdfs/v1/user/sample?op=liststatus'

session = requests.Session()
response = session.request(
    method=method,
    url=url,
    timeout=60,
    headers={'content-type': 'application/octet-stream'},
    # これが必要
    auth=HTTPKerberosAuth()
)

logger.debug(response.status_code)
logger.debug(response.text)

参考