Authentication Policy
This task covers the primary activities you might need to perform when enabling, configuring, and using Istio authentication policies. Find out more about the underlying concepts in the authentication overview.
Before you begin
Understand Istio authentication policy and related mutual TLS authentication concepts.
Install Istio on a Kubernetes cluster with the
default
configuration profile, as described in installation steps6.
Setup
Our examples use two namespaces foo
and bar
, with two services, httpbin
and curl
, both running with an Envoy proxy. We also use second
instances of httpbin
and curl
running without the sidecar in the legacy
namespace. If you’d like to use the same examples when trying the tasks,
run the following:
You can verify setup by sending an HTTP request with curl
from any curl
pod in the namespace foo
, bar
or legacy
to either httpbin.foo
,
httpbin.bar
or httpbin.legacy
. All requests should succeed with HTTP code 200.
For example, here is a command to check curl.bar
to httpbin.foo
reachability:
This one-liner command conveniently iterates through all reachability combinations:
Verify there is no peer authentication policy in the system with the following command:
Last but not least, verify that there are no destination rules that apply on the example services. You can do this by checking the host:
value of
existing destination rules and make sure they do not match. For example:
Auto mutual TLS
By default, Istio tracks the server workloads migrated to Istio proxies, and configures client proxies to send mutual TLS traffic to those workloads automatically, and to send plain text traffic to workloads without sidecars.
Thus, all traffic between workloads with proxies uses mutual TLS, without you doing
anything. For example, take the response from a request to httpbin/header
.
When using mutual TLS, the proxy injects the X-Forwarded-Client-Cert
header to the
upstream request to the backend. That header’s presence is evidence that mutual TLS is
used. For example:
When the server doesn’t have sidecar, the X-Forwarded-Client-Cert
header is not there, which implies requests are in plain text.
Globally enabling Istio mutual TLS in STRICT mode
While Istio automatically upgrades all traffic between the proxies and the workloads to mutual TLS,
workloads can still receive plain text traffic. To prevent non-mutual TLS traffic for the whole mesh,
set a mesh-wide peer authentication policy with the mutual TLS mode set to STRICT
.
The mesh-wide peer authentication policy should not have a selector
and must be applied in the root namespace, for example:
This peer authentication policy configures workloads to only accept requests encrypted with TLS.
Since it doesn’t specify a value for the selector
field, the policy applies to all workloads in the mesh.
Run the test command again:
You see requests still succeed, except for those from the client that doesn’t have proxy, curl.legacy
, to the server with a proxy, httpbin.foo
or httpbin.bar
. This is expected because mutual TLS is now strictly required, but the workload without sidecar cannot comply.
Cleanup part 1
Remove global authentication policy added in the session:
Enable mutual TLS per namespace or workload
Namespace-wide policy
To change mutual TLS for all workloads within a particular namespace, use a namespace-wide policy. The specification of the policy is the same as for a mesh-wide policy, but you specify the namespace it applies to under metadata
. For example, the following peer authentication policy enables strict mutual TLS for the foo
namespace:
As this policy is applied on workloads in namespace foo
only, you should see only request from client-without-sidecar (curl.legacy
) to httpbin.foo
start to fail.
Enable mutual TLS per workload
To set a peer authentication policy for a specific workload, you must configure the selector
section and specify the labels that match the desired workload. For example, the following peer authentication policy enables strict mutual TLS for the httpbin.bar
workload:
Again, run the probing command. As expected, request from curl.legacy
to httpbin.bar
starts failing with the same reasons.
To refine the mutual TLS settings per port, you must configure the portLevelMtls
section. For example, the following peer authentication policy requires mutual TLS on all ports, except port 8080
:
- The port value in the peer authentication policy is the container’s port.
- You can only use
portLevelMtls
if the port is bound to a service. Istio ignores it otherwise.
Policy precedence
A workload-specific peer authentication policy takes precedence over a namespace-wide policy. You can test this behavior if you add a policy to disable mutual TLS for the httpbin.foo
workload, for example.
Note that you’ve already created a namespace-wide policy that enables mutual TLS for all services in namespace foo
and observe that requests from
curl.legacy
to httpbin.foo
are failing (see above).
Re-running the request from curl.legacy
, you should see a success return code again (200), confirming service-specific policy overrides the namespace-wide policy.
Cleanup part 2
Remove policies created in the above steps:
End-user authentication
To experiment with this feature, you need a valid JWT. The JWT must correspond to the JWKS endpoint you want to use for the demo. This tutorial uses the test token JWT test9 and JWKS endpoint10 from the Istio code base.
Also, for convenience, expose httpbin.foo
via an ingress gateway (for more details, see the ingress task11).
Configure the gateway:
Follow the instructions in
Determining the ingress IP and ports
to define the INGRESS_PORT
and INGRESS_HOST
environment variables.
Create the gateway:
Set the INGRESS_PORT
and INGRESS_HOST
environment variables:
Run a test query through the gateway:
Now, add a request authentication policy that requires end-user JWT for the ingress gateway.
Apply the policy in the namespace of the workload it selects, the ingress gateway in this case.
If you provide a token in the authorization header, its implicitly default location, Istio validates the token using the public key set10, and rejects requests if the bearer token is invalid. However, requests without tokens are accepted. To observe this behavior, retry the request without a token, with a bad token, and with a valid token:
To observe other aspects of JWT validation, use the script gen-jwt.py
15 to
generate new tokens to test with different issuer, audiences, expiry date, etc. The script can be downloaded from the Istio repository:
You also need the key.pem
file:
The JWT authentication has 60 seconds clock skew, this means the JWT token will become valid 60 seconds earlier than
its configured nbf
and remain valid 60 seconds after its configured exp
.
For example, the command below creates a token that expires in 5 seconds. As you see, Istio authenticates requests using that token successfully at first but rejects them after 65 seconds:
You can also add a JWT policy to an ingress gateway (e.g., service istio-ingressgateway.istio-system.svc.cluster.local
).
This is often used to define a JWT policy for all services bound to the gateway, instead of for individual services.
Require a valid token
To reject requests without valid tokens, add an authorization policy with a rule specifying a DENY
action for requests without request principals, shown as notRequestPrincipals: ["*"]
in the following example. Request principals are available only when valid JWT tokens are provided. The rule therefore denies requests without valid tokens.
Retry the request without a token. The request now fails with error code 403
:
Require valid tokens per-path
To refine authorization with a token requirement per host, path, or method, change the authorization policy to only require JWT on /headers
. When this authorization rule takes effect, requests to $INGRESS_HOST:$INGRESS_PORT/headers
fail with the error code 403
. Requests to all other paths succeed, for example $INGRESS_HOST:$INGRESS_PORT/ip
.
Cleanup part 3
Remove authentication policy:
Remove authorization policy:
Remove the token generator script and key file:
If you are not planning to explore any follow-on tasks, you can remove all resources simply by deleting test namespaces.