Customizing Object Storage (Swift) Middleware
OpenStack Object Storage, known as swift when reading the code, is based on the Python Paste framework. The best introduction to its architecture is A Do-It-Yourself Framework. Because of the swift project’s use of this framework, you are able to add features to a project by placing some custom code in a project’s pipeline without having to change any of the core code.
Imagine a scenario where you have public access to one of your containers, but what you really want is to restrict access to that to a set of IPs based on a whitelist. In this example, we’ll create a piece of middleware for swift that allows access to a container from only a set of IP addresses, as determined by the container’s metadata items. Only those IP addresses that you explicitly whitelist using the container’s metadata will be able to access the container.
When you join the screen session that
stack.sh starts with
screen -r stack, you see a screen for each service running, which can be a few or several, depending on how many services you configured DevStack to run.
The asterisk * indicates which screen window you are viewing. This example shows we are viewing the key (for keystone) screen window:
The purpose of the screen windows are as follows:
- A shell where you can get some work done
- The keystone service
- The horizon dashboard web application
- The swift services
To create the middleware and plug it in through Paste configuration:
All of the code for OpenStack lives in
/opt/stack. Go to the swift directory in the
shell screen and edit your middleware module.
- Change to the directory where Object Storage is installed:
ip_whitelist.pyPython source code file:
Copy the code as shown below into
ip_whitelist.py. The following code is a middleware example that restricts access to a container based on IP address as explained at the beginning of the section. Middleware passes the request on to another application. This example uses the swift “swob” library to wrap Web Server Gateway Interface (WSGI) requests and responses into objects for swift to interact with. When you’re done, save and close the file.
There is a lot of useful information in
confthat you can use to decide what to do with the request. To find out more about what properties are available, you can insert the following log statement into the
and the following log statement into the
To plug this middleware into the swift Paste pipeline, you edit one configuration file,
/etc/swift/proxy-server.conf, and copy in the following configuration section after it:
[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, and add
ip_whitelistafter ratelimit to the list like so. When you’re done, save and close the file:
[pipeline:main] pipeline = catch_errors gatekeeper healthcheck proxy-logging cache bulk tempurl ratelimit ip_whitelist ...
swift proxyservice to make swift use your middleware. Start by switching to the
- Press Ctrl+A followed by 3.
- Press Ctrl+C to kill the service.
- Press Up Arrow to bring up the last command.
- Press Enter to run it.
Test your middleware with the
swiftCLI. Start by switching to the shell screen and finish by switching back to the
swift-proxyscreen to check the log output:
- Press Ctrl+A followed by 0.
Make sure you’re in the
- Source openrc to set up your environment variables for the CLI:
Create a container called
- <Press Ctrl+A followed by 3 to check the log output.
Among the log statements you’ll see the lines:
proxy-server Remote IP: my.instance.ip.address (txn: ...) proxy-server Allow IPs: set(['my.instance.ip.address']) (txn: ...)
These two statements are produced by our middleware and show that the request was sent from our DevStack instance and was allowed.
Test the middleware from outside DevStack on a remote machine that has access to your DevStack instance:
swiftclients on your local machine:
Attempt to list the objects in the
$ 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!
- Install the
Press Ctrl+A followed by 3 to check the log output. Look at the swift log statements again, and among the log statements, you’ll see the lines:
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: ...)
Here we can see that the request was denied because the remote IP address wasn’t in the set of allowed IPs.
Back in your DevStack instance on the shell screen, add some metadata to your container to allow the request from the remote machine:
- Press Ctrl+A followed by 0.
- Add metadata to the container to allow the IP:
- Now try the command from Step 10 again and it succeeds. There are no objects in the container, so there is nothing to list; however, there is also no error to report.
You can follow a similar pattern in other projects that use the Python Paste framework. Simply create a middleware module and plug it in through configuration. The middleware runs in sequence as part of that project’s pipeline and can call out to other services as necessary. No project core code is touched. Look for a
pipeline value in the project’s
ini configuration files in
/etc/<project> to identify projects that use Paste.
When your middleware is done, we encourage you to open source it and let the community know on the OpenStack mailing list. Perhaps others need the same functionality. They can use your code, provide feedback, and possibly contribute. If enough support exists for it, perhaps you can propose that it be added to the official swift middleware.