GateOneをAPI認証機能有効化してアプリケーションに組み込む

GateOneには、認証機能が備わっています。
Googleの認証と連携したり、PAM認証と連携するなどができるようです。
(GateOneについては前回記事GateOneで複数のSSH接続をブラウザで統合管理 - ike-daiの日記を参照して下さい。)

今回は、GateOneを別のアプリケーションに組み込んで使う場合のAPI認証を有効化する方法を試してみます。
手順は公式マニュアルをもとに実施しています。

server.confの変更

GateOneのserver.confの設定でauth = 'api'に設定します。(デフォルトはnone)

$ vim /opt/gateone/server.conf
・・・略
auth = 'api'
・・・略

APIキーの発行

gateone.pyスクリプトを使ってAPIキーを発行します。

$ /opt/gateone/gateone.py --new_api_key
[I 130322 09:35:19 gateone:2880] A new API key has been generated: MDg0MGFmY2I0YTBiNGU0OTg5NGRjMzcwNmM2NjBkNDEyA
[I 130322 09:35:19 gateone:2881] This key can now be used to embed Gate One into other applications.

発行されたAPIキーの情報はserver.confのapi_keysに登録されています。

api_keys = "MDg0MGFmY2I0YTBiNGU0OTg5NGRjMzcwNmM2NjBkNDEyA:ODg0MmRhYjJkYzU0NDg2M2E4NjQ5MTk3NzY0YjkzOWQwM"

APIキー:シークレットキーのセットで書きこまれているのがわかります。

アプリケーション側の設定

次に、GateOneを組み込みたいアプリケーション側の設定です。

まず、API認証を有効にしない場合にはこのような感じでGateOneを組み込むことができます。
https://hostnameでGateOneが稼働している場合の例です。

<html>
    <head>
        <script src="https://hostname/static/gateone.js"></script>
    </head>
    <body>
        <div style="width: 60em; height: 30em;">
            <div id="gateone"></div>
        </div>
       <span onclick="GateOne.init({url:'https://hostname/'});">Start GateOne</span>
    </body>
</html>

gateone.jsを読み込み、GateOne.initを実行すると、

のエリアにGateOneのコンソールを表示できます。
認証設定がない場合、GateOneはANONYMOUSユーザでアクセスした状態になります。
この状態では、ブックマークの情報や鍵情報が他の誰からも利用できるため、セキュリティ的にもよくありません。
そこで、このファイルを変更し、API認証を実現してみます。

API認証を実施するには、GateOne.initの実行時に認証オブジェクト({auth: auth_obj})を渡す必要があります。
認証オブジェクトは次のようなユーザ名やAPIキー等を持ったオブジェクトです。

authobj = {
    'api_key': 'MjkwYzc3MDI2MjhhNGZkNDg1MjJkODgyYjBmN2MyMTM4M',
    'upn': 'joe@company.com',
    'timestamp': '1323391717238',
    'signature': "f6c6c82281f8d56797599aeee01a5e3efab05a63",
    'signature_method': 'HMAC-SHA1',
    'api_version': '1.0'
}

※GateOne公式マニュアルより引用

今回は試しにJavaScriptで認証オブジェクトを生成して許可するようにします。
このようになります。

<html>
    <head>
        <script src="https://localhost/static/gateone.js"></script>
        <script src='http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha1.js'></script>
        <script>
            function ConnectGateOne(){
                var api_key = 'MDg0MGFmY2I0YTBiNGU0OTg5NGRjMzcwNmM2NjBkNDEyA';
                var upn = 'ikeda@example.com';
                var now = new Date();
                var timestamp = now.getTime();
                var signature_method = 'HMAC-SHA1';
                var api_version = '1.0';
                var secret = 'ODg0MmRhYjJkYzU0NDg2M2E4NjQ5MTk3NzY0YjkzOWQwM';
                var hash = CryptoJS.HmacSHA1(api_key + upn + timestamp, secret);
                var signature = hash.toString(CryptoJS.enc.Hex);
                
                var authobj = {
                    'api_key':api_key,
                    'upn':upn,
                    'timestamp':timestamp.toString(),
                    'signature':signature,
                    'signature_method':signature_method,
                    'api_version':api_version
                }
                console.log(authobj);
                
                GateOne.init({auth: authobj, url:'https://hostname/'});
            }
        </script>
    </head>
    <body>
        <div style="width: 60em; height: 30em;">
            <div id="gateone"></div>
        </div>
       <span onclick="ConnectGateOne();">Start GateOne</span>
    </body>
</html>

注意点としては、timestampの値を文字列型で持たせる必要がある点です。
timestampをそのまま数値型で持たせていると、GateOne内で処理する際に、signature生成処理中にエラーが発生します。

[E 130322 10:51:48 websocket:261] Uncaught exception in /ws
    Traceback (most recent call last):
      File "/usr/lib/pymodules/python2.7/tornado/websocket.py", line 258, in wrapper
        return callback(*args, **kwargs)
      File "/opt/gateone/gateone.py", line 1102, in on_message
        self.commands[key](value)
      File "/opt/gateone/gateone.py", line 1245, in authenticate
        secret, api_key, upn, timestamp)
      File "/usr/lib/pymodules/python2.7/tornado/web.py", line 2068, in _create_signature
        hash.update(utf8(part))
      File "/usr/lib/pymodules/python2.7/tornado/escape.py", line 168, in utf8
        assert isinstance(value, unicode)
    AssertionError

このようにすることで、GateOneへのログインユーザがikeda@example.comになります。
ログインした状態でブックマークの追加や鍵の追加を行うと、他のユーザ(ANONYMOUS等)からはこの情報は見れなくなります。
ブックマーク設定や鍵については、「/opt/gateone/users/ユーザ名」ディレクトリの配下に保存されるようになります。

今回は簡単に動作確認するためにJavaScriptAPI認証を行う処理を記述しましたが、実際には、組み込むアプリケーション内で上記の処理を行うようにします。
PHPRubyPythonでの記述方法についてはGateOneの公式サイトに書かれているので参考にして下さい。
公式マニュアル