フィードバック |
前のチュートリアル、「JAAS Login ユーティリティおよび Java GSS-API を使用した安全なメッセージ交換」では、2 つのアプリケーション (特にクライアントとサーバ) が Java GSS-API を使用して相互間のセキュリティ保護されたコンテキストを確立して、メッセージを安全に交換する方法を示しました。
コンテキストイニシエータ (このクライアント/サーバの例ではクライアント) を使用してコンテキストを確立した後で、コンテキストアクセプタ (サーバ) が実行可能な操作は他にもあります。基本的に、サーバはクライアントを「装う」ことができます。偽装のレベルは、クライアントがクレデンシャルをサーバに委譲しているかどうかにより異なります。
サーバがクライアントを装う 1 つの方法は、クライアントコードを実行するのと同じエンティティ (ユーザ) でコードを実行することです。通常、スレッドにより実行されるメソッドは、そのスレッド自体のアクセス制御設定を使用します。ただし、このチュートリアルでは、サーバがクライアントを装う際、クライアントのアクセス制御設定を使用するため、サーバはクライアント自体が実行時に保持する、厳密に同じリソースにアクセスできます。
このチュートリアルで使用する方法の主要な利点は、JAAS 承認コンポーネントをアクセス制御に使用できることです。JAAS 承認コンポーネントがない場合、サーバプリンシパルは、クライアントユーザで実行されるコードがアクセスするすべてのリソースにアクセスできなければなりません。また、サーバコードに、ユーザがそのリソースにアクセスすることが承認されているかどうかを判別するためのアクセス制御ロジックを含める必要があります。JAAS 承認を利用することにより、プリンシパルベースのアクセス制御が提供されるため、アクセス制御が自動的に処理されます。これらのコード内のセキュリティ関連操作のアクセス権は、ユーザにのみ付与する必要があり、サーバコードに付与する必要はありません。JAAS 承認の詳細は、「JAAS 承認」チュートリアルを参照してください。
基本的なアプローチ
サーバは、どのようにしてクライアントを「装い」、クライアントコードを実行するユーザでコードを実行できるのでしょうか。基本的に、そのユーザでクライアントコードを実行する場合と同じ方法が使用されます。すべてのサーバコードは、ユーザのプリンシパル名を認識する必要があります。ユーザのプリンシパル名は、クライアントで確立されたコンテキストから取得できます。
クライアントコードを実行するユーザの JAAS 認証により、ユーザ (プリンシパル) 名を保持するプリンシパルを含むサブジェクトが作成されることはすでに学びました。その後、プリンシパルは (Login ユーティリティからの
Subject.doAsPrivileged
呼び出しを介して) 新しいアクセス制御コンテキストに関連付けられ、クライアントコードがそのユーザで実行されるものと見なされます。以降のアクセス制御は、必要なアクセス権がクライアントコードを実行する特定のユーザに付与されるかどうかに基づいて決定されます。サーバコードも同様に処理されます。ただし、一般に、認証の指定されたプリンシパルはユーザプリンシパルではなく「サービスプリンシパル」である点が異なります。再度、指定されたプリンシパル名のプリンシパルを含むサブジェクトが作成され、
Subject.doAsPrivileged
が呼び出されます。サーバコードは指定されたプリンシパルで実行されると見なされます。以降のアクセス制御は、必要なアクセス権がサーバコードを実行する特定のプリンシパルに付与されるかどうかに基づいて決定されます。いったんクライアントおよびサーバが相互コンテキストを確立すると、次のコードでコンテキストイニシエータの名前 (クライアントのプリンシパル名) を確認できます。
GSSName clientGSSName = context.getSrcName();コンテキストアクセプタ (サーバ) は、この名前を使用して、同じエンティティを表すプリンシパルを含むサブジェクトを生成できます。たとえば、サンの提供する JRE を使用している場合、次の方法でサブジェクトを生成できます。
Subject client = com.sun.security.jgss.GSSUtil.createSubject(clientGSSName, null);createSubject メソッドは、引数として指定された GSSName および GSSCredential から新たなサブジェクトを作成します。サーバコードがローカル JVM 内のユーザでコードを実行する場合、ユーザのクレデンシャルは必要ではありません。実際のところ、クライアントがサーバにクレデンシャルを委譲しているのでない限り、ユーザのクレデンシャルは取得できません。詳細は、「クライアントから委譲されたクレデンシャルの使用」を参照してください。ここではクレデンシャルは必要ではないため、GSSCredential 引数に
null
を渡します。
注:Sun の提供する JRE を使用しているのではない場合、これを実行する別の方法は、次のようにして KerberosPrincipal インスタンスを生成することです。KerberosPrincipal principal = new KerberosPrincipal(clientGSSName.toString());次に、このプリンシパルを使用して新たなサブジェクトを生成するか、既存のサブジェクトのプリンシパルセット内でこのプリンシパルを生成します。
サーバがユーザとして実行するコードは、
java.security.PrivilegedAction
(またはjava.security.PrivilegedExceptionAction
) を実装するrun
メソッドで開始する必要があります。つまり、コードをこのrun
メソッド内に配置することも、run
メソッドから呼び出すこともできます。サーバコードは、PrivilegedAction (または PrivilegedExceptionAction) のインスタンスと共にサブジェクトを
Subject.doAsPrivileged
に渡し、以降のコードを PrivilegedAction のrun
メソッドから、指定されたサブジェクトのプリンシパル (ユーザ) で開始できます。たとえば、PrivilegedAction クラスが ReadFileAction を呼び出し、引数としてプリンシパル名を保持する String を取る場合を考えましょう。このインスタンスは、次のコードで作成できます。
String clientName = clientGSSName.toString(); PrivilegedAction readFile = new ReadFileAction(clientName);
doAsPrivileged
の呼び出しは、次のようになります。Subject.doAsPrivileged(client, readFile, null);サンプルコードおよびポリシーファイル
次のサンプルコードおよびポリシーファイルは、クライアントを実行する特定のユーザのみに許可されるセキュリティ関連操作用のコードを実行するために、サーバがクライアントを装う方法を示します。
SampleServerImp.java
SampleServerImp.java ファイルは、クライアントとメッセージを交換したあと、クライアントユーザとして
ReadFileAction
を実行するための以下のようなコードが生成される点を除けば、前のチュートリアル (「JAAS Login ユーティリティおよび Java GSS-API を使用した安全なメッセージ交換」) で紹介した SampleServer.java ファイルとまったく同じです。System.out.println("Impersonating client."); /* * Extract the KerberosPrincipal from the client GSSName and * populate it in the principal set of a new Subject. Pass in a * null for credentials since credentials will not be needed. */ GSSName clientGSSName = context.getSrcName(); System.out.println("clientGSSName: " + clientGSSName); Subject client = com.sun.security.jgss.GSSUtil.createSubject(clientGSSName, null); /* * Construct an action that will read a file meant only for the * client */ String clientName = clientGSSName.toString(); PrivilegedAction readFile = new ReadFileAction(clientName); /* * Invoke the action via a doAsPrivileged. This allows the * action to be executed as the client subject, and it also * runs that code as privileged. This means that any permission * checking that happens beyond this point applies only to * the code being run as the client. */ Subject.doAsPrivileged(client, readFile, null);ReadFileAction.java
ReadFileAction.java ファイルには、ReadFileAction クラスが含まれます。このコンストラクタは、クライアントユーザ名の String を引数に取ります。クライアントユーザ名は、ReadFileAction が読み取りを試みるファイルのファイル名の作成に使用されます。ファイル名は、次のようになります。
ここで、<name> は対応する領域を含まないクライアントユーザ名になります。たとえば、ユーザ名全体が./data/<name>_info.txtmjones@KRBNT-OPERATIONS.ABC.COM
の場合、ファイル名は次のようになります。注:Win32 システムの場合、スラッシュをバックスラッシュで置き換えてください。./data/mjones_info.txtReadFileAction の
run
メソッドは、指定されたファイルを読み取り、その内容を出力します。serverimp.policy
ReadFileAction はファイルを読み取ろうとします。この操作はセキュリティチェックの対象になります。 ReadFileAction はクライアントユーザ (プリンシパル) として実行されることになっているので、ReadFileAction コード自体とクライアントであるプリンシパルに対して適切なアクセス権を付与する必要があります。
ReadFileAction
クラスがReadFileAction.jar
という名前の JAR ファイル内に配置され、ユーザプリンシパル名がmjones@KRBNT-OPERATIONS.ABC.COM
である場合を考えましょう。この場合、ポリシーファイル内に次のコードを記述して、アクセス権を付与します。serverimp.policy ファイルは、SampleServer コードにgrant CodeBase "file:./ReadFileAction.jar" Principal javax.security.auth.kerberos.KerberosPrincipal "mjones@KRBNT-OPERATIONS.ABC.COM" { permission java.io.FilePermission "data/mjones_info.txt", "read"; };doAsPrivileged
メソッドの呼び出しに必要なアクセス権javax.security.auth.AuthPermission "doAsPrivileged"
を付与すること、および上に示した FilePermission を付与するプレースホルダを保持することを除き、前の (「JAAS Login ユーティリティおよび Java GSS-API を使用した安全なメッセージ交換」) チュートリアルのserver.policy
ファイルと厳密に同一です。以下に、FilePermission を付与するプレースホルダを示します。grant CodeBase "file:./ReadFileAction.jar" Principal javax.security.auth.kerberos.KerberosPrincipal "your_user_name@your_realm" { permission java.io.FilePermission "data/your_user_name_info.txt", "read"; };ここで、
your_realm
は使用する Kerberos 領域で、your_user_name@your_realm
とdata/your_user_name_info.txt
内のyour_user_name
は使用するユーザ名で置き換える必要があります。Win32 システムの場合、data/your_user_name_info.txt
内の「/」を「¥」で置き換えてください。サンプルコードの実行
クライアントを装うサーバのサンプルコードを実行する場合、前のチュートリアルの「SampleClient および SampleServer プログラムの実行」に説明されているのと同じ操作を実行してください。ただし、以下の点が異なります。
- 「SampleServer の実行準備」のステップ
SampleServer.java
の代わりに SampleServerImp.java を使用します。次のコマンドを実行してコンパイルし、SampleServerImp.class
を含むSampleServerImp.jar
という名前の JAR ファイルを作成します。javac SampleServerImp.java jar -cvf SampleServerImp.jar SampleServerImp.class
- ポリシーファイル
server.policy
の代わりに、serverimp.policy を使用します。
- ログイン構成ファイル
cs.conf
の代わりに、csImpLogin.conf を使用します。
- ReadFileAction.java を、他のファイルと同じディレクトリにコピーします。次のコマンドを実行してコンパイルし、JAR ファイル内に格納します。
javac ReadFileAction.java jar -cvf ReadFileAction.jar ReadFileAction.classcsImpLogin.conf
内の "service_principal@your_realm" を、SampleServer を表すサービスプリンシパルの Kerberos 名で置き換えます。
serverimp.policy
内の 2 か所に表示された "service_principal@your_realm" を、SampleServer を表すサービスプリンシパルの Kerberos 名で置き換えます。これは、ログイン構成ファイルで使用する名前と同じ名前です。また、your_realm
は使用する Kerberos 領域で、your_user_name@your_realm
とdata/your_user_name_info.txt
内のyour_user_name
は使用するユーザ名で置き換える必要があります。Win32 システムの場合、data/your_user_name_info.txt
内の「/」を「¥」で置き換えてください。
- 現在のディレクトリ内に
data
サブディレクトリを作成し、そのディレクトリ内に指定された名前の小さいテキストファイルを作成します。たとえば、ユーザ名がmjones
の場合、data サブディレクトリ内にmjones_info.txt
という名前のファイルを配置します。
- 「SampleServer の実行」ステップ
- このセクション内で指定されたコマンドの代わりに、次のコマンドを使用します。このコマンドにより、
SampleServerImp
の実行、serverimp.policy
およびcsImpLogin.conf
の使用、ReadFileAction.jar
の組み込みを行うことができます。重要:これらのコマンドの、
<port_number>
を適切なポート番号 (4444 などの大きなポート番号) に、<your_realm>
を使用する Kerberos 領域に、<your_kdc>
を使用する Kerberos KDC にそれぞれ置き換えてください。以下に、Win32 システムのコマンドを示します。
java -classpath Login.jar;SampleServerImp.jar;ReadFileAction.jar -Djava.security.manager -Djava.security.krb5.realm=<your_realm> -Djava.security.krb5.kdc=<your_kdc> -Djava.security.policy=serverimp.policy -Djava.security.auth.login.config=csImpLogin.conf Login SampleServerImp <port_number>以下に、UNIX システムのコマンドを示します。
java -classpath Login.jar:SampleServerImp.jar:ReadFileAction.jar -Djava.security.manager -Djava.security.krb5.realm=<your_realm> -Djava.security.krb5.kdc=<your_kdc> -Djava.security.policy=serverimp.policy -Djava.security.auth.login.config=csImpLogin.conf Login SampleServerImp <port_number>通常通り、コマンド全体は 1 行で入力してください。ここでは、読みやすくするために複数行に分けて表示してあります。コマンドが長すぎる場合は、.bat ファイル (Win32) または .sh ファイル (UNIX) に記述します。このファイルを実行することで、コマンドを実行できます。
SampleServer の実行時に、SampleServerImp の実行が予期されるサービスプリンシパルの Kerberos パスワードの入力が求められます。ログイン構成ファイルで指定された Kerberos ログインモジュールにより、サービスプリンシパルの Kerberos へのログインが行われます。認証が成功すると、
SampleServerImp
のコードがサービスプリンシパルとして実行されます。このコードは、指定されたポート上でソケット接続を待機します。通常通り、「SampleClient の実行準備」および「SampleClient の実行」の指示に従って操作を実行すると、クライアントコードにより SampleServerImp とのソケット接続が要求されます。SampleServerImp が接続を受け付けると、SampleClient および SampleServerImp により、前のチュートリアルで解説した方法で、共有コンテキストの確立およびメッセージの交換が行われます。
メッセージの交換後に、SampleServerImp はクライアントコードを実行するユーザのプリンシパル名を判別して、その名前のプリンシパルを含むサブジェクトを新たに作成します。また、
Subject.doAsPrivileged
を呼び出して、指定されたユーザで ReadFileAction 内のコードを実行します。ReadFileAction は、現行ディレクトリのdata
サブディレクトリ内のyour_user_name_info.txt
(your_user_name
は実際のユーザ名) という名前のファイルを読み取り、その内容を出力します。ログイン時のトラブルシューティングについては、「トラブルシューティング」を参照してください。
クライアントがクレデンシャルをサーバに委譲する場合、完成度の最も高い方法でクライアントを装うことができます。
コンテキストアクセプタ (前のチュートリアルのサーバ) とのコンテキストを確立する前に、コンテキストイニシエータ (クライアント) によりさまざまなコンテキストオプションの設定が行われたことを思い起こしてください。次に示すように、イニシエータが
context
オブジェクトに対し、引数true
を指定してrequestCredDeleg
メソッドを呼び出す場合を考えましょう。この場合、コンテキスト確立時に、イニシエータのクレデンシャルをアクセプタに委譲することが求められます。context.requestCredDeleg(true);イニシエータからアクセプタにクレデンシャルを委譲することにより、アクセプタが自らをイニシエータのエージェントまたは代理人として認証することが可能になります。
コンテキスト確立後に、アクセプタはクレデンシャルの委譲が実際に行われたかどうかを最初に確認する必要があります。これは、
getCredDelegState
メソッドを呼び出すことで実行されます。boolean delegated = context.getCredDelegState();クレデンシャルが委譲されている場合、アクセプタは
getDelegCr
メソッドを呼び出して、そのクレデンシャルを取得できます。GSSCredential clientCr = context.getDelegCr();作成された GSSCredential を使用して、以降の GSS-API コンテキストをイニシエータの「代理人」として開始することができます。たとえば、サーバは、バックエンドサーバに対し、クライアントとして認証を行います。バックエンドサーバには、中間的サーバはどれかということよりも、元のクライアントがどれかということの方が重要です。
サーバは、クライアントとして動作することにより、バックエンドサーバとの接続確立、結合セキュリティコンテキストの確立、およびメッセージ交換を、クライアントやサーバと基本的に同じ方法で実行できます。
これを実行する 1 つの方法は、サーバが GSSManager の
createContext
メソッドを呼び出す際に、createContext
にnull
ではなく委譲されたクレデンシャルを渡すことです。別の選択肢として、サーバコードが最初に
com.sun.security.jgss.GSSUtil createSubject
メソッドを呼び出し、それに委譲されたクレデンシャルを渡すという方法もあります。このメソッドは、これらのクレデンシャルをデフォルトのクレデンシャルとして含むサブジェクトを返します。 その後、「JAAS 承認」チュートリアルの「サブジェクトのアクセス制御コンテキストへの関連付け」で説明したように、サーバは、このサブジェクトを現行の AccessControlContext に関連付けることができます。次に、サーバコードは、GSSManagercreateContext
メソッドの呼び出し時に、null (「現行の」サブジェクトをクレデンシャルとして使用することを指示する) を渡すことができます。 つまり、サーバが事実上クライアントになるといえます。その後の、GSS を使用するバックエンドサーバへの接続は前述のチュートリアルで説明したとおりに実行されます。これは、委譲されたクレデンシャルを使用するコードが、デフォルトのローカルクレデンシャルを使用するコードと同一であることが必要な場合に有用な方法です。クレデンシャルの委譲に必要なアクセス権
クレデンシャルを委譲するには、コンテキストイニシエータ (前のチュートリアルでは SampleClient) が
javax.security.auth.kerberos.DelegationPermission
を保持する必要があります。例を次に示します (斜体のプレースホルダには実際の値を指定)。permission javax.security.auth.kerberos.DelegationPermission "¥"service_principal@your_realm¥" ¥"krbtgt/your_realm@your_realm¥"";DelegationPermission が保持する単一のターゲット内に、引用符で囲まれた 2 つの項目が含まれることに注目してください。内部の各引用符は、「¥」でエスケープされています。このため、最初の項目は次のようになります。
2 番目の項目は、次のようになります。"service_principal@your_realm""krbtgt/your_realm@your_realm"これは、基本的に、クライアントとして実行されるコードに対し、指定されたピアに Kerberos チケットを転送するためのアクセス権を付与します。Kerberos チケットは、
krbtgt/your_realm@your_realm
からのサービス利用に使用します。表示されている
your_realm
は、すべて実際の領域で置き換えてください。また、service_principal@your_realm
も、サービスプリンシパル名 (サーバを表すサービスプリンシパルの名前) で置き換えてください(前のチュートリアルの「Kerberos ユーザおよびサービスプリンシパルの名前」を参照)。領域が KRBNT-OPERATIONS.ABC.COM で、サービスプリンシパルが "sample/raven.abc.com@KRBNT-OPERATIONS.ABC.COM" である場合を考えてみましょう。この場合、アクセス権をポリシーファイル内で次のように表示できます。permission javax.security.auth.kerberos.DelegationPermission "¥"sample/raven.abc.com@KRBNT-OPERATIONS.ABC.COM¥" ¥"krbtgt/KRBNT-OPERATIONS.ABC.COM@KRBNT-OPERATIONS.ABC.COM¥"";
フィードバック |