This role installs and configures Kong.
Please refer to Kong documentation for further information on Routes, Services, Consumer and Plugins configuration.
Breaking Changes:
- new `kong_route_config` variable introduced to decouple service and routes config previously in `kong_service_config` - structure of `kong_service_config` updated (Breaking change)
For the last version without breaking changes above please use tag v1.9
WARNING:
- Support for v0.12.x and earlier deprecated and will be removed SOON!!
- hosts: konghost
vars:
kong_version: 0.13.1
kong_cassandra_host: <my_cassandra_ip_or_fqdn>
## OR for postgres backend
## kong_database: postgres
## kong_pg_host: <my_pg_ip_or_fqdn>
roles:
- o2-priority.kong
- hosts: my-kong-host
vars:
kong_version: 0.13.1
kong_use_old_config_format: false
roles:
#*************************#
# SERVICES & ROUTES #
#*************************#
- role: ansible-kong ## ADD/UPDATE service obj for svcOne service
kong_task: service
kong_service_config:
name: svcOne
url: "https://service-upstream.ogonna.com/svcOne/api"
- role: ansible-kong ## ADD route obj for svcOne
kong_task: route
kong_route_config:
name: svcOneRoute1
service: svcOne
paths: [ "/svcOne" ]
hosts: [ "og.com", "ab.com" ]
- role: ansible-kong ## ADD route obj for svcOne
kong_task: route
kong_route_config:
name: svcOneRoute2
service: svcOne
paths: [ "/svcOnePlus" ]
methods: [ "GET", "POST", "PUT" ]
- role: ansible-kong ## DELETE service obj for svcThree
kong_task: service
kong_delete_service_obj: true
kong_service_config:
name: svcThree
#*************************#
# UPSTREAM & TARGETS #
#*************************#
- role: ansible-kong ## ADD/UPDATE upstream obj for svcOne upstream
kong_task: upstream
kong_upstream_config:
name: upstreamOne
slots: 1000
- role: ansible-kong ## ADD target obj for upstreamOne
kong_task: target
kong_target_config:
upstream: upstreamOne
target: targetOne
weight: 200
- role: ansible-kong ## DELETE upstreamOne with all targets
kong_task: upstream
kong_delete_upstream_obj: true
kong_upstream_config:
name: upstreamOne
#*****************#
# CONSUMERS #
#*****************#
- role: ansible-kong ## ADD/UPDATE consumer obj for consumerOne
kong_use_old_config_format: false
kong_task: consumer
kong_consumer_config:
username: consumerOne
custom_id: con-1111
- role: ansible-kong ## DELETE consumer obj for consumerTwo
kong_use_old_config_format: false
kong_task: consumer
kong_consumer_config:
username: consumerTwo
kong_delete_consumer_obj: true
- role: ansible-kong ## ADD/UPDATE consumer obj for consumerThree with plugin configs
kong_use_old_config_format: false
kong_task: consumer
kong_consumer_config:
username: consumerThree
custom_id: con-3333
plugins:
- name: acl
parameters:
groups: [ svcOne-user-group ]
- name: key-auth
parameters:
key: "e2f599f74fc4479681e6586a1e644768"
- name: oauth2
parameters:
name: amazing-service
client_id: AMAZING-CLIENT-ID
client_secret: AMAZING-CLIENT-SECRET
redirect_uri: http://amazing-domain/endpoint/
- name: basic-auth
parameters:
username: smith
password: bobSecret
- name: hmac-auth
parameters:
username: james
- name: jwt
parameters:
key: "9efdde658a1b4b6e869d57d35dc8d7fb"
secret: "1bf8825a9f0e44a0bfb18f7dacf5c43f"
algorithm: "HS256"
#****************#
# PLUGINS #
#****************#
- role: ansible-kong ## ADD rate-limiting plugin obj (global)
kong_task: plugin
kong_plugin_config:
name: rate-limiting
config: { minute: 50, hour: 500 }
kong_delete_plugin_obj: false
- role: ansible-kong ## DELETE rate-limiting plugin obj for svcOne service and consumerOne consumer
kong_task: plugin
kong_plugin_config:
name: rate-limiting
service: svcOne
consumer: consumerOne
config: { minute: 20, hour: 500 }
kong_delete_plugin_obj: true
- role: ansible-kong ## ADD plugin obj for svcOne service
kong_task: plugin
kong_plugin_config:
name: oauth2
service: svcOne
config:
enable_authorization_code: true
scopes: "email,phone,address"
mandatory_scope: true
- role: ansible-kong ## ADD plugin obj for svcOne service
kong_task: plugin
kong_plugin_config:
name: cors
service: svcOne
config:
origins: "*"
methods: "GET, POST, PATCH, PUT, DELETE"
headers: "Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Auth-Token, Access-Control-Allow-Origin, Authorization"
exposed_headers: "X-Auth-Token"
credentials: true
max_age: 3600
- role: ansible-kong ## ADD plugin obj for svcOne service
kong_task: plugin
kong_plugin_config:
name: basic-auth
service: svcOne
config: { hide_credentials: true }
- role: ansible-kong ## ADD plugin obj for svcOne service
kong_task: plugin
kong_plugin_config:
name: key-auth
service: svcOne
config: { key_names: X-Api-Access-Key }
- role: ansible-kong ## ADD plugin obj for svcOne service
kong_task: plugin
kong_plugin_config:
name: acl
service: svcOne
config: { whitelist: "svcOne-user-group, another-user-group" }
Kong uses different algorithms to calculate how to go from a Request -> Route -> Service -> Proxy Request.
Currently we use v0
with strip_path=true
for all Routes.
service.path | route.path | route.strip_path | route.path_handling | request path | proxied path |
---|---|---|---|---|---|
/s | /tv0/ | true | v0 | /tv0/req | /s/req |
Most matching we do is via the Request Path,
we typlically don't match based on host
or method
, therefore "a client request's path must be prefixed with one of the values of the paths attribute."
# | request path | route.path | service.path | proxied path |
---|---|---|---|---|
1 | /reporting-service/realtime | [/reporting-service, /reporting-service/realtime] | /reporting-service/reporting | /reporting-service/reporting |
2 | /reporting-service/personalcontent | [/reporting-service, /reporting-service/realtime] | /reporting-service/reporting | /reporting-service/reporting/personalcontent |
3 | /reporting-service/realtime | [/reporting-service, /realtime] | /reporting-service/reporting | /reporting-service/reporting/realtime |
4 | /reporting-service/restricted/realtime | [/reporting-service [1], /reporting-service/restricted [2]] | /reporting-service/reporting | /reporting-service/reporting/realtime |
Request #1:
/reporting-service/realtime
matches 2nd Route path- Intermediary path is
[/reporting-service/reporting][/reporting-service/realtime]
(Service path + Request path) strip_path=true
so/reporting-service/realtime
is removed (because this is the Route path which was matched) leaving/reporting-service/reporting
as the proxied path
Request #2:
/reporting-service/personalcontent
matches 1st Route path due to path matching rules- Intermediary path is
[/reporting-service/reporting][/reporting-service/personalcontent]
strip_path=true
so/reporting-service
is removed leaving/reporting-service/reporting/personalcontent
as the proxied path
Request #3:
/reporting-service/realtime
matches 1st Route path - the request path must be prefixed with one of the values of the paths attribute- Intermediary path is
[/reporting-service/reporting][/reporting-service/realtime]
strip_path=true
so/reporting-service
is removed leaving/reporting-service/reporting/realtime
as the proxied path
Request #4:
/reporting-service/restricted/realtime
matches 2nd Route path- Intermediary path is
[/reporting-service/reporting][/reporting-service/restricted/realtime]
strip_path=true
so/reporting-service/restricted
is removed leaving/reporting-service/reporting/realtime
as the proxied path- Different ACLs can be applied depending on Route matched [1] and [2]. See ACLs section below.
An ACL plugin can be applied to a Route, Service or Globally. The plugin precedence rules decide which plugin configuration to choose. Requests #3 and #4 above are matched by the 1st and 2nd Route paths respectively. Depending on which Route path is matched determines which ACLs are applied.
For example, in Request #3 /reporting-service
was the matched Route path, therefore any ACL plugins which have been applied to this Route object will be applied.
With Request #4 /reporting-service/restricted
was the matched path, therefore any ACL plugins associated with this Route object will be applied. Restricted ACL [2] is applied here.
All of the Routes configured for Services follow the pattern of having a catch-all Route path, e.g. /reporting-service
. This allows any API requests to be mapped to an Upstream without any addition configuration.
For example, creating Upstream endpoints personalcontent
or events/registration
will allow requests /reporting-service/personalcontent
and /reporting-service/events/registration
without any addition Kong configuration.
However, if a separate Route is applying a different ACL to a particular path, then it needs to be excluded from the catch-all Route.
- Route A -> paths
[/reporting-service]
(ACL 1
restrictions applied) - Route B -> paths
[/reporting-service/restricted]
(ACL 2
restrictions applied)
ACL 1 credentials |
-> | request /reporting-service/realtime |
-> | matches Route A | -> | Upstream /reporting-service/reporting/realtime |
ACL 2 credentials |
-> | request /reporting-service/restricted/realtime |
-> | matches Route B | -> | Upstream /reporting-service/reporting/realtime |
Because of Kong's strip_path
functionality, both requests map correctly to the Upstream service. However, we don't want ACL 1
credentials access to be able to request /reporting-service/realtime
, it should only be possible to request it via /reporting-service/restricted/realtime
using ACL 2
credentials.
The workaround for this, counterintuitively, is to add the path you want to restrict to the Route you don't want it to access
- Route A -> paths
[/reporting-service, /reporting-service/realtime]
(ACL 1
restrictions applied)
This shortcuts Kong's path matching and strip_path
functionality to produce the following invalid route:
ACL 1 credentials |
-> | request /reporting-service/realtime |
-> | matches Route A | -> | Upstream /reporting-service/reporting |
To run integration tests of this role
kitchen test --destroy=never && docker kill node0 node1 postgres && docker rm node0 node1 postgres
Note:
--destroy=never
must be supplied because two nodes are required to be running for all the tests to pass. Thedocker
commands remove the left over containers for the next platform run.
Note: Docker-in-Docker doesn't work on Linux (AUFS 'already stacked' error). The Postgres container runs locally (i.e. Kitchen host) rather than from inside the container started by Kitchen.
none