Note
This is a new feature in OpenStack Liberty.
OpenStack supports Cross-Origin Resource Sharing (CORS), a W3C specification defining a contract by which the single-origin policy of a user agent (usually a browser) may be relaxed. It permits it’s javascript engine to access an API that does not reside on the same domain, protocol, or port.
This feature is most useful to organizations which maintain one or more custom user interfaces for OpenStack, as it permits those interfaces to access the services directly, rather than requiring an intermediate proxy server. It can, however, also be misused by malicious actors; please review the security advisory below for more information.
Note
Both the Object Storage and dashboard projects provide CORS support that is not covered by this document. For those, please refer to their respective implementations:
In most cases, CORS support is built directly into the service itself. To enable it, simply follow the configuration options exposed in the default configuration file, or add it yourself according to the pattern below.
1 2 3 4 5 6 | [cors]
allowed_origin = https://first_ui.example.com
max_age = 3600
allow_methods = GET,POST,PUT,DELETE
allow_headers = Content-Type,Cache-Control,Content-Language,Expires,Last-Modified,Pragma,X-Custom-Header
expose_headers = Content-Type,Cache-Control,Content-Language,Expires,Last-Modified,Pragma,X-Custom-Header
|
This method also enables you to define multiple origins. To express this in your configuration file, first begin with a [cors] group as above, into which you place your default configuration values. Then, add as many additional configuration groups as necessary, naming them [cors.{something}] (each name must be unique). The purpose of the suffix to cors. is legibility, we recommend using a reasonable human-readable string:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | [cors.ironic_webclient]
# CORS Configuration for a hypothetical ironic webclient, which overrides
# authentication
allowed_origin = https://ironic.example.com:443
allow_credentials = True
[cors.horizon]
# CORS Configuration for horizon, which uses global options.
allowed_origin = https://horizon.example.com:443
[cors.wildcard]
# CORS Configuration for the CORS specified domain wildcard, which only
# permits HTTP GET requests.
allowed_origin = *
allow_methods = GET
|
In other services, CORS is configured via PasteDeploy. In this case, you must first make sure that OpenStack’s oslo_middleware package (version 2.4.0 or later) is available in the Python environment that is running the service. Then, add the following configuration block to your paste.ini file.
1 2 3 4 5 6 7 | [filter:cors]
paste.filter_factory = oslo_middleware.cors:filter_factory
allowed_origin = https://website.example.com:443
max_age = 3600
allow_methods = GET,POST,PUT,DELETE
allow_headers = Content-Type,Cache-Control,Content-Language,Expires,Last-Modified,Pragma,X-Custom-Header
expose_headers = Content-Type,Cache-Control,Content-Language,Expires,Last-Modified,Pragma,X-Custom-Header
|
Note
To add another domain, simply add another filter.
CORS specifies a wildcard character *, which permits access to all user agents, regardless of domain, protocol, or host. While there are valid use cases for this approach, it also permits a malicious actor to create a convincing facsimile of a user interface, and trick users into revealing authentication credentials. Please carefully evaluate your use case and the relevant documentation for any risk to your organization.
Note
The CORS specification does not support using this wildcard as a part of a URI. Setting allowed-origin to * would work, while *.openstack.org would not.
CORS is very easy to get wrong, as even one incorrect property will violate the prescribed contract. Here are some steps you can take to troubleshoot your configuration.
The CORS middleware used by OpenStack provides verbose debug logging that should reveal most configuration problems. Here are some example log messages, and how to resolve them.
Most browsers provide helpful debug output when a CORS request is rejected. Usually this happens when a request was successful, but the return headers on the response do not permit access to a property which the browser is trying to access.
By using curl or a similar tool, you can trigger a CORS response with a properly constructed HTTP request. An example request and response might look like this.
Request:
$ curl -I -X OPTIONS https://api.example.com/api -H "Origin: https://ui.example.com"
Response:
HTTP/1.1 204 No Content
Content-Length: 0
Access-Control-Allow-Origin: https://ui.example.com
Access-Control-Allow-Methods: GET,POST,PUT,DELETE
Access-Control-Expose-Headers: origin,authorization,accept,x-total,x-limit,x-marker,x-client,content-type
Access-Control-Allow-Headers: origin,authorization,accept,x-total,x-limit,x-marker,x-client,content-type
Access-Control-Max-Age: 3600
If the service does not return any access control headers, check the service log, such as /var/log/upstart/ironic-api.log for an indication on what went wrong.
Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License http://creativecommons.org/licenses/by/3.0/legalcode.