Applies to VAST Cluster 5.5.
Security in VAST Event Broker is set per Kafka view by your platform team: which connections the view accepts (unencrypted, TLS or both), how clients authenticate on each (none, SASL/PLAIN or mTLS), and whether identity policies decide what each user may do. This page explains what each choice means for your clients, gives the client settings for each mode, and lists the permissions to ask for.
Connections and authentication
A Kafka view can accept two kinds of connection, each on its own port, and sets an authentication method for each:
| Connection | Port | Authentication methods |
|---|---|---|
| Unencrypted | 9092 | None, or SASL/PLAIN |
| TLS | 9093 | None, SASL/PLAIN, or mTLS (5.5) |
A view can allow one kind or both. Each kind has exactly one method, so on TLS a client uses SASL/PLAIN or mTLS, not both. If a client uses a kind the view doesn't allow, the broker accepts the TCP connection and closes it at the first request, before it answers even the version request.
If your platform team sets nothing, a new Kafka view accepts unencrypted connections without authentication.
TLS
TLS uses one Kafka certificate for the whole cluster. The cluster gives clients IP addresses, not host names, so the certificate lists every virtual IP of every Kafka view's VIP pool as an IP address in its Subject Alternative Names. It's issued by an internal CA, because public CAs don't issue certificates for private IP addresses. Your client needs that CA's certificate: without it, the TLS handshake fails. Hostname verification, which confluent-kafka does by default, passes against the IP-address entries.
The broker negotiates TLS 1.2. A client that allows only TLS 1.3 fails the handshake.
To look at the certificate the broker presents:
openssl s_client -connect <vip>:9093 -showcerts
If your platform team adds another Kafka view later, they reissue the certificate with its virtual IPs. Clients keep working as long as they trust the same CA.
SASL/PLAIN
SASL/PLAIN works on both unencrypted and TLS connections. The user name and password are those of a VAST local user in the view's tenant. Active Directory and LDAP users aren't supported. S3 access keys don't work here.
Use SASL/PLAIN over TLS: on an unencrypted connection, the password crosses the network in clear text.
Things to know:
PLAINis the only SASL mechanism. The broker answers a request for SCRAM, GSSAPI (Kerberos) or OAUTHBEARER by listingPLAINas its only mechanism, then closes the connection.confluent-kafkalogsSASL authentication error: SaslAuthenticateRequest failed: Local: Broker transport failure.- When the view requires SASL/PLAIN, the broker answers only the version and SASL handshake requests before authentication. Any other request closes the connection, with no error code.
confluent-kafkalogsDisconnected: verify that security.protocol is correctly configured, broker might require SASL authentication. - When the view doesn't use SASL on that connection kind, a SASL handshake gets
ILLEGAL_SASL_STATE.confluent-kafkareports it as a broker transport failure. - A wrong password, or a user name that isn't a local user, gets
SASL_AUTHENTICATION_FAILEDwith no message.confluent-kafkalogsSASL authentication error:followed by nothing, and requests fail with_TRANSPORT. - If the client sets a SASL authorization identity, it must be the user name. Otherwise the broker answers
Authentication failed: SASL PLAIN authorization identity (authzid) must match authentication identity (authcid). - There's no re-authentication: a connection stays authenticated until it closes.
mTLS
With mTLS (5.5), the client authenticates with its own certificate on a TLS connection. Your platform team decides which CA to trust: one for the whole cluster, or up to two per tenant (a third is refused with "We cannot have more than 2 per tenant TLS certificates for protocol KAFKA.").
The certificate's common name (CN) is looked up as a VAST user name, and that user is who the connection acts as for permissions.
What happens to a connection without a usable certificate:
| Client certificate | What happens |
|---|---|
| Signed by a CA the cluster doesn't trust | The TLS handshake fails with unknown ca. |
| None | The TLS handshake completes, and the broker closes the connection at the first request. |
| CN isn't a VAST user | The same: the broker closes the connection at the first request. |
| Revoked in a revocation list your platform team uploaded for the CA | The TLS handshake fails with certificate revoked. |
With no certificate, or a CN that isn't a user, confluent-kafka logs Disconnected while requesting ApiVersion: might be caused by broker version < 0.10.
Revocation through CRL distribution points in certificates isn't supported. The platform team uploads a revocation list instead.
mTLS uses TLS 1.2, like the other TLS connections, so leave TLS 1.2 enabled in your client.
With mTLS, the broker views of several tenants can share one VIP pool. The cluster tells the tenants apart by the client certificate.
Client settings
These are confluent-kafka (librdkafka) settings. Add them to the configuration of every producer, consumer and admin client. Bootstrap addresses use the port of the connection kind: 9092 or 9093. Each configuration below was used to produce, consume and run admin calls against a view in that mode.
No authentication, unencrypted:
conf = {
"bootstrap.servers": os.environ["KAFKA_BOOTSTRAP"], # "<vip>:9092,..."
"security.protocol": "PLAINTEXT",
}
SASL/PLAIN over TLS (recommended for SASL):
conf = {
"bootstrap.servers": os.environ["KAFKA_BOOTSTRAP"], # "<vip>:9093,..."
"security.protocol": "SASL_SSL",
"sasl.mechanism": "PLAIN",
"sasl.username": os.environ["KAFKA_USERNAME"],
"sasl.password": os.environ["KAFKA_PASSWORD"],
"ssl.ca.location": "/path/to/kafka-ca.pem",
}
SASL/PLAIN, unencrypted: as above, with "security.protocol": "SASL_PLAINTEXT", port 9092 and no ssl.ca.location.
TLS without client authentication:
conf = {
"bootstrap.servers": os.environ["KAFKA_BOOTSTRAP"], # "<vip>:9093,..."
"security.protocol": "SSL",
"ssl.ca.location": "/path/to/kafka-ca.pem",
}
mTLS:
conf = {
"bootstrap.servers": os.environ["KAFKA_BOOTSTRAP"], # "<vip>:9093,..."
"security.protocol": "SSL",
"ssl.ca.location": "/path/to/kafka-ca.pem", # CA of the cluster's Kafka certificate
"ssl.certificate.location": "/path/to/client.pem", # your certificate, CN = your VAST user
"ssl.key.location": "/path/to/client.key",
}
Permissions
When permissions are checked
Permissions are checked only when your platform team turns on authorization for the Kafka view. Authorization needs an authentication method on at least one connection kind, so that the cluster knows who the user is. The VAST CLI refuses it otherwise, with At least one of the authentication mechanisms must be allowed in order to enable authorization for the KAFKA protocol.
- Authorization off: every client that can connect can do everything: produce, consume, and create or delete topics and groups. This includes users with no identity policy at all.
- Authorization on: only operations that the user's identity policy explicitly allows are performed. An operation that no statement matches is refused, and so is every operation on a connection that hasn't authenticated, for example an unencrypted connection without authentication on a view that uses mTLS on TLS. A
Denystatement overrides anAllow.
The cluster evaluates the identity policies attached to the user and its groups. Bucket policies on the Kafka view's bucket don't apply to Kafka operations.
Actions
Kafka actions are written without a prefix, for example "Action": "KafkaRead". Each row below was checked with authorization on, by granting and removing the action:
| Action | Allows |
|---|---|
KafkaWrite on a topic |
Producing to the topic |
KafkaRead on a topic |
Fetching from the topic |
KafkaRead on a consumer group |
Joining the group, heartbeats, and committing and fetching its offsets |
KafkaDescribe on a topic |
Seeing the topic in metadata, and looking up its offsets. Without it the topic is left out of metadata, so clients can't produce to it or consume from it: they get TOPIC_AUTHORIZATION_FAILED. |
KafkaDescribe on a consumer group |
Listing and describing the group. Without it the group is left out of group lists. |
KafkaCreate on the cluster and on the new topic |
Creating the topic. Both are needed. |
KafkaDelete on a topic or group |
Deleting the topic, or the consumer group |
KafkaDescribeConfigs on the cluster, and KafkaCreate on the topic |
Reading the topic's configuration (describe_configs()). Without KafkaDescribeConfigs you get CLUSTER_AUTHORIZATION_FAILED; without KafkaCreate on the topic, TOPIC_AUTHORIZATION_FAILED. |
KafkaAlterConfigs on the cluster and on the topic |
Changing the topic's configuration (alter_configs()). Both are needed. |
So a consumer typically needs KafkaRead and KafkaDescribe on its topics, and KafkaRead on its group. A producer needs KafkaWrite and KafkaDescribe on its topics.
Kafka* matches every Kafka action. s3:Kafka* matches none of them.
Resources
The resources are:
| Resource | Format |
|---|---|
| The Kafka view (cluster) | vast:kafka:cluster:<bucket> |
| A topic | vast:kafka:topic:<bucket>:<topic> |
| A consumer group | vast:kafka:group:<bucket>:<group> |
<bucket> is the Kafka view's bucket. "*" matches every resource, and * inside a name works as a wildcard: vast:kafka:topic:<bucket>:orders* matches every topic whose name starts with orders. A resource that doesn't start with vast:kafka: (or the DataEngine prefix) is refused when the policy is saved, with Resource VRN=… requires data engine or kafka prefix.
An example identity policy for a consumer of one topic, with its own group. With it, the user consumed and committed offsets, and producing was refused:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadOrdersTopic",
"Effect": "Allow",
"Action": ["KafkaRead", "KafkaDescribe"],
"Resource": "vast:kafka:topic:<bucket>:orders"
},
{
"Sid": "UseOwnGroup",
"Effect": "Allow",
"Action": "KafkaRead",
"Resource": "vast:kafka:group:<bucket>:orders-reporting"
}
]
}
What a refused operation looks like
| Refused | Client sees |
|---|---|
| Produce or fetch on a topic | TOPIC_AUTHORIZATION_FAILED for that topic. A consumer reports Subscribed topic not available: <topic>: Broker: Topic authorization failed. |
| A consumer group operation | GROUP_AUTHORIZATION_FAILED, for example JoinGroup failed: Broker: Group authorization failed |
| Creating a topic, or reading or changing configurations | CLUSTER_AUTHORIZATION_FAILED when the cluster action is missing, TOPIC_AUTHORIZATION_FAILED when the topic action is |
| Deleting a topic | TOPIC_AUTHORIZATION_FAILED |
No Kafka ACLs
The Kafka ACL API isn't supported: identity policies take its place. A DescribeAcls request gets SECURITY_DISABLED ("No Authorizer is configured on the broker"), which confluent-kafka's describe_acls() returns as an empty list. create_acls() and delete_acls() fail in the client with _UNSUPPORTED_FEATURE, and a client that sends those requests anyway has its connection closed. Tools that manage Kafka ACLs (such as kafka-acls) don't work.
Reading topics as tables
Reading a topic as a table through the VAST DataBase SDK, the Query Engine, Trino or Spark is a VAST DataBase request, not a Kafka one. It uses S3 access keys and the usual s3:Tabular* permissions on the Kafka view's bucket, not the Kafka actions. Blob expansion needs s3:TabularCreateBlobExpansion, s3:TabularAlterBlobExpansion, s3:TabularDropBlobExpansion and s3:TabularGetBlobExpansion. See VAST DataBase security and setup prerequisites.
So one VAST user can need two credentials: a password (SASL/PLAIN) or certificate (mTLS) for Kafka, and an S3 key pair for SQL and the SDK.
Auditing
Producing, consuming and topic management aren't recorded by VAST protocol auditing, and aren't subject to Quality of Service limits. If you need an audit trail of who read or wrote a topic, keep it in your application.
What to ask your platform team
- Which connection kinds the view allows, and the port of each (9092, 9093).
- The authentication method on each kind.
- For TLS: the CA certificate that signed the cluster's Kafka certificate.
- For SASL/PLAIN: a VAST local user and its password.
- For mTLS: a client certificate with your VAST user name as its CN, its key, and confirmation that the CA is trusted for your tenant.
- Whether authorization is on, and an identity policy with the actions above for your topics and groups.
- For SQL and SDK reads: an S3 key pair and
s3:Tabular*read access to the Kafka view's bucket.
The procedures are in the KB: user authentication, mTLS authentication, authorization and Kafka permissions in an identity policy.
See also
- Setup: everything to ask for, and a connection check.
- Kafka compatibility: supported and unsupported APIs.
- Troubleshooting: authentication failures and dropped connections.
- VAST DataBase security: identities and policies for SQL and the SDK.