Google Secure Data Connectorを利用してFW内のデータをAppsと連携する

Google Secure Data Connector(以下SDC)は、Google Appsイントラネットのデータを安全に連携するために提供されているGoogle Appsの一機能です。

SDCを利用して、連携できるAppsのサービスには

があります。

Appsとイントラネットの連携は、SDCエージェントをイントラネットにインストールしてSSLトンネリングで通信します。

接続はSDC Agent(図中のSecure Data Connector)からTunnel Serversに対して行われるので、デフォルトではアウトバウンドの443ポートのみ空けてあれば基本的な通信は可能です。環境によってはFWの設定なしでも利用できてしまいます。

今回は、SDCをイントラネットと連携する手順を一通り試してみました。

  1. Appsの設定
  2. SDCエージェントのインストール
  3. AppEngineアプリケーションの作成

アプリケーションは、infoScoopのSaaSであるinfoScoop for Google Appsとの連携をターゲットとしているのでSites用のガジェットではなくAppEngineを選択しています。

今回はEC2インスタンスのローカルに立てたApacheイントラネットのサービスに見立ててテストプログラムを作成しました。
つまりは、本来DMZに置かれるSDC Agentと同じく、サービスも同一セグメントのDMZに配置されている環境と言えます。
試した環境の構成は以下ようになります。

EC2インスタンスのセキュリティグループの設定は以下の通り。インバウンドで空けているのはssh(22ポート)のみです。

以下、http://code.google.com/intl/ja/securedataconnector/docs/1.3/config.htmlにしたがって、SDCのインストールとAppsの設定を行い、その後、作成したAppEngineのアプリケーションのサンプルについて解説していきます。

Java 1.6のインストール

SDCはJavaのプログラムですので前提条件としてJava1.6以上がインストールされている必要があります。

EC2のAmazon Linuxは1.6のOpenJDKがインストールされているので、とりあえずそれを使います。

$java -version
java version "1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.1) (amazon-44.1.9.1.21.amzn1-x86_64)
OpenJDK 64-Bit Server VM (build 19.0-b06, mixed mode)

SDCのダウンロードとインストール

SDCをダウンロードして解凍します。2011/4/22現在の最新版は1.3-rc2です。

$ wget http://google-secure-data-connector.googlecode.com/files/google-secure-data-connector-1.3-rc2-all.zip
$ unzip google-secure-data-connector-1.3-rc2-all.zip

ダウンロードサイトはこちら: http://code.google.com/p/google-secure-data-connector/downloads/list

Apps管理画面でSDCを有効にする

Appsの管理画面の[高度なツール]タブの[Secure Data Connector]を表示します。

[Secure Data Connector有効にして設定]リンクをクリックします。

[有効化]にチェックして、[SDC Agent パスワード]を設定して[変更を保存]します。

次にsecure-data-connector-userの画像認証をアンロックします。
しかし、google accountsのアカウントとAppsアカウントが統合された後に変更されたのが原因なのか分かりませんが、開発ドキュメントにあるURL「https://www.google.com/a/<ドメイン名>/UnlockCaptcha」では「ユーザ名とパスワードが一致しません」とエラーになってしまいました。
とりあえず、Google Accountsの以下のURLで画像認証を外しました。

https://www.google.com/accounts/UnlockCaptcha

(後述しますが、最初に起動した際は認証エラーでSDCサーバーへ接続できませんでした、画像認証のアンロックができていないのが原因かと思い上記の手順で無理やりアンロックしましたが、その後もmypasswordファイルの設定に気づかず認証エラーになってしまっていたので、画像認証のアンロックが実際に有効だったのかどうかは不明です)

Google Appsへの接続を設定する

設定はconfig/localConfig.xmlに記述します。

$ cd data-connector-agent/config
$ vi localConfig.xml

  apps-secure-data-connector.google.com
  443
  co-meeting.com
  secure-data-connector-user
  xxxyyyzzz
  agent1_co-meeting-com
  1080
  

設定するのは赤字の箇所のみでが、一通り解説。

  • sdcServerHost: Google側のSDCサーバーのホスト名なので設定はそのまま。
  • sdcServerPort: Google側のSDCサーバーの通信ポートなので設定はそのまま。
  • domain: Google Appsドメインを指定します。
  • user: 管理画面で確認した固定のSDC Agentユーザ名「secure-data-connector-user」を指定するので、そのままです。
  • password: 管理画面で設定したSDC Agentパスワードを指定します。注意: 1.3-rc2では、起動シェルstart.shから呼び出されるrunagent.shをそのまま使う場合この設定は無視されます。
  • agentId: SDC AgentのIDを指定します。例の通り「agent1_<ドメイン名>」などを設定してください。英数字と_-のみが利用可能です。
  • socksServerPort: 1080を他のサービスで利用していない場合はそのまま。
  • healthCheckGadgetUsers: ヘルスチェックガジェットにアクセスするユーザを指定しますが、とりあえず今回は使わないので空のまま。

config/mypasswordにパスワードを設定する。
前述の通り、起動シェルをそのまま使う場合、SDCサーバに接続するパスワードはlocalConfig.xmlのpassword要素の値ではなくconfig/mypasswordに記述した値が使用されます。
シェルを編集しない場合は、mypasswordにパスワードを設定してください。

$ vi config/mypassword
xxxyyyzzz

ちなみに、com.google.dataconnector.client.Clientのソースを見ると以下の様に記述されているので、mypasswordファイルを消すか、runagent.shを適当に編集すればlocalConfig.xmlの値が使われます。

if (localConf.getPasswordFile() != null) {
  String password = new FileUtil().readFile(localConf.getPasswordFile());
  localConf.setPassword(password);
}

リソースルールを設定する

リソースルールはローカルドメインにアクセス可能なユーザを指定します。

$ vi resourceRules.xml

デフォルト設定は親切にたくさん書いてありますが、今回はAppEngineからのアクセスに関する設定のみしてみます。


  
    1
    agent1_co-meeting-com
    adimn@co-meeting.com
    
      AppEngine
      hello-sdc
    
    http://localhost/news.feed
    URLEXACT
  

イントラネットのホストへアクセスするためのURLは各rule要素に指定します。

  • ruleNum: SDCの処理が実行される順番を整数で指定します。
  • agetnId: localConfig.xmlに指定したagentIdを指定します。allはドキュメントには将来対応すると書いてありますが、現在のバージョンで動作するかは未確認です。
  • viewerEmail: URLで指定されるリソースにアクセス可能なユーザのEmailアドレスを指定します。これはこの組織の人に公開とかするには面倒くさいのか?
  • apps: 連携するサービスの設定を含みます。
    • service: 「AppEngine」、「Apps Script」、「Gadgets」、「Spreadsheets」のいづれかを指定
    • appId: service要素に「AppEngine」を指定した場合はアプリケーションID(http://<ここ>.appspot.com)、「Gadgets」の場合はガジェットのURLを指定します。
    • allowAnyAppId: service要素に「Spreadsheets」を指定した場合は、値trueを指定します。
  • url: アクセスするリソースのURLを指定します。
  • urlMatch: url要素に指定したURLの適用方法について指定します。
    • HOSTPORT: 指定したホスト名とポート名が合って入れば任意のパスに対してアクセスできます。
    • URLEXACT: 指定したURLのパスにのみアクセスを許可します。クエリーパラメータは任意です。

SDC Agentの起動

起動はroot権限のあるユーザで実行します。

$ sudo ./bin/start.sh

起動の確認

log/agent.logに以下の様なログが出力されればSDCサーバーへ接続成功です。

$ vi log/agent.log
*******************************************************************
Date: 2011年  4月 25日 月曜日 07:15:43 UTC
Config directory:
Runtime directory: /home/ec2-user/data-connector-agent
PID file: /home/ec2-user/data-connector-agent/agent.pid
args =
Password file = /home/ec2-user/data-connector-agent/config/mypassword
Run: /usr/bin/java -Djava.net.preferIPv4Stack=true -jar /home/ec2-user/data-connector-agent/lib/sdc-agent.jar  -localConfigFile "/home/ec2-user/data-connector-agent/config/localConfig.xml" -rulesFile "/home/ec2-user/data-connector-agent/config/resourceRules.xml" -log4jPropertiesFile "/home/ec2-user/data-connector-agent/config/log4j.properties" -passwordFile "/home/ec2-user/data-connector-agent/config/mypassword"
log4j:ERROR Could not find value for key log4j.appender.A.layout
25 4 2011 07:15:45,904 [main] INFO  com.google.dataconnector.client.SdcConnection  - Connecting to SDC server
25 4 2011 07:15:45,908 [main] INFO  com.google.dataconnector.util.SSLSocketFactoryInit  - Using SSL for client connections.
Setting iddle timeout to 60000 ms.
Setting accept timeout to 60000 ms.
Setting udp timeout to 600000 ms.
25 4 2011 07:15:45,918 [com.google.dataconnector.client.JsocksStarter] INFO  com.google.dataconnector.client.JsocksStarter  - Starting JSOCKS listener thread on port 1080
25 4 2011 07:15:45,923 [com.google.dataconnector.client.JsocksStarter] INFO  net.sourceforge.jsocks.socks.ProxyServer  - Starting SOCKS Proxy on:127.0.0.1:1080
25 4 2011 07:15:47,056 [main] INFO  com.google.dataconnector.client.SdcConnection  - Sending initial handshake msg
25 4 2011 07:15:47,056 [main] INFO  com.google.dataconnector.client.SdcConnection  - Attemping login
25 4 2011 07:15:47,839 [main] INFO  com.google.dataconnector.client.SdcConnection  - Successful login
25 4 2011 07:15:47,849 [main] INFO  com.google.dataconnector.registration.v4.Registration  - Sending resources info
agentId: "agent1_co-meeting-com"
socksServerPort: 1080
healthCheckPort: 65535
resourceKey {
  ip: "localhost"
  port: 80
  key: 270530414318891608
}
resourcesXml: "<resourceRules>\n  <rule repeatable=\"true\">\n    <ruleNum>1</ruleNum>\n    <agentId>agent1_co-meeting-com</agentId>\n    <viewerEmail repeatable=\"true\">adimn@co-meeting.com</viewerEmail>\n    <apps repeatable=\"true\">\n       <service>AppEngine</service>\n       <appId>hello-sdc</appId>\n    </apps>\n    <url>http://localhost/news.feed</url>\n    <urlMatch>URLEXACT</urlMatch>\n  </rule>\n</resourceRules>\n"

25 4 2011 07:15:47,854 [main] INFO  com.google.dataconnector.util.SdcKeysManager  - Adding rule for Pair[localhost, 80]
25 4 2011 07:15:47,858 [main] INFO  com.google.dataconnector.client.SdcConnection  - Starting hearbeat/ health check thread.
25 4 2011 07:15:47,863 [main] INFO  com.google.dataconnector.client.SdcConnection  - starting a thread to watch resources file
25 4 2011 07:15:47,864 [com.google.dataconnector.client.HealthCheckHandler] INFO  com.google.dataconnector.client.HealthCheckHandler  - healthcheck config is not yet received from the SDC server. will check again in 5 sec
25 4 2011 07:15:48,329 [main] INFO  com.google.dataconnector.registration.v4.Registration  - registration successful. Received config info from the SDC server
...

SDCと連携するAppEngineアプリケーションの作成

AppEngineから外部リソースの取得はURLFetchServiceを利用しますが、SDCの通信を通したリソースの取得も同様にURLFetchServiceを利用します。これまでの設定が終わっていれば、以下の様に通常の呼び出しにSDC通信用のヘッダーを指定するだけです。

from google.appengine.api import urlfetch
result = urlfetch.fetch(url="http://www.corp.example.com/sales.csv",
                        headers={'use_intranet': 'yes'})
if result.status_code == 200:
  parseCSV(result.content)

上記は、Pythonのコードです。以下に載せているシンプルなアプリケーションもPythonで記述しています。

Java版はSDCの開発ガイドに完全なサンプルの解説がありますのでそちらを参考にしてください。http://code.google.com/intl/ja/securedataconnector/docs/1.0/tutorials/appengine.html

また、Python版のAppEngine SDKの環境構築については以下を参考にしてください:

アプリケーションの登録

まず、AppEngineの管理画面(https://appengine.google.com/)から新規にアプリケーションを登録します。
SDCと連携するアプリケーションは、自社ドメインにのみ公開することがほとんどかと思います。
この場合、[Open to all Google Accounts users (default)]の[Edit]をクリックして[Restricted to the following Google Apps domain:]を選択、ドメインを指定します。

次にAppsにAppEngineのアプリケーションを登録します。
https://appengine.google.com/から登録したアプリケーションを選択します。

[Application Settings]を開き[Domain Setup]の[Add Domain...]をクリックします。

アプリケーションを利用するドメインを入力して[Add Domain...]をクリックします。
指定したドメインのAppsのログイン画面が表示されるのでログインします。

Google App Engine利用規約に[同意]にチェックし[このサービスを有効にする]をクリックします。

以上で、ドメインでAppEngineのアプリケーションが利用可能になります。

hello-sdc.py

Google Tunnel Servers - SDC Agentを介してhttp://localhost/news.feedからRSSを取得して表示する簡単なサンプルは以下の通りです。

from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.api import urlfetch
from xml.etree.ElementTree import *

user = users.get_current_user()
class MainPage(webapp.RequestHandler):
  def get(self):
    user = users.get_current_user()

    if user:
      #url = 'http://www.infoscoop.org/index.php/ja/news.feed'
      url = 'http://localhost/news.feed'
      result = urlfetch.fetch(url,
                        headers={'use_intranet': 'yes'})
      self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
      if result.status_code == 200:
        rss = fromstring(result.content)
        for item in rss.getiterator('item'):
          title = item.find('title')
          link = item.find('link')
          self.response.out.write(''+ title.text + '
') else: self.redirect(users.create_login_url(self.request.uri)) application = webapp.WSGIApplication( [('/', MainPage)], debug=True) def main(): run_wsgi_app(application) if __name__ == "__main__": main()

返すHTMLはかなり適当です。

XMLの処理は、標準ライブラリに含まれるxml.etree.ElementTreeを利用しました。
http://docs.python.org/library/xml.etree.elementtree.html#

app.yml
application: hello-sdc
version: 1
runtime: python
api_version: 1

handlers:
- url: /.*
  script: hello-sdc.py

サンプルは以上2ファイルです。
AppEngineへのデプロイします。

テスト用のRSSフィードを用意

ローカルにApacheをインストールしてルートにRSSを置きます。

$ sudo yum install httpd
$ cd /var/www/html/
$ sudo wget http://www.infoscoop.org/index.php/ja/news.feed
$ sudo /etc/init.d/httpd start
動作の確認

http://hello-sdc.appspot.com/」にアクセスすると、Appsドメインのログイン画面が表示されます。
ログインすると以下の様にRSSのアイテムが表示されました。(RSSはinfoScoopのニュースを拝借しています)