RPC::PlServer v0.2012

by Hippo2000(2000/12/24)

DBI::ProxyServerの親、RPC::PlServerなのです。Java RMIに対応するようなものだということです。

作者はJochen Wiedmannさんです。それにしても、この方は色々なものを作っています。

Jochen Wiedmannさんにはメールで了解をいただきました。

なお内容等が間違っていたら修正します。ご連絡ください。


目次


名前

RPC::PlServer - PlRPCサーバーを書くためのPerl拡張


概要

  # RPC::PlServerのサブクラスを作成する
  use RPC::PlServer;
  package MyServer;
  $MyServer::VERSION = '0.01';
  @MyServer::ISA = qw(RPC::PlServer);
  # Run()メソッドを1つの接続を扱うようにオーバーライト
  sub Run {
      my $self = shift;
      my $socket = $self->{'socket'};
  }
  # MyServerクラスのインスタンスの作成
  package main;
  my $server = MyServer->new({'localport' => '1234'}, \@ARGV);
  # 実際に実行させるポートいバインドする
  $server->Bind();

説明

PlRPC (Perl RPC) は完全にPerlによってかかれたサーバーとクライントを実装するためのパッケージです。この名前はSunのRPC(Remote Procedure Call)から借りてきましたが、JavaのRMI("Remote Method Interface")と同じぐらいよいものになるかもしれません。というのもPlRPCは非常に簡単なやり方でPerlのOOフレームワークの完全なパワーを与えるからです。

RPC::PlServerはサーバー側で使われるパッケージです、RPC::PlClientがなんのためのものかはお分かりでしょう。両方のパッケージはRPC::PlServer::Commを通信のために共有しています。これらの部分についてはPlRPC::Client(3)RPC::PlServer::Comm をご覧ください。

PlRPCはクライアントによって実行されるであろうメソッドのセットを定義することにより稼動します。例えばサーバーはメソッド "multply" をクライアントに提供するとします。そうすると関数の呼出しはクライアントでは以下のようになります。

    @result = $client->multiply($a, $b);

これはサーバーでは以下のように対応する呼出しに対応づけられます

    @result = $server->multiply($a, $b);

引数と結果はサーバーへ、あるいはサーバーから自動的に魔法で転送されます。(この魔法はPerlでの名前を持っています:それはStorableモジュールです。この素晴らしいパッケージについてRaphael Manfrediに感謝します)。簡単でしょ? :-)

RPC::PlServer と RPC::PlClient は抽象的なサーバーとクライアントです: 独自のクラスを派生させる必要があります。


追加のオプション

RPC::PlServerはNet::Daemonのオプションと属性をすべて継承しており、さらに以下のものを追加します。

cipher
この属性値はCrypt::DES、Crypt::IDEAまたはブロック暗号化のための同じAPIを持っているその他のクラスのインスタンスです。もしそのような属性を設定すると、クライアントとサーバーの間の転送はこのオプションを使って暗号化されます。
 
maxmessage (--maxmessage=size)
サービス不能攻撃をさけるため、クライアントとサーバーの間で交換されるメッセージの大きさは制限されます。デフォルトではその制限は65536 です。
 
users
これはクライアント・オブジェクトの属性は構成設定ファイル(config file)での許可/否認(Permit/Deny)ルールのために使われます。その値は、与えられたクライアントから接続を許された、ユーザー名の配列へのリファレンスです。構成設定ファイル(config file)の例は下記のC構成設定ファイル をご覧ください。

エラーの取扱い

完全にPerlの例外をベースとしているので、RPCパッケージでのエラーの取扱いはとても簡単です。そのため典型的なコードは以下のようになります:

  eval {
      # ここで何かをします。エラーについて何も注意しません
      ...
  };
  if ($@) {
      # エラー発生
      ...
  }

サーバのコンストラクタ

  my $server = RPC::PlServer(\%options, \@args);

(クラス・メソッド) このコンストラクタはNet::Daemonパッケージからそのまま継承されています。詳しくは Net::Daemon(3) をご覧ください。


アクセス制御

  $ok = $self->AcceptApplication($app);
  $ok = $self->AcceptVersion($version);
  $ok = $self->AcceptUser($user, $password);

RPC::PlServerパッケージはとても細かなアクセス制御スキームを持っています:まず最初にNet::Daemonのホストをベースとしたアクセス制御を継承しています。それはバージョン制御とユーザー認証も加えています。これを実現するために、Net::Daemonからのメソッド Accept は3つのメソッドに分割されています。AcceptApplication, AcceptVersion and AcceptUser のそれぞれは、TRUEまたはFALSEを返します。クライアントはその引数をapplication, version, user そして password. として羽k取ります。クライアントは上記のメソッドすべてがTRUEを返すときだけ受け付けられます。

デフォルトの実装では以下のようになります: $self$app のサブクラスであれば、AcceptApplicationはTRUEを返します。AcceptVersionは、要求されたバージョンが${$class}::VERSION 以下であればTRUEを返します。ユーザーが接続を許されるかどうかはクライアント構成設定によります。例は 構成設定ファイル をご覧ください。


メソッドベースのアクセス制御

クライアントに任意のメソッドを呼び出す能力を与えることは、恐ろしいセキュリティホールになりかねません。そのためサーバーはmethods 属性を持っています。これはクラス名をキーに、値はメソッド名をキーとしたハッシュのリファレンスとするハッシュのリファレンスです。つまり以下のようなハッシュだったとします:

    $self->{'methods'} = {
        'CalcServer' => {
            'NewHandle' => 1,
            'CallMethod' => 1 },
        'Calculator' => {
            'new' => 1,
            'multiply' => 1,
            'add' => 1,
            'divide' => 1,
            'subtract' => 1 }
        };

このときクライアントがオブジェクトを作るためにCalcServerのNewHandle メソッドを使うことができますが、許可されたコンストラクタ Calculator->newを介する場合のみです。一度、Calculatorが作成されると、サーバーはメソッドをmultiply、add、divide、subtractを呼び出すことができます。


構成設定ファイル

サーバーの構成設定ファイル(config file)はNet::Daemonから継承されています。users cipher 属性をクライアント・リストに追加しています。このため典型的な構成設定ファイルは以下のようになります:

    # Load external modules; this is not required unless you use
    # the chroot() option.
    #require DBD::mysql;
    #require DBD::CSV;
    # キーの作成
    my $myhost_key = Crypt::IDEA->new('83fbd23390ade239');
    my $bob_key    = Crypt::IDEA->new('be39893df23f98a2');
    {
        # 'chroot' => '/var/dbiproxy',
        'facility' => 'daemon',
        'pidfile' => '/var/dbiproxy/dbiproxy.pid',
        'user' => 'nobody',
        'group' => 'nobody',
        'localport' => '1003',
        'mode' => 'fork',
        # アクセス制御
        'clients' => [
            # ローカル LAN (192.168.1.*)を受け付ける
            {
                'mask' => '^192\.168\.1\.\d+$',
                'accept' => 1,
                'users' => [ 'bob', 'jim' ],
                'cipher' => $myhost_key
            },
            # myhost.company.com を受け付ける
            {
                'mask' => '^myhost\.company\.com$',
                'accept' => 1,
                'users' => [ {
                    'name' => 'bob',
                    'cipher' => $bob_key
                    } ]
            },
            # その他は受け付けない
            {
                'mask' => '.*',
                'accept' => 0
            }
        ]
    }

気をつけなければならないこと:192.168.1*のユーザーリストにはスカラー値が入っていますが、myhost.company.comのユーザリストにはハッシュへのリファレンスが入っています;これはユーザーの構成設定はユーザー・ベースの暗号化になっているので、これは必要です。


十分に時間を使ったので、言葉ではなく例を展開しましょう :-)。MD5ダイジェストのためのサーバーという簡単なサーバーを書きましょう。このサーバーは外部パッケージ MD5を使いますが、クライアントにはこのパッケージ MD5 をインストールする必要はありません。ここではサーバーのソースを示します。クライアントの部分はRPC::PlClient manページにあります。 RPC::PlClient(3). をご覧ください。

    #!/usr/bin/perl -wT
    # -T スィッチに注意してください! これはPerlサーバーには常に推奨されます。
    use strict;               # つねによい選択です.
    require RPC::PlServer;
    require MD5;
    package MD5_Server;  # クライアントはアプリケーション "MD5_Server"を 
                         # 要求する必要があります
    $MD5_Server::VERSION = '1.0'; # もしバージョン 1.1を要求するのであれば、
                                  # クライアントは拒絶されます
    @MD5_Server::ISA = qw(RPC::PlServer);
    eval {
        # 以下のサーバーオプションは構成設定フィル(config file)またはコマンドラインで
        # 上書きすることができます。
        my $server = MD5_Server->new({
            'pidfile'    => '/var/run/md5serv.pid',
            'configfile' => '/etc/md5serv.conf',
            'facility'   => 'daemon', # Default
            'user'       => 'nobody',
            'group'      => 'nobody',
            'localport'  => 2000,
            'logfile'    => 0,        # Use syslog
            'mode'       => 'fork',   # Recommended for Unix
            'methods'    => {
                'MD5_Server' => {
                    'ClientObject' => 1,
                    'CallMethod' => 1,
                    'NewHandle' => 1
                    },
                'MD5' => {
                    'new' => 1,
                    'add' => 1,
                    'hexdigest' => 1
                    },
                }
        });
        $server->Bind();
    };

セキュリティ

いわなければならないことがあります:PlRPCをベースとしたサーバーは潜在的にセキュリティに問題があります! 私はセキュリティ問題を避けるために全力を尽くしてきましたが、何かを落としているかもしれません。セキュリティは設計の結果1つですが、設計の結果だけではありません。(よく知られている問題ですが...)

以下の設計原則を強くお勧めします:


"信用されている"ユーザーに対する保護

perlsec
perlのセキュリティに関するFAQ(perldoc perlsec)を読み、-T スィッチを使ってください。
 
汚染モードperl
-Tスィッチを使ってください。 私は本気で言っています!
データの検証
 
検証しないで文字列を汚染モードからはずさないでください。2回検証するとさらによいです。例えば CallMethodは、それにメソッドを強制する前に、まずオブジェクト・ハンドルが適切かどうかをチェックします
 
制約的になれ
 
メソッドへのアクエスをクライアントに与える前に2回考えましょう。
perlsec
そして私が忘れている場合には: perlsec man ページを読みましょう。 :-)

信用されていないユーザに対する保護

ホスト・ベースの認証
PlRPC は組込みのホスト・ベースの認証スキームを持っています; それを使ってください! 構成設定ファイル をご覧ください。
ユーザー・ベースの認証
PlRPC は組込みのユーザー・ベースの認証スキームを持っています; それを使ってください! 構成設定ファイル をご覧ください。
暗号化
PlRPC で暗号かを使うことはとても簡単です。クライアントと暗号化なしで通信するのには何ら絶対的な理由はありません。さらに:wたしは2フェーズ暗号化を推奨します。最初のフェーズはログインフェースで、ここではホストをベースとしたキーを使います。ユーザーが認証されたら、ユーザをベースとするキーに切り替えます。例としてDBI::ProxyServerをご覧ください。

作者と著作権(AUTHOR AND COPYRIGHT)

(原文のまま)

The PlRPC-modules are

  Copyright (C) 1998, Jochen Wiedmann
                      Am Eisteich 9
                      72555 Metzingen
                      Germany
                      Phone: +49 7123 14887
                      Email: joe@ispsoft.de
  All rights reserved.

You may distribute this package under the terms of either the GNU General Public License or the Artistic License, as specified in the Perl README file.


参考資料

RPC::PlClient(3), RPC::PlServer::Comm(3), Net::Daemon(3), Net::Daemon::Log(3), Storable(3), Sys::Syslog(3), Win32::EventLog(3)

アプリケーションの例として DBI::ProxyServer(3) をご覧ください。


ホーム Perlの小技

ご意見、ご質問はこちらの掲示板で受け付けています。
またメールは河馬屋(Nifty)にお願いします。