OpenStack Object Storage は、コード参照時に swift としても知られ、Python Paste フレームワークに基づいています。そのアーキテクチャーは、 A Do-It-Yourself Framework から始めると最も良いでしょう。swift プロジェクトはこのフレームワークを使用しているので、コアのコードを変更することなく、プロジェクトのパイプラインにカスタムコードをいくつか配置することにより、プロジェクトに機能を追加できます。
お使いのコンテナーの 1 つにパブリックにアクセスできるシナリオを想像してください。しかし、本当にやりたいことは、ホワイトリストに基づいてアクセスできる IP を制限することです。この例では、コンテナーのメタデータ項目により決められるよう、ある IP アドレス群だけからコンテナーにアクセスを許可する、swift 向けのミドルウェア部品を作成します。コンテナーのメタデータを使用して、明示的にホワイトリストに入っている IP アドレスのみが、コンテナーにアクセスできます。
警告
この例は実証目的のみのためにあります。さらなる作りこみと広範なセキュリティテストなしにコンテナのIPホワイトリスト・ソリューションとして使用するべきではありません。
stack.sh
が screen -r stack
で作成したセッションに join すると、動作中の各サービスのスクリーンを参照できます。これは、DevStack が実行するよう設定したサービスの数に依存して、いくつかあるでしょう。
アスタリスク (*) は、表示している screen ウィンドウを表しています。この例は、keystone 用の key という screen ウィンドウを表示していることを表しています。
0$ shell 1$ key* 2$ horizon 3$ s-proxy 4$ s-object 5$ s-container 6$ s-account
screen ウィンドウの目的は、以下のとおりです。
shell
key*
horizon
s-{name}
ミドルウェアを作成して Paste の環境設定を通して組み込むためには:
すべての OpenStack のコードは /opt/stack
にあります。 shell
セッションの screen の中で swift ディレクトリに移動し、あなたのミドルウェアモジュールを編集してください。
Object Storage がインストールされるディレクトリーを変更します。
$ cd /opt/stack/swift
ip_whitelist.py
Python ソースコードファイルを作成します。
$ vim swift/common/middleware/ip_whitelist.py
以下の示すコードを ip_whitelist.py
にコピーします。以下のコードは、このセクションの初めに説明されたように、IP アドレスに基づいてコンテナーへのアクセスを制限するミドルウェアの例です。ミドルウェアは、他のアプリケーションへのリクエストを通過させます。この例は、swift 「swob」 ライブラリーを使用して、swift が通信するオブジェクトに関する Web Server Gateway Interface (WSGI) のリクエストとレスポンスをラップします。これを実行したとき、ファイルを保存して閉じます。
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2014 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import socket
from swift.common.utils import get_logger
from swift.proxy.controllers.base import get_container_info
from swift.common.swob import Request, Response
class IPWhitelistMiddleware(object):
"""
IP Whitelist Middleware
Middleware that allows access to a container from only a set of IP
addresses as determined by the container's metadata items that start
with the prefix 'allow'. E.G. allow-dev=192.168.0.20
"""
def __init__(self, app, conf, logger=None):
self.app = app
if logger:
self.logger = logger
else:
self.logger = get_logger(conf, log_route='ip_whitelist')
self.deny_message = conf.get('deny_message', "IP Denied")
self.local_ip = socket.gethostbyname(socket.gethostname())
def __call__(self, env, start_response):
"""
WSGI entry point.
Wraps env in swob.Request object and passes it down.
:param env: WSGI environment dictionary
:param start_response: WSGI callable
"""
req = Request(env)
try:
version, account, container, obj = req.split_path(1, 4, True)
except ValueError:
return self.app(env, start_response)
container_info = get_container_info(
req.environ, self.app, swift_source='IPWhitelistMiddleware')
remote_ip = env['REMOTE_ADDR']
self.logger.debug("Remote IP: %(remote_ip)s",
{'remote_ip': remote_ip})
meta = container_info['meta']
allow = {k:v for k,v in meta.iteritems() if k.startswith('allow')}
allow_ips = set(allow.values())
allow_ips.add(self.local_ip)
self.logger.debug("Allow IPs: %(allow_ips)s",
{'allow_ips': allow_ips})
if remote_ip in allow_ips:
return self.app(env, start_response)
else:
self.logger.debug(
"IP %(remote_ip)s denied access to Account=%(account)s "
"Container=%(container)s. Not in %(allow_ips)s", locals())
return Response(
status=403,
body=self.deny_message,
request=req)(env, start_response)
def filter_factory(global_conf, **local_conf):
"""
paste.deploy app factory for creating WSGI proxy apps.
"""
conf = global_conf.copy()
conf.update(local_conf)
def ip_whitelist(app):
return IPWhitelistMiddleware(app, conf)
return ip_whitelist
env
と conf
には、リクエストについて何をするのか判断するのに使える有用な情報が多数含まれています。どんなプロパティが利用可能なのかを知るには、以下のログ出力文を __init__
メソッドに挿入してください。
self.logger.debug("conf = %(conf)s", locals())
そして以下のログ出力分を __call__
メソッドに挿入してください。
self.logger.debug("env = %(env)s", locals())
このミドルウェアを swift Paste のパイプラインに組み込むには、設定ファイル /etc/swift/proxy-server.conf
を編集します。
$ vim /etc/swift/proxy-server.conf
/etc/swift/proxy-server.conf
の [filter:ratelimit]
セクションを探し、その後ろに以下の環境定義セクションを貼り付けてください。
[filter:ip_whitelist]
paste.filter_factory = swift.common.middleware.ip_whitelist:filter_factory
# You can override the default log routing for this filter here:
# set log_name = ratelimit
# set log_facility = LOG_LOCAL0
# set log_level = INFO
# set log_headers = False
# set log_address = /dev/log
deny_message = You shall not pass!
/etc/swift/proxy-server.conf
[pipeline:main]
セクションを探し、このように ip_whitelist
リストを ratelimit の後ろに追加してください。完了したら、ファイルを保存して閉じてください。
[pipeline:main]
pipeline = catch_errors gatekeeper healthcheck proxy-logging cache bulk tempurl ratelimit ip_whitelist ...
swift proxy
にこのミドルウェアを使わせるために、Swift プロキシサービスを再起動します。swift-proxy
の screen セッションに切り替えてはじめてください。
swift``の CLI でミドルウェアのテストをしてください。shell の screen セッションに切り替えてテストを開始し、 ``swift-proxy
の screen セッションにもどってログ出力をチェックして終了します。
Ctrl+A に続けて 0 を押します。
devstack
ディレクトリーにいることを確認します。
$ cd /root/devstack
openrc を読み込み、CLI の環境変数を設定します。
$ . openrc
middleware-test
という名前のコンテナーを作成します。
$ swift post middleware-test
Ctrl+A に続けて 3 を押して、ログ出力を確認します。
ログの中に以下の行があるでしょう。
proxy-server Remote IP: my.instance.ip.address (txn: ...)
proxy-server Allow IPs: set(['my.instance.ip.address']) (txn: ...)
これらの2行は、このミドルウェアによって出力されており、リクエストが DevStack インスタンスから送られており、許可されていることを示しています。
Test the middleware from outside DevStack on a remote machine that has access to your DevStack instance:
ローカルマシンに keystone
と swif
クライアントをインストールします。
# pip install python-keystoneclient python-swiftclient
middleware-test
コンテナーにあるオブジェクトを一覧表示しようとします。
$ swift --os-auth-url=http://my.instance.ip.address:5000/v2.0/ \
--os-region-name=RegionOne --os-username=demo:demo \
--os-password=devstack list middleware-test
Container GET failed: http://my.instance.ip.address:8080/v1/AUTH_.../
middleware-test?format=json 403 Forbidden You shall not pass!
Ctrl+A に続けて 3 を押して、ログ出力を確認します。再び swift のログを確認すると、ログの中に以下の行があるでしょう。
proxy-server Authorizing from an overriding middleware (i.e: tempurl) (txn: ...)
proxy-server ... IPWhitelistMiddleware
proxy-server Remote IP: my.local.ip.address (txn: ...)
proxy-server Allow IPs: set(['my.instance.ip.address']) (txn: ...)
proxy-server IP my.local.ip.address denied access to Account=AUTH_... \
Container=None. Not in set(['my.instance.ip.address']) (txn: ...)
ここで、リモートIPアドレスが、許可されたIPアドレスの中になかったため、リクエストが拒否されていることがわかります。
シェル画面において DevStack 用インスタンスに戻り、リモートマシンからのリクエストを許可するようなコンテナのメタデータを追加します。
Ctrl+A に続けて 0 を押します。
メタデータをコンテナーに追加して、IP を許可します。
$ swift post --meta allow-dev:my.local.ip.address middleware-test
ここで手順 10 のコマンドを再び試みて、続行します。コンテナーにオブジェクトがありません。そのため、一覧には何もありません。しかしながら、レポートするエラーもありません。
警告
このような機能試験は、正しいユニットテストと結合テストの代わりになるものではありませんが、作業を開始することはできます。
Python Paste フレームワークを使う他のすべてのプロジェクトで、類似のパターンに従うことができます。単純にミドルウェアモジュールを作成し、環境定義によって組み込んでください。そのミドルウェアはプロジェクトのパイプラインの一部として順番に実行され、必要に応じて他のサービスを呼び出します。プロジェクトのコア・コードは一切修正しません。Paste を使っているプロジェクトを確認するには、 /etc/<project>
に格納されている、プロジェクトの conf
または ini
環境定義ファイルの中で pipeline
変数を探してください。
あなたのミドルウェアが完成したら、オープンソースにし、OpenStack メーリングリストでコミュニティに知らせることをお薦めします。もしかしたら他の人も同じ機能を必要としているかもしれません。彼らはあなたのコードを使い、フィードバックし、おそらくコントリビュートするでしょう。もし十分な支持があれば、もしかしたら公式な swift ミドルウェア への追加を提案してもよいでしょう。
Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. See all OpenStack Legal Documents.