Documentation Index
Fetch the complete documentation index at: https://docs.streamnative.io/llms.txt
Use this file to discover all available pages before exploring further.
You can configure JSON Web Token (JWT) authentication to a Pulsar cluster using asymmetric (RS256) signing. A private key signs tokens and a public key verifies them on the brokers and proxies, so the signing credential never needs to be distributed to cluster components.
Before you begin
-
Install the following tools.
Keep my-private.key strictly controlled. Anyone who holds the private key can issue valid tokens for any subject, including broker-admin and proxy-admin. Treat it with the same care as a root credential:
- Store it outside the Kubernetes cluster.
- Consider storing it in a secrets manager (for example, AWS Secrets Manager, HashiCorp Vault) and only retrieving it when issuing new tokens.
Generate the RSA256 key pair and tokens
Use pulsarctl to generate an RSA256 key pair:
pulsarctl token create-key-pair \
--output-private-key my-private.key \
--output-public-key my-public.key
Issue tokens for the broker-admin and proxy-admin subjects using the private key:
pulsarctl token create -a RS256 \
--private-key-file my-private.key \
--subject broker-admin
pulsarctl token create -a RS256 \
--private-key-file my-private.key \
--subject proxy-admin
Issue a token for the client subject:
pulsarctl token create -a RS256 \
--private-key-file my-private.key \
--subject client
Create Kubernetes Secrets for keys and tokens
Only the public key goes into Kubernetes. The private key must never be stored as a cluster Secret.
-
Create the public key Secret:
kubectl create secret generic token-public-key \
--from-file=my-public.key \
-n pulsar
-
Create the
broker-admin token Secret:
kubectl create secret generic broker-admin \
--from-literal=token=<broker-admin-token> \
-n pulsar
-
Create the
proxy-admin token Secret:
kubectl create secret generic proxy-admin \
--from-literal=token=<proxy-admin-token> \
-n pulsar
Replace <broker-admin-token> and <proxy-admin-token> with the tokens generated in the previous step.
Enable JWT authentication for the Pulsar cluster
Add the following configurations to the PulsarBroker object:
spec:
config:
clientAuth:
jwt:
secret: broker-admin
custom:
authenticationEnabled: 'true'
authenticateOriginalAuthData: 'true'
authenticationProviders: 'org.apache.pulsar.broker.authentication.AuthenticationProviderToken'
brokerClientAuthenticationPlugin: 'org.apache.pulsar.client.impl.auth.AuthenticationToken'
superUserRoles: 'broker-admin, proxy-admin'
proxyRoles: 'proxy-admin'
authorizationEnabled: 'true'
authorizationProvider: 'org.apache.pulsar.broker.authorization.PulsarAuthorizationProvider'
pod:
secretRefs:
- mountPath: /mnt/secrets
secretName: token-public-key
vars:
- name: brokerClientAuthenticationParameters
valueFrom:
secretKeyRef:
name: broker-admin
key: token
- name: tokenPublicKey
value: 'file:///mnt/secrets/my-public.key'
config.clientAuth: configures the toolset to mount and use the broker-admin token automatically.
config.custom: sets Pulsar authentication and authorization properties.
pod.secretRefs: mounts the public key Secret as a file inside the pod.
pod.vars: exposes the public key path and broker-admin token as environment variables for Pulsar configuration.
On the PulsarProxy object, add the following configurations:
spec:
config:
custom:
authenticationEnabled: 'true'
authenticateOriginalAuthData: 'true'
forwardAuthorizationCredentials: 'true'
authenticationProviders: 'org.apache.pulsar.broker.authentication.AuthenticationProviderToken'
brokerClientAuthenticationPlugin: 'org.apache.pulsar.client.impl.auth.AuthenticationToken'
superUserRoles: 'proxy-admin'
pod:
secretRefs:
- mountPath: /mnt/secrets
secretName: token-public-key
vars:
- name: brokerClientAuthenticationParameters
valueFrom:
secretKeyRef:
name: proxy-admin
key: token
- name: tokenPublicKey
value: 'file:///mnt/secrets/my-public.key'
config.custom: sets Pulsar authentication and authorization properties.
pod.secretRefs: mounts the public key Secret as a file inside the pod.
pod.vars: exposes the public key path and broker-admin token as environment variables for Pulsar configuration.
Connect clients to Pulsar
-
Create authorization for the client subject using the
broker-admin token:
pulsarctl --admin-service-url http://<Your PulsarProxy Endpoint>:8080 \
--token "<broker-admin-token>" \
namespaces grant-permission \
--role client \
--actions produce \
--actions consume \
public/default
Expected output:
Grant permissions [produce consume] to the client role client to access the namespace public/default successfully
If you are running this command from the toolset pod, pulsarctl reads /pulsar/conf/client.conf which already has the admin service URL and the broker-admin token configured. You can omit the --admin-service-url and --token flags:pulsarctl namespaces grant-permission \
--role client \
--actions produce \
--actions consume \
public/default
-
Produce messages with the client token:
bin/pulsar-client \
--url pulsar://<Your PulsarProxy Endpoint>:6650 \
--auth-plugin org.apache.pulsar.client.impl.auth.AuthenticationToken \
--auth-params "token:<client-token>" \
produce public/default/test -m "test" -n 10
-
Consume messages with the client token:
bin/pulsar-client \
--url pulsar://<Your PulsarProxy Endpoint>:6650 \
--auth-plugin org.apache.pulsar.client.impl.auth.AuthenticationToken \
--auth-params "token:<client-token>" \
consume public/default/test -s my-subscription -n 10
Verify the configuration
After the pods restart, confirm the broker is using the public key and not a shared secret:
kubectl exec -n pulsar <broker-pod> -- \
grep -E 'tokenPublicKey|tokenSecretKey' /pulsar/conf/broker.conf
Expected output:
tokenSecretKey=
tokenPublicKey=file:///mnt/secrets/my-public.key
tokenSecretKey must be empty. If it is set, the broker is using symmetric auth instead of RS256.
The Console CRD does not have a native plain-JWT field. Authentication is configured by injecting environment variables directly via pod.vars and mounting the public key via pod.secretRefs.
A single dedicated service account (consoleservice) is used for both the Console backend connection to the broker and the web login. This subject must be in superUserRoles on the broker so it can perform all admin operations, including listing clusters.
Issue the Console service account token
Issue a dedicated RS256 token for the consoleservice subject:
pulsarctl token create -a RS256 \
--private-key-file my-private.key \
--subject consoleservice
Create a Kubernetes Secret that contains both the token and the public key:
kubectl create secret generic consoleservice \
--from-literal=token=<consoleservice-token> \
--from-file=my-public.key \
-n pulsar
Replace <consoleservice-token> with the token generated above.
Add the Console service account to the broker
Update the PulsarBroker object to include consoleservice in superUserRoles:
spec:
config:
custom:
superUserRoles: 'broker-admin, proxy-admin, consoleservice'
Add the following to the Console object:
spec:
pod:
secretRefs:
- mountPath: /pulsar-manager/keys
secretName: consoleservice
vars:
- name: AUTHENTICATION_NAME
value: pulsar-jwt
- name: AUTHENTICATION_PROVIDER
value: org.apache.pulsar.manager.authentication.PulsarJWTAuthenticationProvider
- name: AUTHENTICATION_CUSTOM_CLAIM
value: sub
- name: SERVICE_ACCOUNT_NAME
value: jwt
- name: SERVICE_ACCOUNT_PROVIDER
value: org.apache.pulsar.manager.serviceAccount.JWTProvider
- name: AUTH_METHOD_NAME
value: pulsar-jwt
- name: AUTH_METHOD_PROVIDER
value: org.apache.pulsar.manager.authMethod.PulsarJWTAuthMethodProvider
- name: AUTH_METHOD_CUSTOM_CLAIM
value: sub
- name: USERNAME_CLAIM
value: sub
- name: JWT_BROKER_TOKEN_MODE
value: PRIVATE
- name: JWT_BROKER_PUBLIC_KEY_PATH
value: /pulsar-manager/keys/my-public.key
- name: TOKEN
valueFrom:
secretKeyRef:
name: consoleservice
key: token
- name: BACKEND_DEFAULT_SUPER_USER_ROLE
value: consoleservice