Blog / Defense-In-Depth / Using Istio & OpenID Connect / OAUTH2 To Authorise

Using Istio & OpenID Connect / OAUTH2 To Authorise

We have a large number of management only services ( kibana , grafana , prometheus , alertmanager , etc.). I want to make it very easy for developers to light up new ones, but also very secure. More specifically, I want to make it easier to be secure than to be insecure. Many breaches happen because a development-only thing is forgotten online. If you have a system where TLS + Authentication + Authorization is easy to do, and on-by-default, then you don’t have to worry (as much).

The method we have settled on here at Agilicus is to have *.admin.DOMAIN be universally managed by OpenID Connect-based (OAUTH2) login. This means that all services XXX.admin get an automatic TLS certificate, an automatic authentication. Without any integration. Without any effort.

How did we do this? The magic of Istio and Service Mesh. I’ll share the YAML below. But, in a nutshell, we run an oauth2_proxy for each domain we run (.ca, .com, .dev). This is integrated with our G Suite login. I talked more about it here.

The key to the operation is a wee bit of Lua code in the filter. You will see we instantiate this for 3 entries (.ca/.com/.dev). All development occurs in .dev (which further guarantees it will be secured & encrypted since .dev is in the HSTS preload list entirely). Even without that, we added our domains to the preload list, guaranteeing that nobody forgets the encrypted-only memo.

---
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: authn-filter
spec:
workloadSelector:
labels:
app: istio-ingressgateway
configPatches:
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
portNumber: 443
patch:
operation: INSERT_BEFORE
value:
name: envoy.lua
config:
inlineCode: |
base_host = string.upper("admin.__ROOT_DOMAIN__")
a, oauth_host = string.match("admin.__ROOT_DOMAIN__", '(admin.*%.)(.*%..*)')
oauth_host = "oauth2." .. oauth_host
function starts_with(str, start)
return str:sub(1, #start) == start
end
function ignore_request (request_handle)
local host = string.upper(request_handle:headers():get(":authority"))
local path = request_handle:headers():get(":path")
-- we skip un-protected hosts or 'well-known' paths (used for e.g. acme)
i, j = string.find(host, base_host, 0, true)
if i == nil or starts_with(path, '/.well-known/') then
return true
end
-- request_handle:logWarn("Host protected")
return false
end
function login (request_handle)
local request_url = "https://"..request_handle:headers():get(":authority")..request_handle:headers():get(":path")
local redirect_url = "https://"..oauth_host.."/oauth2/start?rd="..request_url
headers = {
[":status"] = 302,
["location"] = redirect_url
}
request_handle:respond(headers, "")
end
function envoy_on_request(request_handle)
if ignore_request(request_handle) then
return
end
cookie = request_handle:headers():get("Cookie")
if cookie == nil then
-- request_handle:logWarn("login")
login(request_handle)
return
end
-- request_handle:logWarn("validating token against /ouath2/auth")
local headers, body = request_handle:httpCall(
"outbound|443||"..oauth_host,
{
[":method"] = "GET",
[":path"] = "/oauth2/auth",
[":authority"] = oauth_host,
["Cookie"] = cookie
},
nil,
5000)
local status
for header, value in pairs(headers) do
if header == ":status" then
status = value
end
end
-- request_handle:logWarn("token validation status:"..status)
if status ~= "202" then
-- request_handle:logWarn("Not validated")
login(request_handle)
return
end
end
---
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: oauth2
spec:
hosts:
- oauth2.MYDOMAIN.ca
- oauth2.MYDOMAIN.com
- oauth2.MYDOMAIN.dev
ports:
- number: 443
name: https-for-tls
protocol: HTTPS
resolution: DNS
location: MESH_EXTERNAL
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: oauth2-mydomain-ca
spec:
host: oauth2.MYDOMAIN.ca
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
portLevelSettings:
- port:
number: 443
tls:
mode: SIMPLE 
sni: oauth2.MYDOMAIN.ca
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: oauth2-mydomain-com
spec:
host: oauth2.MYDOMAIN.com
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
portLevelSettings:
- port:
number: 443
tls:
mode: SIMPLE  
sni: oauth2.MYDOMAIN.com
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: oauth2-MYDOMAIN-dev
spec:
host: oauth2.MYDOMAIN.dev
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
portLevelSettings:
- port:
number: 443
tls:
mode: SIMPLE  
sni: oauth2.MYDOMAIN.dev

Leave a Reply

Your email address will not be published.