Transparent Encryption in HDFS を試してみる

  • 2
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

HDFS上のデータを暗号化するTransparent Encryption in HDFSを試してみました。
環境は
* CentOS 7.2
* Oracle Java 1.8.0_65
* Apache Hadoop 2.7.2

Transparent Encryption in HDFS の説明はドキュメント参照です。

雑にまとめると、新しいコンポーネントKey Management Server(KMS)を追加する代わりに、暗号化キーの管理を透過的にやってくれます。

漂うSPOF感・・・

HDFSクラスタの初期化

以前書いた手順で実施。2.7.2でも同じです。

http://qiita.com/bwtakacy/items/6491b3d11754492f9702

KMSの設定

kms-site.xmlの以下のパラメータが最低限必要なようです。デフォルトで入っている値を今回は使います。

  <!-- KMS Backend KeyProvider -->

  <property>
    <name>hadoop.kms.key.provider.uri</name>
    <value>jceks://file@/${user.home}/kms.keystore</value>
    <description>
      URI of the backing KeyProvider for the KMS.
    </description>
  </property>

  <property>
    <name>hadoop.security.keystore.java-keystore-provider.password-file</name>
    <value>kms.keystore.password</value>
    <description>
      If using the JavaKeyStoreProvider, the password for the keystore file.
    </description>
  </property>

KeyProviderとしてJava付属のKeyStoreを利用する実装になっているようです。
KMS初回起動時に、指定したパスに指定したパスワードでKeyStoreが作成されます。

KMSの起動

$ sbin/kms.sh start

ハマりどころ1

ドキュメントを見ると、

The password file is looked up in the Hadoop’s configuration directory via the class path.

http://hadoop.apache.org/docs/r2.7.2/hadoop-kms/index.html

とあるので、Hadoopの設定ファイルが置いてある場所(この環境では$HADOOP_PREFIX/etc/hadoop)に置いていたのですが、パスワードファイルをKMSがうまく認識してくれず起動しませんでした。

いろいろ試行錯誤したのですが、上の説明をよく読むと分かるように、CLASSPATH経由で見えている必要があるようです。

KMSはTomcatにロードされて起動するのですが、その起動の際にTomcatのCLASSPATHにHadoopの設定ファイルのパスが渡されていないようです。

kms-site.xmlと同じところにあるkms-env.shに以下の1行を追加すると、うまく認識できるようになりました。

export CLASSPATH=$CLASSPATH:/usr/local/hadoop/etc/hadoop/kms.keystore.password

2016/2/7 11:20 追記
やり直してみたところ、上記でもダメでした。。。
どうやらTomcatのクラスパスが差しているところにパスワードファイルを置くくらいしか見出せませんでした。
場所は$HADOOP_PREFIX/share/hadoop/kms/tomcat/webapps/kms/WEB-INF/classes/です。

2016/2/7 11:40 追記
もう一つやり方を見つけました。
どうやら、keystoreパスワードはファイルに書いて読み込ませる他に、環境変数HADOOP_KEYSTORE_PASSWORDで与える方法もあるようです。この場合、kms-env.shに

export HADOOP_KEYSTORE_PASSWORD=password

とやってパスワードを与えればOKです。

Hadoop側でのKMS利用設定

hfs-site.xmlに以下を追記します。

<property>
  <name>dfs.encryption.key.provider.uri</name>
  <value>kms://http@localhost:16000/kms</value>
  <description>
    The KeyProvider to use when interacting with encryption keys used 
    when reading and writing to an encryption zone.
  </description>
</property>

ハマリどころ2

上記のパラメータだけだとEncryption Zoneを作成しようとした際に次のエラーが出ました。

There are no valid KeyProviders configured. No key
was created. You can use the -provider option to specify
a provider to use.

なん・・・だと・・・?

これまたネット上及びソースコードを調査しました。
何故かドキュメントに記載がなく、core-default.xmlにすら記載がないのですが、以下のパラメータをcore-site.xmlに設定する必要があるようです。

    <property>
        <name>hadoop.security.key.provider.path</name>
        <value>kms://http@localhost:16000/kms</value>
    </property>

設定する値はdfs.encryption.key.provider.uriと同じで良いみたいです。

HDFSクラスタの再起動

$ stop-dfs.sh
$ start-dfs.sh

使ってみる

Encryption keyの作成

$ hadoop key create mykey
mykey has been successfully created with options Options{cipher='AES/CTR/NoPadding', bitLength=128, description='null', attributes=null}.
KMSClientProvider[http://localhost:16000/kms/v1/] has been updated.
$ hadoop key list
Listing keys for KeyProvider: KMSClientProvider[http://localhost:16000/kms/v1/]
mykey

keytoolコマンドでも確認。

$ keytool -list -storetype jceks -keystore kms.keystore 
キーストアのパスワードを入力してください:  

キーストアのタイプ: JCEKS
キーストア・プロバイダ: SunJCE

キーストアには2エントリが含まれます

mykey@0,2016/04/14, SecretKeyEntry, 
mykey,2016/04/14, SecretKeyEntry, 

ハマリどころ3

ドキュメントに書いてあるコマンド例は失敗します。

$ hadoop key create myKey
java.lang.IllegalArgumentException: Uppercase key names are unsupported: myKey
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at org.apache.hadoop.util.HttpExceptionUtils.validateResponse(HttpExceptionUtils.java:157)
    at org.apache.hadoop.crypto.key.kms.KMSClientProvider.call(KMSClientProvider.java:546)
    at org.apache.hadoop.crypto.key.kms.KMSClientProvider.call(KMSClientProvider.java:504)
    at org.apache.hadoop.crypto.key.kms.KMSClientProvider.createKeyInternal(KMSClientProvider.java:677)
    at org.apache.hadoop.crypto.key.kms.KMSClientProvider.createKey(KMSClientProvider.java:685)
    at org.apache.hadoop.crypto.key.KeyShell$CreateCommand.execute(KeyShell.java:483)
    at org.apache.hadoop.crypto.key.KeyShell.run(KeyShell.java:79)
    at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70)
    at org.apache.hadoop.crypto.key.KeyShell.main(KeyShell.java:515)

ほんまか?工藤・・・

とにかく、key名は小文字のみにしましょう。

2016/2/10 追記
ドキュメント修正パッチがマージされました。
https://issues.apache.org/jira/browse/HDFS-9784

ハマりどころ4

keytoolコマンドで確認する場合、storetypeを指定する必要があるかどうか注意しましょう。
kms-site.xmlのデフォルトだとJCEKSになっているので、listする際に明示的に指定しないと以下のような意味不明のエラーが出ます。

$ keytool -list -keystore kms.keystore 
keytoolエラー: java.io.IOException: Invalid keystone format

Encryption Zoneの作成

$ hdfs dfs -mkdir /securezone
$ hdfs crypto -createZone -keyName mykey -path /securezone
Added encryption zone /securezone
$ hdfs crypto -listZones
/securezone  mykey 

データを置いてみる

$ hdfs dfs -put README.txt /securezone/
$ hdfs dfs -ls /securezone
Found 1 items
-rw-r--r--   1 hadoop supergroup       1366 2016-02-07 14:31 /securezone/README.txt

通常のHDFSアクセスと方法は何も変わりません。

中身が暗号化されているか確認

実際のブロック内容を覗いてみます。
その前に、比較のためにEncryption Zoneの外に同じファイルを置いておきます。

$ hdfs dfs -put README.txt /
$ hdfs dfs -ls -R /
-rw-r--r--   1 hadoop supergroup       1366 2016-02-07 14:32 /README.txt
drwxr-xr-x   - hadoop supergroup          0 2016-02-07 14:31 /securezone
-rw-r--r--   1 hadoop supergroup       1366 2016-02-07 14:31 /securezone/README.txt

DataNodeのDFSディレクトリを下ってブロックファイルを探します。

$ pwd
/tmp/hadoop-hadoop/dfs/data/current/BP-1497679019-127.0.0.1-1454819646630/current/finalized/subdir0/subdir0
$ ll
合計 16
-rw-rw-r--. 1 hadoop hadoop 1366  2月  7 14:31 blk_1073741825
-rw-rw-r--. 1 hadoop hadoop   19  2月  7 14:31 blk_1073741825_1001.meta
-rw-rw-r--. 1 hadoop hadoop 1366  2月  7 14:32 blk_1073741826
-rw-rw-r--. 1 hadoop hadoop   19  2月  7 14:32 lk_1073741826_1002.meta

ファイルの中を覗いてみると、、、

$ cat blk_1073741825
??e7]߭?
??z
jtח͸??????>zP!X)?ň?W?0 ?]sqj?IBD????4????_?r?W?B1? ?#Y$+??/by?c?N??ҍ????-? ??DmE?8}yJA??8t??cyx??&??CDnYߤ?1???? ?KO?W?????
??ߤ?p
?^|x?R=_?w??D?? LΧ?mE?s?͞s??})Agn??wc????????9R:(?[x??    ???T??E?S?1??^?*R??
??I5??r??&??.?yQHz;,?a????s??Z??0?qHErѾ??#??cT7&撳&HI3???@????1M??IL?????8???:dV}?&bP???i???Ѷ/?U?m??5?w?d?`mK??O9
?[??Ǵ??F];=ӿ%Q??Uc?k?z'????~???8???V%???O%a?
                                            ??}????P?z֙
?bE?j????Dso??h)ժɠ??<ܻ???d?.G????zۺx?}e?a?U刳M?
                                              ??09E?,??Vz?h?    ֥????"iAf?J#{d??????4?PF?W?g\??`??L?????O???q???͟J???'?B?P??Iy?Fd??????   (S??!S????\P?{??7=???p?f?Gu>J??8??_2??5X??Ń?)??_!??
8J?%,??n%<WT??w?'??[<9Fe?Yq????
?8y???
Dd????v?]d@/?
LJ)Lݕ??Ѻ????1J‚>OM&?g2?-??A6?-?t??2???~sҩ9???C
Ǥ??*?)Y?4kj?}ǧ?\?G???Be
??n:k1?%/=m???v?????????    ???yf"?,????W   3????y?C߾??iӦW+?ʍ?????/BP[t??46o{8??t??n
                                                                                        ?~??"?o|ؓF?+??g??U???]Zq?? ??
Vnl?Xk?u?#R8?^?NI?(Q?v?&ț?;?َ??vl??
                     oF?V??,C?֚?E}5?q?w?i?i]+?Lg?Z?&?D?????E
                                                           ???GVx?L?W????(?ܰ?!?<U???%<86U(?w???D?E\???ESVI??hJ?1j??H?M?U;?? .J?Ce>?[3?????ɐi????<?Aa?s??????%X??9??"??*???ï??&?$ ??I
                 ??bx~?Mtr,?(????????d?6??????<?>m?M*

こっちは暗号化された方みたいですね。いい感じのバイナリデータです。

$ cat blk_1073741826
For the latest information about Hadoop, please visit our website at:

   http://hadoop.apache.org/core/

and our wiki, at:

   http://wiki.apache.org/hadoop/

This distribution includes cryptographic software.  The country in 
which you currently reside may have restrictions on the import, 
possession, use, and/or re-export to another country, of 
encryption software.  BEFORE using any encryption software, please 
check your country's laws, regulations and policies concerning the
import, possession, or use, and re-export of encryption software, to 
see if this is permitted.  See <http://www.wassenaar.org/> for more
information.

The U.S. Government Department of Commerce, Bureau of Industry and
Security (BIS), has classified this software as Export Commodity 
Control Number (ECCN) 5D002.C.1, which includes information security
software using or performing cryptographic functions with asymmetric
algorithms.  The form and manner of this Apache Software Foundation
distribution makes it eligible for export under the License Exception
ENC Technology Software Unrestricted (TSU) exception (see the BIS 
Export Administration Regulations, Section 740.13) for both object 
code and source code.

The following provides more details on the included cryptographic
software:
  Hadoop Core uses the SSL libraries from the Jetty project written 
by mortbay.org.

もう一方はテキストファイルのままでした。こっちはEncryption Zoneの外に置いた方ですね。

HDFSクライアントから暗号化を意識せずにアクセスできるか確認

$ hdfs dfs -tail /securezone/README.txt
try, of 
encryption software.  BEFORE using any encryption software, please 
check your country's laws, regulations and policies concerning the
import, possession, or use, and re-export of encryption software, to 
see if this is permitted.  See <http://www.wassenaar.org/> for more
information.

The U.S. Government Department of Commerce, Bureau of Industry and
Security (BIS), has classified this software as Export Commodity 
Control Number (ECCN) 5D002.C.1, which includes information security
software using or performing cryptographic functions with asymmetric
algorithms.  The form and manner of this Apache Software Foundation
distribution makes it eligible for export under the License Exception
ENC Technology Software Unrestricted (TSU) exception (see the BIS 
Export Administration Regulations, Section 740.13) for both object 
code and source code.

The following provides more details on the included cryptographic
software:
  Hadoop Core uses the SSL libraries from the Jetty project written 
by mortbay.org.

大丈夫でした。

MapReduceから暗号化を意識せずにアクセスできるか確認

HadoopといえばWordCount

$ hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.2.jar wordcount /securezone/README.txt /out2
$ hdfs dfs -ls -R /out2
-rw-r--r--   1 hadoop supergroup          0 2016-02-07 14:42 /out2/_SUCCESS
-rw-r--r--   1 hadoop supergroup       1306 2016-02-07 14:42 /out2/part-r-00000
$ hdfs dfs -cat /out2/part-r-00000
(BIS),  1
(ECCN)  1
(TSU)   1
(see    1
5D002.C.1,  1
740.13) 1
<http://www.wassenaar.org/> 1
Administration  1
Apache  1
BEFORE  1
BIS 1
Bureau  1
Commerce,   1
Commodity   1
Control 1
Core    1
Department  1
ENC 1
Exception   1
Export  2
For 1
Foundation  1
Government  1
Hadoop  1
Hadoop, 1
Industry    1
Jetty   1
License 1
Number  1
Regulations,    1
SSL 1
Section 1
Security    1
See 1
Software    2
Technology  1
The 4
This    1
U.S.    1
Unrestricted    1
about   1
algorithms. 1
and 6
and/or  1
another 1
any 1
as  1
asymmetric  1
at: 2
both    1
by  1
check   1
classified  1
code    1
code.   1
concerning  1
country 1
country's   1
country,    1
cryptographic   3
currently   1
details 1
distribution    2
eligible    1
encryption  3
exception   1
export  1
following   1
for 3
form    1
from    1
functions   1
has 1
have    1
http://hadoop.apache.org/core/  1
http://wiki.apache.org/hadoop/  1
if  1
import, 2
in  1
included    1
includes    2
information 2
information.    1
is  1
it  1
latest  1
laws,   1
libraries   1
makes   1
manner  1
may 1
more    2
mortbay.org.    1
object  1
of  5
on  2
or  2
our 2
performing  1
permitted.  1
please  2
policies    1
possession, 2
project 1
provides    1
re-export   2
regulations 1
reside  1
restrictions    1
security    1
see 1
software    2
software,   2
software.   2
software:   1
source  1
the 8
this    3
to  2
under   1
use,    2
uses    1
using   2
visit   1
website 1
which   2
wiki,   1
with    1
written 1
you 1
your    1

大丈夫そうですね。
念のためEncryption Zoneの外に置いた同じファイルに対する結果をdiffしても同じ結果になっていました。

以上!

おまけ ~KMSが死ぬ時どうなるの~

もしもHDFSクラスタが動作していてもKMSが死んだら?

$ hdfs dfs -cat /securezone/README.txt
cat: 接続を拒否されました
$ hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.0.0-SNAPSHOT.jar wordcount /securezone/README.txt /out
16/02/07 23:55:57 INFO Configuration.deprecation: session.id is deprecated. Instead, use dfs.metrics.session-id
16/02/07 23:55:57 INFO jvm.JvmMetrics: Initializing JVM Metrics with processName=JobTracker, sessionId=
16/02/07 23:55:58 INFO input.FileInputFormat: Total input files to process : 1
16/02/07 23:55:58 INFO mapreduce.JobSubmitter: number of splits:1
16/02/07 23:55:58 INFO mapreduce.JobSubmitter: Submitting tokens for job: job_local471899076_0001
16/02/07 23:55:58 INFO mapreduce.Job: The url to track the job: http://localhost:8080/
16/02/07 23:55:58 INFO mapreduce.Job: Running job: job_local471899076_0001
16/02/07 23:55:58 INFO mapred.LocalJobRunner: OutputCommitter set in config null
16/02/07 23:55:58 INFO output.FileOutputCommitter: File Output Committer Algorithm version is 2
16/02/07 23:55:58 INFO output.FileOutputCommitter: FileOutputCommitter skip cleanup _temporary folders under output directory:false, ignore cleanup failures: false
16/02/07 23:55:58 INFO mapred.LocalJobRunner: OutputCommitter is org.apache.hadoop.mapreduce.lib.output.FileOutputCommitter
16/02/07 23:55:58 INFO mapred.LocalJobRunner: Waiting for map tasks
16/02/07 23:55:58 INFO mapred.LocalJobRunner: Starting task: attempt_local471899076_0001_m_000000_0
16/02/07 23:55:58 INFO output.FileOutputCommitter: File Output Committer Algorithm version is 2
16/02/07 23:55:58 INFO output.FileOutputCommitter: FileOutputCommitter skip cleanup _temporary folders under output directory:false, ignore cleanup failures: false
16/02/07 23:55:58 INFO mapred.Task:  Using ResourceCalculatorProcessTree : [ ]
16/02/07 23:55:58 INFO mapred.MapTask: Processing split: hdfs://localhost:9000/securezone/README.txt:0+1366
16/02/07 23:55:58 INFO mapred.MapTask: (EQUATOR) 0 kvi 26214396(104857584)
16/02/07 23:55:58 INFO mapred.MapTask: mapreduce.task.io.sort.mb: 100
16/02/07 23:55:58 INFO mapred.MapTask: soft limit at 83886080
16/02/07 23:55:58 INFO mapred.MapTask: bufstart = 0; bufvoid = 104857600
16/02/07 23:55:58 INFO mapred.MapTask: kvstart = 26214396; length = 6553600
16/02/07 23:55:58 INFO mapred.MapTask: Map output collector class = org.apache.hadoop.mapred.MapTask$MapOutputBuffer
16/02/07 23:55:58 INFO mapred.MapTask: Starting flush of map output
16/02/07 23:55:58 INFO mapred.LocalJobRunner: map task executor complete.
16/02/07 23:55:58 WARN mapred.LocalJobRunner: job_local471899076_0001
java.lang.Exception: java.net.ConnectException: 接続を拒否されました
    at org.apache.hadoop.mapred.LocalJobRunner$Job.runTasks(LocalJobRunner.java:490)
    at org.apache.hadoop.mapred.LocalJobRunner$Job.run(LocalJobRunner.java:550)
Caused by: java.net.ConnectException: 接続を拒否されました
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at sun.net.NetworkClient.doConnect(NetworkClient.java:175)
    at sun.net.www.http.HttpClient.openServer(HttpClient.java:432)
    at sun.net.www.http.HttpClient.openServer(HttpClient.java:527)
    at sun.net.www.http.HttpClient.<init>(HttpClient.java:211)
    at sun.net.www.http.HttpClient.New(HttpClient.java:308)
    at sun.net.www.http.HttpClient.New(HttpClient.java:326)
    at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1169)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1105)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:999)
    at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:933)
    at org.apache.hadoop.security.authentication.client.KerberosAuthenticator.authenticate(KerberosAuthenticator.java:190)
    at org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticator.authenticate(DelegationTokenAuthenticator.java:128)
    at org.apache.hadoop.security.authentication.client.AuthenticatedURL.openConnection(AuthenticatedURL.java:215)
    at org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticatedURL.openConnection(DelegationTokenAuthenticatedURL.java:322)
    at org.apache.hadoop.crypto.key.kms.KMSClientProvider$1.run(KMSClientProvider.java:486)
    at org.apache.hadoop.crypto.key.kms.KMSClientProvider$1.run(KMSClientProvider.java:481)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAs(Subject.java:422)
    at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1729)
    at org.apache.hadoop.crypto.key.kms.KMSClientProvider.createConnection(KMSClientProvider.java:481)
    at org.apache.hadoop.crypto.key.kms.KMSClientProvider.decryptEncryptedKey(KMSClientProvider.java:774)
    at org.apache.hadoop.crypto.key.KeyProviderCryptoExtension.decryptEncryptedKey(KeyProviderCryptoExtension.java:388)
    at org.apache.hadoop.hdfs.DFSClient.decryptEncryptedDataEncryptionKey(DFSClient.java:901)
    at org.apache.hadoop.hdfs.DFSClient.createWrappedInputStream(DFSClient.java:969)
    at org.apache.hadoop.hdfs.DistributedFileSystem$3.doCall(DistributedFileSystem.java:273)
    at org.apache.hadoop.hdfs.DistributedFileSystem$3.doCall(DistributedFileSystem.java:268)
    at org.apache.hadoop.fs.FileSystemLinkResolver.resolve(FileSystemLinkResolver.java:81)
    at org.apache.hadoop.hdfs.DistributedFileSystem.open(DistributedFileSystem.java:280)
    at org.apache.hadoop.fs.FileSystem.open(FileSystem.java:777)
    at org.apache.hadoop.mapreduce.lib.input.LineRecordReader.initialize(LineRecordReader.java:85)
    at org.apache.hadoop.mapred.MapTask$NewTrackingRecordReader.initialize(MapTask.java:548)
    at org.apache.hadoop.mapred.MapTask.runNewMapper(MapTask.java:786)
    at org.apache.hadoop.mapred.MapTask.run(MapTask.java:341)
    at org.apache.hadoop.mapred.LocalJobRunner$Job$MapTaskRunnable.run(LocalJobRunner.java:271)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
16/02/07 23:55:59 INFO mapreduce.Job: Job job_local471899076_0001 running in uber mode : false
16/02/07 23:55:59 INFO mapreduce.Job:  map 0% reduce 0%
16/02/07 23:55:59 INFO mapreduce.Job: Job job_local471899076_0001 failed with state FAILED due to: NA
16/02/07 23:55:59 INFO mapreduce.Job: Counters: 0

アカンやつや・・・