This is an example of setting up auth for seldon core model deployments in an istio enabled kubernetes cluster. Here we discuss the following topics:
- Authentication at Seldon Deployment Component Level
- Authorization based on user id token claims
- Authentication at the Ingress Level
- Kubernetes Cluster (~1.14)
- Istio 1.5
- Helm v3
- Install and setup Istio 1.5 with istioctl as mentioned in the docs. For this demo we have used the demo profile as shown.
istioctl manifest apply --set profile=demo
- Create namespace
foo
and setup an istio gateway
kubectl create namespace foo
kubectl label namespace foo istio-injection=enabled
kubectl apply -f ../../notebooks/resources/seldon-gateway.yaml
- Create a
seldon-system
namespace and install Seldon Core using Helm.
kubectl create namespace seldon-system
helm install seldon-core ../../helm-charts/seldon-core-operator/ --namespace seldon-system --set istio.enabled=true
- Deploy an iris model by applying the seldon manifest shown below,
kubectl apply -f iris.yaml
- Make a prediction after the Seldon Deployment is available via the ingress gateway created.
export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl -X POST http://$INGRESS_HOST/seldon/foo/iris-model/api/v1.0/predictions \
-H 'Content-Type: application/json' \
-d '{ "data": { "ndarray": [[1,2,3,4]] } }'
- Response
{
"data": {
"names": ["t:0", "t:1", "t:2"],
"ndarray": [
[0.0006985194531162841, 0.003668039039435755, 0.9956334415074478]
]
},
"meta": {}
}
- Setup Istio RequestAuthentication as follows:
kubectl apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "RequestAuthentication"
metadata:
name: seldon-auth-example
namespace: foo
spec:
selector:
matchLabels:
seldon-deployment-id : iris-model
jwtRules:
- issuer: "[email protected]"
jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.5/security/tools/jwt/samples/jwks.json"
outputPayloadToHeader: "Seldon-Core-User"
EOF
- Verify that invalid token requests get blocked and returns a
401 Unauthorized
status code
curl -X POST http://$INGRESS_HOST/seldon/foo/iris-model/api/v1.0/predictions \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer invalidToken" \
-d '{ "data": { "ndarray": [[1,2,3,4]] } }' \
-o /dev/null -s -w "%{http_code}"
- Response Code
401 Unauthorized
- Setup an istio AuthorizationPolicy to validate the requestPrincipal. Istio constructs the requestPrincipal by combining the iss and sub of the JWT token with a / separator as shown:
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: seldon-auth-example
namespace: foo
spec:
selector:
matchLabels:
seldon-deployment-id : iris-model
action: ALLOW
rules:
- from:
- source:
requestPrincipals: ["[email protected]/[email protected]"]
EOF
- Verify that requests without a token get blocked and returns a
403 Forbidden
status code
curl -X POST http://$INGRESS_HOST/seldon/foo/iris-model/api/v1.0/predictions \
-H 'Content-Type: application/json' \
-d '{ "data": { "ndarray": [[1,2,3,4]] } }' \
-o /dev/null -s -w "%{http_code}"
- Response Code
403 Forbidden
- Fetch a token to make requests.
TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.5/security/tools/jwt/samples/demo.jwt -s) && echo $TOKEN | cut -d '.' -f2 - | base64 --decode -
- Response
{
"exp": 4685989700,
"foo": "bar",
"iat": 1532389700,
"iss": "[email protected]",
"sub": "[email protected]"
}
- Verify that requests with this valid token passes through successfully.
curl -X POST http://$INGRESS_HOST/seldon/foo/iris-model/api/v1.0/predictions \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $TOKEN" \
-d '{ "data": { "ndarray": [[1,2,3,4]] } }' \
-o /dev/null -s -w "%{http_code}"
- Response Code
200 OK
- Also we can deny all invalid requests (without a valid decodable token) by adding the following rule to the authorization policy
action: DENY
rules:
- from:
- source:
notRequestPrincipals: ["*"]
-
This can be extended to Seldon deployment component level by selecting specific components by matching labels.
-
Further the seldon core executor/engine can base64 decode the user claims from the header
Seldon-Core-User
as configured in the RequestAuthentication with as outputPayloadToHeader.
- Setup and authorization policy to enable only users in group
group1
to make predictions (Group level authentication)
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: seldon-auth-example
namespace: foo
spec:
selector:
matchLabels:
seldon-deployment-id : iris-model
action: ALLOW
rules:
- from:
- source:
requestPrincipals: ["[email protected]/[email protected]"]
when:
- key: request.auth.claims[groups]
values: ["group1"]
EOF
- Verify that the user not beloging to the group is forbidden.
curl -X POST http://$INGRESS_HOST/seldon/foo/iris-model/api/v1.0/predictions \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $TOKEN" \
-d '{ "data": { "ndarray": [[1,2,3,4]] } }' \
-o /dev/null -s -w "%{http_code}"
- Response Code
403 Forbidden
- Fetch a new user token which has
group1
in the group claims and verify that the request is allowed
TOKEN_WITH_GROUP=$(curl https://raw.githubusercontent.com/istio/istio/release-1.5/security/tools/jwt/samples/groups-scope.jwt -s) && echo $TOKEN_WITH_GROUP | cut -d '.' -f2 - | base64 --decode -
- Response
{
"exp": 3537391104,
"groups": ["group1", "group2"],
"iat": 1537391104,
"iss": "[email protected]",
"scope": ["scope1", "scope2"],
"sub": "[email protected]"
}
curl -X POST http://$INGRESS_HOST/seldon/foo/iris-model/api/v1.0/predictions \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $TOKEN_WITH_GROUP" \
-d '{ "data": { "ndarray": [[1,2,3,4]] } }' \
-o /dev/null -s -w "%{http_code}"
- Response Code
200 OK
- Cleanup
kubectl -n foo delete authorizationpolicies.security.istio.io seldon-auth-example
kubectl -n foo delete requestauthentication seldon-auth-example
Similarly, you can also setup RequestAuthentication and AuthorizationPolicy at the ingress gateway level by changing the selector
- Setup request Authentication as follow:
kubectl apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "RequestAuthentication"
metadata:
name: seldon-auth-example
namespace: istio-system
spec:
selector:
matchLabels:
istio: ingressgateway
jwtRules:
- issuer: "[email protected]"
jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.5/security/tools/jwt/samples/jwks.json"
outputPayloadToHeader: "Seldon-Core-User"
EOF
- Here is an example of an Authorization policy that denies DELETE methods to the
/seldon
path at the ingress level
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: seldon-auth-example
namespace: istio-system
spec:
selector:
matchLabels:
istio: ingressgateway
action: DENY
rules:
- to:
- operation:
methods: ["DELETE"]
paths: ["/seldon"]
EOF
- Verify that the DELETE method is not allowed for the request
curl -X DELETE http://$INGRESS_HOST/seldon/foo/iris-model/api/v1.0/predictions \
-H 'Content-Type: application/json' \
-d '{ "data": { "ndarray": [[1,2,3,4]] } }' \
-o /dev/null -s -w "%{http_code}"
- Response Code
405 Method Not Allowed
- Cleanup
kubectl -n istio-system delete authorizationpolicies.security.istio.io seldon-auth-example
kubectl -n istio-system delete requestauthentication seldon-auth-example
kubectl delete namespace foo