[ English | 日本語 | Deutsch | Indonesia ]
定制对象存储 (Swift) 中间件¶
OpenStack 对象存储,在代码中被称为 swift,基于 Python Paste 框架。 最佳的架构介绍可以在 Read The Docs 上找到。 由于 swift 项目使用此框架,因此您可以通过将一些自定义代码放置在项目的管道中来添加功能,而无需更改任何核心代码。
想象一下这样一个场景:您有一个容器具有公共访问权限,但实际上您只想将其访问权限限制为基于白名单的一组 IP。 在此示例中,我们将为 swift 创建一个中间件,该中间件仅允许从一组 IP 地址访问容器,这些 IP 地址由容器的元数据项确定。 只有您使用容器的元数据显式列入白名单的 IP 地址才能访问该容器。
警告
此示例仅用于说明目的。 在没有进一步开发和广泛的安全测试的情况下,不应将其用作容器 IP 白名单解决方案。
当您加入 stack.sh 启动的 screen 会话,使用 screen -r stack 时,您会看到每个正在运行的服务的一个 screen,具体数量取决于您配置 DevStack 运行的服务数量。
星号 * 表示您正在查看的 screen 窗口。 此示例显示我们正在查看 key (用于 keystone) screen 窗口
0$ shell 1$ key* 2$ horizon 3$ s-proxy 4$ s-object 5$ s-container 6$ s-account
screen 窗口的用途如下
shell一个 shell,您可以在其中完成一些工作
key*keystone 服务
horizonhorizon 仪表板 Web 应用程序
s-{name}swift 服务
要创建中间件并通过 Paste 配置将其插入
OpenStack 的所有代码都位于 /opt/stack 中。 转到 shell screen 中的 swift 目录并编辑您的中间件模块。
更改到安装对象存储的目录
$ cd /opt/stack/swift
创建
ip_whitelist.pyPython 源代码文件$ vim swift/common/middleware/ip_whitelist.py
将以下代码复制到
ip_whitelist.py中。 以下代码是一个中间件示例,它根据 IP 地址限制对容器的访问,如本节开头所述。 中间件将请求传递给另一个应用程序。 此示例使用 swift “swob” 库将 Web Server Gateway Interface (WSGI) 请求和响应包装到 swift 可以交互的对象中。 完成后,保存并关闭文件。# 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 # # https://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-proxyscreen按下 Ctrl+A,然后按下 3。
按下 Ctrl+C 以终止服务。
按下 Up Arrow 以调出上一个命令。
按下 Enter 以运行它。
使用
swiftCLI 测试您的中间件。 首先切换到 shell screen,最后切换回swift-proxyscreen 以检查日志输出按下 Ctrl+A,然后按下 0。
确保您位于
devstack目录中$ cd /root/devstack
source 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: ...)
这两条语句由我们的中间件生成,表明请求是从我们的 DevStack 实例发送的,并且被允许了。
从可以访问您的 DevStack 实例的远程机器上的 DevStack 外部测试中间件
在您的本地机器上安装
keystone和swift客户端# 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 实例的 shell screen 中,为容器添加一些元数据,以允许来自远程机器的请求
按下 Ctrl+A,然后按下 0。
添加元数据以允许 IP
$ swift post --meta allow-dev:my.local.ip.address middleware-test
现在再次尝试步骤 10 中的命令,它会成功。 容器中没有对象,因此没有要列出的内容;但是,也没有错误报告。
警告
像这样的功能测试不能替代适当的单元和集成测试,但它可以帮助您入门。
您可以遵循其他使用 Python Paste 框架的项目中的类似模式。 仅创建中间件模块并通过配置将其插入。 中间件按顺序运行,作为该项目管道的一部分,并可以根据需要调用其他服务。 不会触碰任何项目核心代码。 在 /etc/<project> 中的项目的 conf 或 ini 配置文件中查找 pipeline 值,以识别使用 Paste 的项目。
当您的中间件完成后,我们鼓励您将其开源并在 OpenStack 邮件列表中告知社区。 也许其他人需要相同的功能。 他们可以使用您的代码、提供反馈并可能做出贡献。 如果对其存在足够的支持,也许您可以建议将其添加到官方 swift middleware。