概要
パッケージ版のサイボウズ Office は、LDAP 連携も SAML 連携にも対応していません。予算が確保できるのであれば、クラウド版のサイボウズ Office 1かサイボウズガルーンを使うべきだと思いますが、組織規模も予算規模も小さいため Single Sign On (SSO) 対応の予算は確保できませんでした。仕方がないので、リバースプロキシ方式で SSO 対応する Perl スクリプトを作成しました。
- フロントエンドの Apache に、リバースプロキシとして動作する CGI を設置する。ただし、このリバースプロキシ CGI は、バックエンドで動作しているサイボウズ本来の CGI からの応答を監視していて、必要に応じて代理ログインを行う。
- バックエンドの Apache で、サイボウズ本来の CGI を動作させる。
- 画像などの静的なコンテンツは、フロントエンド側の Apache で処理する。
なお、本稿はサイボウズ Office 8 で動作確認していますので、最新版などをご利用の場合は適宜に読みかえて下さい。
前提
- サイボウズ Office のアカウント名と、認証連携先のアカウント名が同一である
- サイボウズ Office のパスワードは、全員共通の秘密パスワードにしても差し支えない
手順
フロントエンドの Apache にサイボウズ Office をインストールする
インストールマニュアルにしたがって、通常通りにサイボウズ Office が動作するように設定してください。デフォルトの設定では、以下の URL でアクセスできる場所にインストールされているはずだと思います。
- サイボウズの CGI ⇒ https://example.jp/cgi-bin/cbag/ag.cgi
- サイボウズの画像など静的なファイル ⇒ https://example.jp/cb80/
バックエンドの Apache でもサイボウズ Office を動くように設定する
フロントエンドの設定をコピーして、https://example.jp/ で参照できるウェブページが https://example.jp:8443/ でも参照できるように設定してください。Debian GNU/Linux の場合は、以下のような手順になります。
cp -p /etc/apache2/sites-available/default-ssl.conf /etc/apache2/sites-available/my-ssl-backend.conf
vi /etc/apache2/sites-available/my-ssl-backend.conf
a2ensite my-ssl-backend
a2ensite する前に、/etc/apache2/sites-available/my-ssl-backend.conf の先頭のポート番号を以下のように書き換えておきます。
<IfModule mod_ssl.c>
<VirtualHost _default_:8443>
ServerName example.jp:8443
ServerAdmin webmaster@example.jp
(以下略)
ここまでの設定で、https://example.jp:8443/cgi-bin/cbag/ag.cgi でもサイボウズ Office が動くようになっているはずですので、確認しておいてください。また、全ての既存ユーザのパスワードを、共通秘密パスワードに変更しておいてください。
フロントエンドのサイボウズ Office を Shibboleth 認証管理下に置く
以下の設定を適切な箇所に記述します。
<LocationMatch "/(cgi-bin/cbag|cb80)/">
AuthType shibboleth
Require shibboleth
</LocationMatch>
これで、https://example.jp/cgi-bin/cbag/ag.cgi にたどり着く前に Shibboleth 認証が必要になっているはずですので、確認しておいてください。
なお、後述するリバースプロキシ CGI は REMOTE_USER 環境変数を参照しているだけですから、Shibboleth に限らず Apache の認証モジュールなら大抵は利用することができます。
フロントエンドのサイボウズ Office をリバースプロキシ CGI で置き換える
まず、mod_fcgid と必要な perl モジュールをインストールしておきます。
apt-get install libapache2-mod-fcgid libfcgi-perl
次に、以下のような CGI を適当なディレクトリに配置します。なお、$BACKENDURL
は実際のバックエンド側 Apache で動作しているサイボウズ Office の URL に、また、$PASSWORD
はサイボウズ Office にログインする場合の共通秘密パスワードに修正してください。
# !/usr/bin/perl
use FCGI;
use LWP::UserAgent;
use strict;
use warnings;
binmode STDIN, ':bytes';
binmode STDOUT, ':bytes';
binmode STDERR, ':bytes';
our $BACKENDURL = 'https://example.jp:8443/cgi-bin/cbag/ag.cgi';
our $PASSWORD = '0123456789';
&run_proxy_server();
sub run_proxy_server {
my $fcgi = FCGI::Request();
my $ua = LWP::UserAgent->new( keep_alive => 1, max_redirect => 0 );
while( $fcgi->Accept() >= 0 ){
my $request = &create_request();
$ua->agent( $ENV{'HTTP_USER_AGENT'} || 'libwww-perl' );
my $response = $ua->simple_request( $request );
unless( $response->header( 'X-CybozuLogin' ) ){
&print_response( $response );
} else {
&login_cybozu( $ua );
}
}
}
sub login_cybozu {
my $ua = shift;
my $response = $ua->post( $BACKENDURL,
{ _Account => $ENV{'REMOTE_USER'},
Password => $PASSWORD,
_System => 'login',
_Login => 1,
csrf_ticket => '' } );
my( @cookie ) = $response->header( 'Set-Cookie' );
if( grep( /\AAGSESSID=/, @cookie ) ){
print "Status: 302\n";
for my $x ( @cookie ) {
print 'Set-Cookie: ', $x, "\n";
}
printf( "Location: %s?%s\n\n", $ENV{'SCRIPT_URI'}, $ENV{'QUERY_STRING'} );
} else {
printf <<'__forbidden__';
Status: 403 Forbidden
Content-Type: text/plain
You are not allowed to access Cybozu. Contact the administrator.
__forbidden__
}
}
sub create_request {
my $url = $BACKENDURL;
$url .= $ENV{'PATH_INFO'} if $ENV{'PATH_INFO'};
$url .= '?' . $ENV{'QUERY_STRING'} if $ENV{'QUERY_STRING'};
my $request = HTTP::Request->new( $ENV{'REQUEST_METHOD'}, $url );
while( my( $key, $val ) = each %ENV ){
next unless $key =~ s/\AHTTP_//;
next if $key =~ m/\A(HOST|CONNECTION)\Z/;
$request->header( $key, $val );
}
if( my $type = $ENV{'CONTENT_TYPE'} ){
$request->header('content-type', $type );
if ( my $len = $ENV{'CONTENT_LENGTH'} ) {
my $buf;
read( STDIN, $buf, $len );
$request->content( $buf );
} else {
$request->content( join( '', <STDIN> ) );
}
}
$request->header( 'X-Forwarded-For', $ENV{REMOTE_ADDR} );
$request->header( 'X-Forwarded-Host', $ENV{HTTP_HOST} );
$request->header( 'X-Forwarded-Server', $ENV{SERVER_NAME} );
$request;
}
sub print_response {
my( $response ) = @_;
my $status = $response->status_line;
$status =~ s!\AHTTP/\d+\.\d+\s+!!;
print 'Status: ', $status, "\n", $response->headers_as_string, "\n", $response->content;
}
この CGI は、以下のように動作します。
- mod_fcgid 経由で、ブラウザからのリクエストを受け取る。
- 受け取ったリクエスト(cookie などのヘッダおよびコンテンツ)に基づいて、バックエンドで動作しているサイボウズ Office 本来の CGI にアクセスするためのリクエストを作成する(create_request 関数)。
- 得られたリクエストを用いて、バックエンドにアクセスする。
- バックエンドからの応答において X-Cybozu-Login というヘッダに値がセットされていれば、サイボウズ Office へのログインができていない状態と判定し、代理ログインするための login_cybozu 関数に遷移。それ以外の場合は、バックエンドからの応答をそのまま出力する(リバースプロキシとして動作する)。
その上で,フロントエンドのサイボウズ Office の代わりに、上記のリバースプロキシ CGI が呼び出されるように以下の設定を書いてください。
ScriptAlias /cgi-bin/cbag/ag.cgi /local/example/cbag/cybozu-proxy.fcgi
これで、https://example.jp/cgi-bin/cbag/ag.cgi にアクセスすると、リバースプロキシ用 CGI によって代理ログインされて、サイボウズ Office が使えるはずです。
バックエンドのサイボウズ Office へのアクセスを制限する
サイボウズ Office の全ユーザが同一の共通秘密パスワードを使うようになっていますので、バックエンドのサイボウズ Office に直接アクセスされると、他ユーザへのなりすましが可能です。この問題を避けるために、バックエンドの Apache には以下のように設定して、外部からのアクセスを禁止しておきましょう。
<LocationMatch "/(cgi-bin/cbag|cb80)/">
Order deny,allow
Deny from all
Allow from aaa.bbb.ccc.ddd
</LocationMatch>
aaa.bbb.ccc.ddd
は、サーバの IP アドレスで置き換えてください。
注意点
ユーザーが、サイボウズ Office の「個人設定」で自分のパスワードを変更してしまうと、login_cybozu 関数による代理ログインが動作しないようになってしまいます。「システム設定」→「ユーザー管理」→「パスワードの制限」と進んで、「個人設定でのパスワード変更」を「許可しない」に設定しておきましょう。
-
クラウド版のサイボウズ Office は、SAML 連携に対応しているようです。 ↩