Skip to content

Commit

Permalink
Merge branch 'master' into pathrouting-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Carlo Palmieri authored May 18, 2020
2 parents 1419c35 + 357f0d4 commit d559394
Show file tree
Hide file tree
Showing 19 changed files with 728 additions and 18 deletions.
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,12 @@ executors:
environment:
S2I_VERSION: "1.1.12-2a783420"
DOCKER_COMPOSE_VERSION: "1.16.1"
OPENRESTY_VERSION: "1.17.4.1-0-centos8"
OPENRESTY_VERSION: "1.17.5.1-0-centos8"

openresty:
working_directory: /opt/app-root/apicast
docker:
- image: quay.io/3scale/s2i-openresty-centos7:1.17.4.1-0-centos8
- image: quay.io/3scale/s2i-openresty-centos7:1.17.5.1-0-centos8
- image: redis:3.2.8-alpine
environment:
TEST_NGINX_BINARY: openresty
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

- Fixed issues with liquid replaces [THREESCALE-4937](https://issues.jboss.org/browse/THREESCALE-4937) [PR #1185](https://github.com/3scale/APIcast/pull/1185)
- Fixed issues with path routing and query args [THREESCALE-5149](https://issues.redhat.com/browse/THREESCALE-5149) [PR #1190](https://github.com/3scale/APIcast/pull/1190)
- Fixed issues with HTTPS_PROXY and large bodies [THREESCALE-3863](https://issues.jboss.org/browse/THREESCALE-3863) [PR #1191](https://github.com/3scale/APIcast/pull/1191)


### Added

- Added upstream Mutual TLS policy [THREESCALE-672](https://issues.jboss.org/browse/THREESCALE-672) [PR #1182](https://github.com/3scale/APIcast/pull/1182)
- Added Rate-limit headers policy [THREESCALE-3795](https://issues.jboss.org/browse/THREESCALE-3795) [PR #1166](https://github.com/3scale/APIcast/pull/1166)
- Added Content-caching policy [THREESCALE-2894](https://issues.jboss.org/browse/THREESCALE-2894) [PR #1182](https://github.com/3scale/APIcast/pull/1182)

Expand Down
4 changes: 4 additions & 0 deletions doc/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ It can be used to first load policies from a development directory or to load ex

The path to the key of the client SSL certificate.

This parameter can be overridden by the Upstream_TLS policy.

### `APICAST_PROXY_HTTPS_CERTIFICATE`

**Default:**
Expand All @@ -175,6 +177,8 @@ The path to the client SSL certificate that APIcast will use when connecting
with the upstream. Notice that this certificate will be used for all the
services in the configuration.

This parameter can be overridden by the Upstream_TLS policy.

### `APICAST_PROXY_HTTPS_PASSWORD_FILE`

**Default:**
Expand Down
2 changes: 1 addition & 1 deletion gateway/Roverfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ luarocks {
group 'testing' {
module { 'busted' },
module { 'luacov' },
module { 'ljsonschema' },
module { 'jsonschema' },
},

group 'development' {
Expand Down
2 changes: 1 addition & 1 deletion gateway/Roverfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ http 0.3-0||development
inspect 3.1.1-0||production
ldoc 1.4.6-2||development
liquid 0.1.5-1||production
ljsonschema 0.1.0-1||testing
jsonschema 0.8-0|aa4740624cca4c10585bd7d086b42aa0b9ab14fa|testing
lpeg 1.0.2-1||development
lpeg_patterns 0.5-0||development
lua-resty-env 0.4.0-1||production
Expand Down
21 changes: 21 additions & 0 deletions gateway/src/apicast/http_proxy.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ local resty_url = require "resty.url"
local resty_resolver = require 'resty.resolver'
local round_robin = require 'resty.balancer.round_robin'
local http_proxy = require 'resty.http.proxy'
local file_reader = require("resty.file").file_reader

local _M = { }

Expand Down Expand Up @@ -96,10 +97,30 @@ local function forward_https_request(proxy_uri, uri)
-- We cannot use resty.http's .get_client_body_reader().
-- In POST requests with HTTPS, the result of that call is nil, and it
-- results in a time-out.
--
--
-- If ngx.req.get_body_data is nil, can be that the body is too big to
-- read and need to be cached in a local file. This request will return
-- nil, so after this we need to read the temp file.
-- https://github.com/openresty/lua-nginx-module#ngxreqget_body_data
body = ngx.req.get_body_data(),
proxy_uri = proxy_uri
}

if not request.body then
local temp_file_path = ngx.req.get_body_file()
ngx.log(ngx.INFO, "HTTPS Proxy: Request body is bigger than client_body_buffer_size, read the content from path='", temp_file_path, "'")

if temp_file_path then
local body, err = file_reader(temp_file_path)
if err then
ngx.log(ngx.ERR, "HTTPS proxy: Failed to read temp body file, err: ", err)
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
request.body = body
end
end

local httpc, err = http_proxy.new(request)

if not httpc then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function _M:access(context)
-- This is because `proxy_no_cache` directive is used, so we need to make
-- the negative here.
ngx.var.cache_request = (rule.cache and "" or "true")
if rule.header then
if rule.header and rule.header ~= "" then
context[self] = {header = rule.header}
end
return
Expand Down
2 changes: 1 addition & 1 deletion gateway/src/apicast/policy/rate_limit/apicast-policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@
"window": {
"type": "integer",
"description": "The time window in seconds before the request count is reset",
"exclusiveMinimum": 0,
"minimum": 0,
"default": 1
}
}
Expand Down
44 changes: 44 additions & 0 deletions gateway/src/apicast/policy/upstream_mtls/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Upstream Mutual TLS policy

This policy enables the Mutual TLS policy per API, so connection to the upstream
API will use the certificates defined in this policy.

## Configuration

### Path configuration

Using certificates Path, both for Openshift and Kubernetes secrets.

```
{
"name": "apicast.policy.upstream_mtls",
"configuration": {
"certificate": "/secrets/client.cer",
"certificate_type": "path",
"certificate_key": "/secrets/client.key",
"certificate_key_type": "path"
}
}
```

### Embedded configuration

When using http forms and file upload

```
{
"name": "apicast.policy.upstream_mtls",
"configuration": {
"certificate_type": "embedded",
"certificate_key_type": "embedded",
"certificate": "data:application/pkix-cert;name=client.cer;base64,XXXXXXXXXxx",
"certificate_key": "data:application/x-iwork-keynote-sffkey;name=client.key;base64,XXXXXXXX"
}
}
```

## Additional considerations

This policy overwrites `APICAST_PROXY_HTTPS_CERTIFICATE_KEY` and
`APICAST_PROXY_HTTPS_CERTIFICATE` values and it uses the certificates set by
the policy, so those environment variables will have no effect.
92 changes: 92 additions & 0 deletions gateway/src/apicast/policy/upstream_mtls/apicast-policy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
{
"$schema": "http://apicast.io/policy-v1.1/schema#manifest#",
"name": "Upstream Mutual TLS",
"summary": "Certificates to be used with the upstream API",
"description": "With this policy a new TLS connection with the upstream API will be used with the certificates set in the config",
"version": "builtin",
"configuration": {
"title": "Upstream MTLS",
"description": "Built-in Upstream MTLS APIcast policy",
"type": "object",
"required": [
"certificate_type",
"certificate_key_type"
],
"properties": {
"certificate_type": {
"title": "Certificate type",
"type": "string",
"enum": [
"path",
"embedded"
],
"default": "path"
},
"certificate_key_type": {
"title": "Certificate key type",
"type": "string",
"enum": [
"path",
"embedded"
],
"default": "path"
}
},
"dependencies": {
"certificate_type": {
"oneOf": [
{
"properties": {
"certificate_type": {
"const": "embedded"
},
"certificate": {
"title": "Certificate",
"format": "data-url",
"type": "string"
}
}
},
{
"properties": {
"certificate_type": {
"const": "path"
},
"certificate": {
"title": "Certificate",
"type": "string"
}
}
}
]
},
"certificate_key_type": {
"oneOf": [
{
"properties": {
"certificate_key_type": {
"const": "embedded"
},
"certificate_key": {
"title": "Certificate Key",
"format": "data-url",
"type": "string"
}
}
},
{
"properties": {
"certificate_key_type": {
"const": "path"
},
"certificate_key": {
"title": "Certificate Key",
"type": "string"
}
}
}
]
}
}
}
}
1 change: 1 addition & 0 deletions gateway/src/apicast/policy/upstream_mtls/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
return require("upstream_mtls")
123 changes: 123 additions & 0 deletions gateway/src/apicast/policy/upstream_mtls/upstream_mtls.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
-- This policy enables MTLS with the upstream API endpoint

local ssl = require('ngx.ssl')
local ffi = require "ffi"
local base = require "resty.core.base"
local data_url = require('resty.data_url')

local C = ffi.C
local get_request = base.get_request
local open = io.open


ffi.cdef([[
int ngx_http_apicast_ffi_set_proxy_cert_key(
ngx_http_request_t *r, void *cdata_chain, void *cdata_key);
]])


local policy = require('apicast.policy')
local _M = policy.new('mtls', "builtin")

local path_type = "path"
local embedded_type = "embedded"

local new = _M.new


local function read_file(path)
ngx.log(ngx.DEBUG, "reading path:", path)

local file = open(path, "rb")
if not file then
ngx.log(ngx.ERR, "Cannot read path: ", path)
return nil
end

local content = file:read("*a")
file:close()
return content
end


local function get_cert(value, value_type)
if value_type == path_type then
return read_file(value)
end

if value_type == embedded_type then
local parsed_data, err = data_url.parse(value)
if err then
ngx.log(ngx.ERR, "Cannot parse certificate content: ", err)
return nil
end
return parsed_data.data
end
end

local function read_certificate(value, value_type)
local data = get_cert(value, value_type)
if data == nil then
ngx.log(ngx.ERR, "Certificate value is invalid")
return
end
return ssl.parse_pem_cert(data)
end

local function read_certificate_key(value, value_type)

local data = get_cert(value, value_type)

if data == nil then
ngx.log(ngx.ERR, "Certificate value is invalid")
return
end

if data == nil then
ngx.log(ngx.ERR, "Certificate key value is invalid")
return
end

return ssl.parse_pem_priv_key(data)

end

function _M.new(config)
local self = new(config)
if config == nil then
config = {}
end

self.cert = read_certificate(
config.certificate,
config.certificate_type or path_type)
self.cert_key = read_certificate_key(
config.certificate_key,
config.certificate_key_type or path_type)
return self
end


-- Set the certs for the upstream connection. Need to receive the pointers from
-- parse_* functions.
--- Public function to be able to unittest this.
function _M.set_certs(cert, key)
local r = get_request()
if not r then
ngx.log(ngx.ERR, "Invalid request")
return
end

local val = C.ngx_http_apicast_ffi_set_proxy_cert_key(r, cert, key)
if val ~= ngx.OK then
ngx.log(ngx.ERR, "Certificate cannot be set correctly")
end
end

function _M:balancer(context)
if self.cert and self.cert_key then
self.set_certs(self.cert, self.cert_key)
end
end

return _M
Loading

0 comments on commit d559394

Please sign in to comment.