This is an automated email from the ASF dual-hosted git repository.
junma pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar.git
The following commit(s) were added to refs/heads/master by this push:
new 3cd129b959f [improve][doc] Improve the information architecture of
encryption and cookbook (#17666)
3cd129b959f is described below
commit 3cd129b959f6f262fb6e1d60639c0fa19575bab1
Author: momo-jun <[email protected]>
AuthorDate: Tue Sep 20 16:06:44 2022 +0800
[improve][doc] Improve the information architecture of encryption and
cookbook (#17666)
* remove duplicate file `cookbooks-encryption.md`
* Revert "remove duplicate file `cookbooks-encryption.md`"
This reverts commit a1e5a547c9ca5c65fc806aca3c6c606d555cc68d.
* streamline encryption cookbook and make it single-sourced
* Add code snippets for Java/C++/Go clients.
* use one svg image to replace two jpg files
* client version and title updates
* improve security overview
* Add note and link for TLS encryption
---
site2/docs/administration-dashboard.md | 2 +-
site2/docs/assets/pulsar-encryption-consumer.jpg | Bin 31221 -> 0 bytes
site2/docs/assets/pulsar-encryption-producer.jpg | Bin 30885 -> 0 bytes
site2/docs/assets/pulsar-encryption.svg | 1 +
site2/docs/client-libraries-node.md | 156 +--------
site2/docs/client-libraries-python.md | 98 +-----
site2/docs/cookbooks-encryption.md | 396 ++++++++++++++++-------
site2/docs/security-authorization.md | 2 +-
site2/docs/security-encryption.md | 302 +----------------
site2/docs/security-overview.md | 42 ++-
10 files changed, 330 insertions(+), 669 deletions(-)
diff --git a/site2/docs/administration-dashboard.md
b/site2/docs/administration-dashboard.md
index c722097cbf3..0bee7be44ce 100644
--- a/site2/docs/administration-dashboard.md
+++ b/site2/docs/administration-dashboard.md
@@ -62,6 +62,6 @@ bin/pulsar standalone --advertised-address 1.2.3.4
:::note
-Currently, only Pulsar Token
[authentication](security-overview.md#authentication-providers) is supported.
+Currently, only Pulsar Token
[authentication](security-overview.md#authentication) is supported.
:::
\ No newline at end of file
diff --git a/site2/docs/assets/pulsar-encryption-consumer.jpg
b/site2/docs/assets/pulsar-encryption-consumer.jpg
deleted file mode 100644
index 41f3f248928..00000000000
Binary files a/site2/docs/assets/pulsar-encryption-consumer.jpg and /dev/null
differ
diff --git a/site2/docs/assets/pulsar-encryption-producer.jpg
b/site2/docs/assets/pulsar-encryption-producer.jpg
deleted file mode 100644
index 1c4050e7e40..00000000000
Binary files a/site2/docs/assets/pulsar-encryption-producer.jpg and /dev/null
differ
diff --git a/site2/docs/assets/pulsar-encryption.svg
b/site2/docs/assets/pulsar-encryption.svg
new file mode 100644
index 00000000000..21d1a2a0930
--- /dev/null
+++ b/site2/docs/assets/pulsar-encryption.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:lucid="lucid" width="2108.33"
height="420.87"><g transform="translate(-18.746783964459837 867.2597739494072)"
lucid:page-tab-id="0_0"><path d="M0-1258.83h3677.73V64H0z" fill="#fff"/><path
d="M1301.86-841.26a6 6 0 0 1 6-6h793.22a6 6 0 0 1 6 6v368.87a6 6 0 0 1-6
6h-793.22a6 6 0 0 1-6-6z" stroke="#5e5e5e" stroke-width="2" fill="#fff"/><use
xlink:href="#a" transform="matrix(1,0,0,1,1306.857120948584,-842 [...]
\ No newline at end of file
diff --git a/site2/docs/client-libraries-node.md
b/site2/docs/client-libraries-node.md
index ab38c04ee2f..e0739763bdb 100644
--- a/site2/docs/client-libraries-node.md
+++ b/site2/docs/client-libraries-node.md
@@ -451,158 +451,4 @@ The following static methods are available for the
message id object:
## End-to-end encryption
-[End-to-end encryption](/cookbooks-encryption.md#docsNav) allows applications
to encrypt messages at producers and decrypt at consumers.
-
-### Configuration
-
-If you want to use the end-to-end encryption feature in the Node.js client,
you need to configure `publicKeyPath` for producer and `privateKeyPath` for
consumers.
-
-```conf
-publicKeyPath: "./public.pem"
-privateKeyPath: "./private.pem"
-```
-
-### Tutorial
-
-This section provides step-by-step instructions on how to use the end-to-end
encryption feature in the Node.js client.
-
-**Prerequisite**
-
-- Pulsar C++ client 2.7.1 or later
-
-**Step**
-
-1. Create both public and private key pairs.
-
- **Input**
-
- ```shell
- openssl genrsa -out private.pem 2048
- openssl rsa -in private.pem -pubout -out public.pem
- ```
-
-2. Create a producer to send encrypted messages.
-
- **Input**
-
- ```javascript
- const Pulsar = require('pulsar-client');
-
- (async () => {
- // Create a client
- const client = new Pulsar.Client({
- serviceUrl: 'pulsar://localhost:6650',
- operationTimeoutSeconds: 30,
- });
-
- // Create a producer
- const producer = await client.createProducer({
- topic: 'persistent://public/default/my-topic',
- sendTimeoutMs: 30000,
- batchingEnabled: true,
- publicKeyPath: "./public.pem",
- encryptionKey: "encryption-key"
- });
-
- console.log(producer.ProducerConfig)
- // Send messages
- for (let i = 0; i < 10; i += 1) {
- const msg = `my-message-${i}`;
- producer.send({
- data: Buffer.from(msg),
- });
- console.log(`Sent message: ${msg}`);
- }
- await producer.flush();
-
- await producer.close();
- await client.close();
- })();
- ```
-
-3. Create a consumer to receive encrypted messages.
-
- **Input**
-
- ```javascript
- const Pulsar = require('pulsar-client');
-
- (async () => {
- // Create a client
- const client = new Pulsar.Client({
- serviceUrl: 'pulsar://172.25.0.3:6650',
- operationTimeoutSeconds: 30
- });
-
- // Create a consumer
- const consumer = await client.subscribe({
- topic: 'persistent://public/default/my-topic',
- subscription: 'sub1',
- subscriptionType: 'Shared',
- ackTimeoutMs: 10000,
- privateKeyPath: "./private.pem"
- });
-
- console.log(consumer)
- // Receive messages
- for (let i = 0; i < 10; i += 1) {
- const msg = await consumer.receive();
- console.log(msg.getData().toString());
- consumer.acknowledge(msg);
- }
-
- await consumer.close();
- await client.close();
- })();
- ```
-
-4. Run the consumer to receive encrypted messages.
-
- **Input**
-
- ```shell
- node consumer.js
- ```
-
-5. In a new terminal tab, run the producer to produce encrypted messages.
-
- **Input**
-
- ```shell
- node producer.js
- ```
-
- Now you can see the producer sends messages and the consumer receives
messages successfully.
-
- **Output**
-
- This is from the producer side.
-
- ```
- Sent message: my-message-0
- Sent message: my-message-1
- Sent message: my-message-2
- Sent message: my-message-3
- Sent message: my-message-4
- Sent message: my-message-5
- Sent message: my-message-6
- Sent message: my-message-7
- Sent message: my-message-8
- Sent message: my-message-9
- ```
-
- This is from the consumer side.
-
- ```
- my-message-0
- my-message-1
- my-message-2
- my-message-3
- my-message-4
- my-message-5
- my-message-6
- my-message-7
- my-message-8
- my-message-9
- ```
-
+Pulsar encryption allows applications to encrypt messages at producers and
decrypt messages at consumers. See [cookbook](cookbooks-encryption.md) for more
details.
diff --git a/site2/docs/client-libraries-python.md
b/site2/docs/client-libraries-python.md
index 026afa093b8..8e4358fee83 100644
--- a/site2/docs/client-libraries-python.md
+++ b/site2/docs/client-libraries-python.md
@@ -515,100 +515,4 @@ consumer = client.subscribe(
## End-to-end encryption
-[End-to-end encryption](/cookbooks-encryption.md#docsNav) allows applications
to encrypt messages at producers and decrypt messages at consumers.
-
-### Configuration
-
-To use the end-to-end encryption feature in the Python client, you need to
configure `publicKeyPath` for producers and `privateKeyPath` for consumers.
-
-```
-publicKeyPath: "./public.pem"
-privateKeyPath: "./private.pem"
-```
-
-### Tutorial
-
-This section provides step-by-step instructions on how to use the end-to-end
encryption feature in the Python client.
-
-**Prerequisite**
-
-- Pulsar Python client 2.7.1 or later
-
-**Step**
-
-1. Create both public and private key pairs.
-
- **Input**
-
- ```shell
- openssl genrsa -out private.pem 2048
- openssl rsa -in private.pem -pubout -out public.pem
- ```
-
-2. Create a producer to send encrypted messages.
-
- **Input**
-
- ```python
- import pulsar
-
- publicKeyPath = "./public.pem"
- privateKeyPath = ""
- crypto_key_reader = pulsar.CryptoKeyReader(publicKeyPath, privateKeyPath)
- client = pulsar.Client('pulsar://localhost:6650')
- producer = client.create_producer(topic='encryption',
encryption_key='encryption', crypto_key_reader=crypto_key_reader)
- producer.send('encryption message'.encode('utf8'))
- print('sent message')
- producer.close()
- client.close()
- ```
-
-3. Create a consumer to receive encrypted messages.
-
- **Input**
-
- ```python
- import pulsar
-
- publicKeyPath = ""
- privateKeyPath = "./private.pem"
- crypto_key_reader = pulsar.CryptoKeyReader(publicKeyPath, privateKeyPath)
- client = pulsar.Client('pulsar://localhost:6650')
- consumer = client.subscribe(topic='encryption',
subscription_name='encryption-sub', crypto_key_reader=crypto_key_reader)
- msg = consumer.receive()
- print("Received msg '{}' id = '{}'".format(msg.data(), msg.message_id()))
- consumer.close()
- client.close()
- ```
-
-4. Run the consumer to receive encrypted messages.
-
- **Input**
-
- ```shell
- python consumer.py
- ```
-
-5. In a new terminal tab, run the producer to produce encrypted messages.
-
- **Input**
-
- ```shell
- python producer.py
- ```
-
- Now you can see the producer sends messages and the consumer receives
messages successfully.
-
- **Output**
-
- This is from the producer side.
-
- ```
- sent message
- ```
-
- This is from the consumer side.
-
- ```
- Received msg 'encryption message' id = '(0,0,-1,-1)'
- ```
+Pulsar encryption allows applications to encrypt messages at producers and
decrypt messages at consumers. See [cookbook](cookbooks-encryption.md) for more
details.
\ No newline at end of file
diff --git a/site2/docs/cookbooks-encryption.md
b/site2/docs/cookbooks-encryption.md
index 201b5986bcb..9aa321e73c0 100644
--- a/site2/docs/cookbooks-encryption.md
+++ b/site2/docs/cookbooks-encryption.md
@@ -1,46 +1,230 @@
---
id: cookbooks-encryption
-title: Pulsar Encryption
-sidebar_label: "Encryption "
+title: Configure end-to-end encryption
+sidebar_label: "Configure end-to-end encryption"
---
-Pulsar encryption allows applications to encrypt messages at the producer and
decrypt at the consumer. Encryption is performed using the public/private key
pair configured by the application. Encrypted messages can only be decrypted by
consumers with a valid key.
-
-## Asymmetric and symmetric encryption
-
-Pulsar uses a dynamically generated symmetric AES key to encrypt
messages(data). The AES key(data key) is encrypted using the
application-provided ECDSA/RSA key pair. As a result, there is no need to share
the secret with everyone.
-
-Key is a public/private key pair used for encryption/decryption. The producer
key is the public key, and the consumer key is the private key of the key pair.
-
-The application configures the producer with the public key. This key is used
to encrypt the AES data key. The encrypted data key is sent as part of the
message header. Only entities with the private key(in this case the consumer)
will be able to decrypt the data key which is used to decrypt the message.
-
-A message can be encrypted with more than one key. Any one of the keys used
for encrypting the message is sufficient to decrypt the message
-
-Pulsar does not store the encryption key anywhere in the pulsar service. If
you lose/delete the private key, your message is irretrievably lost, and is
unrecoverable
-
-## Producer
-
-
-## Consumer
-
-
-## Here are the steps to get started:
-
-1. Create your ECDSA or RSA public/private key pair.
-
-```shell
-openssl ecparam -name secp521r1 -genkey -param_enc explicit -out
test_ecdsa_privkey.pem
-openssl ec -in test_ecdsa_privkey.pem -pubout -outform pkcs8 -out
test_ecdsa_pubkey.pem
-```
-
-2. Add the public and private key to the key management and configure your
producers to retrieve public keys and consumers clients to retrieve private
keys.
-3. Implement CryptoKeyReader::getPublicKey() interface from producer and
CryptoKeyReader::getPrivateKey() interface from consumer, which will be invoked
by Pulsar client to load the key.
-4. Add encryption key to producer configuration:
conf.addEncryptionKey("myapp.key")
-5. Add CryptoKeyReader implementation to producer/consumer config:
conf.setCryptoKeyReader(keyReader)
-6. Sample producer application:
-
-```java
-class RawFileKeyReader implements CryptoKeyReader {
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+[Pulsar encryption](security-encryption.md) allows clients to encrypt messages
at producers and decrypt messages at consumers.
+
+## Prerequisites
+
+* Pulsar Java/Python/C++/Node.js client 2.7.1 or later versions.
+* Pulsar Go client 0.6.0 or later versions.
+
+## Configure end-to-end encryption
+
+1. Create both public and private key pairs.
+ * ECDSA(for Java and Go clients)
+ ```shell
+ openssl ecparam -name secp521r1 -genkey -param_enc explicit -out
test_ecdsa_privkey.pem
+ openssl ec -in test_ecdsa_privkey.pem -pubout -outform pem -out
test_ecdsa_pubkey.pem
+ ```
+
+ * RSA (for Python, C++ and Node.js clients)
+ ```shell
+ openssl genrsa -out test_rsa_privkey.pem 2048
+ openssl rsa -in test_rsa_privkey.pem -pubout -outform pkcs8 -out
test_rsa_pubkey.pem
+ ```
+
+2. Configure a `CryptoKeyReader` on producers, consumers or readers.
+
+ ````mdx-code-block
+ <Tabs groupId="lang-choice"
+ defaultValue="Java"
+
values={[{"label":"Java","value":"Java"},{"label":"Python","value":"Python"},{"label":"C++","value":"C++"},{"label":"Go","value":"Go"},{"label":"Node.js","value":"Node.js"}]}>
+ <TabItem value="Java">
+
+ ```java
+ PulsarClient pulsarClient =
PulsarClient.builder().serviceUrl("pulsar://localhost:6650").build();
+ String topic = "persistent://my-tenant/my-ns/my-topic";
+ // RawFileKeyReader is just an example implementation that's not provided
by Pulsar
+ CryptoKeyReader keyReader = new RawFileKeyReader("test_ecdsa_pubkey.pem",
"test_ecdsa_privkey.pem");
+
+ Producer<byte[]> producer = pulsarClient.newProducer()
+ .topic(topic)
+ .cryptoKeyReader(keyReader)
+ .addEncryptionKey("myappkey")
+ .create();
+
+ Consumer<byte[]> consumer = pulsarClient.newConsumer()
+ .topic(topic)
+ .subscriptionName("my-subscriber-name")
+ .cryptoKeyReader(keyReader)
+ .subscribe();
+
+ Reader<byte[]> reader = pulsarClient.newReader()
+ .topic(topic)
+ .startMessageId(MessageId.earliest)
+ .cryptoKeyReader(keyReader)
+ .create();
+ ```
+
+ </TabItem>
+ <TabItem value="Python">
+
+ ```python
+ from pulsar import Client, CryptoKeyReader
+
+ client = Client('pulsar://localhost:6650')
+ topic = 'my-topic'
+ # CryptoKeyReader is a built-in implementation that reads public key and
private key from files
+ key_reader = CryptoKeyReader('test_rsa_pubkey.pem', 'test_rsa_privkey.pem')
+
+ producer = client.create_producer(
+ topic=topic,
+ encryption_key='myappkey',
+ crypto_key_reader=key_reader
+ )
+
+ consumer = client.subscribe(
+ topic=topic,
+ subscription_name='my-subscriber-name',
+ crypto_key_reader=key_reader
+ )
+
+ reader = client.create_reader(
+ topic=topic,
+ start_message_id=MessageId.earliest,
+ crypto_key_reader=key_reader
+ )
+
+ client.close()
+ ```
+
+ </TabItem>
+ <TabItem value="C++">
+
+ ```cpp
+ Client client("pulsar://localhost:6650");
+ std::string topic = "persistent://my-tenant/my-ns/my-topic";
+ // DefaultCryptoKeyReader is a built-in implementation that reads public
key and private key from files
+ auto keyReader =
std::make_shared<DefaultCryptoKeyReader>("test_rsa_pubkey.pem",
"test_rsa_privkey.pem");
+
+ Producer producer;
+ ProducerConfiguration producerConf;
+ producerConf.setCryptoKeyReader(keyReader);
+ producerConf.addEncryptionKey("myappkey");
+ client.createProducer(topic, producerConf, producer);
+
+ Consumer consumer;
+ ConsumerConfiguration consumerConf;
+ consumerConf.setCryptoKeyReader(keyReader);
+ client.subscribe(topic, "my-subscriber-name", consumerConf, consumer);
+
+ Reader reader;
+ ReaderConfiguration readerConf;
+ readerConf.setCryptoKeyReader(keyReader);
+ client.createReader(topic, MessageId::earliest(), readerConf, reader);
+ ```
+
+ </TabItem>
+ <TabItem value="Go">
+
+ ```go
+ client, err := pulsar.NewClient(pulsar.ClientOptions{
+ URL: "pulsar://localhost:6650",
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ defer client.Close()
+
+ topic := "persistent://my-tenant/my-ns/my-topic"
+ keyReader := crypto.NewFileKeyReader("test_ecdsa_pubkey.pem",
"test_ecdsa_privkey.pem")
+ producer, err := client.CreateProducer(pulsar.ProducerOptions{
+ Topic: topic,
+ Encryption: &pulsar.ProducerEncryptionInfo{
+ KeyReader: keyReader,
+ Keys: []string{"myappkey"},
+ },
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer producer.Close()
+
+ consumer, err := client.Subscribe(pulsar.ConsumerOptions{
+ Topic: topic,
+ SubscriptionName: "my-subscriber-name",
+ Decryption: &pulsar.MessageDecryptionInfo{
+ KeyReader: keyReader,
+ },
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer consumer.Close()
+
+ reader, err := client.CreateReader(pulsar.ReaderOptions{
+ Topic: topic,
+ Decryption: &pulsar.MessageDecryptionInfo{
+ KeyReader: keyReader,
+ },
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer reader.Close()
+ ```
+
+ </TabItem>
+ <TabItem value="Node.js">
+
+ ```javascript
+ const Pulsar = require('pulsar-client');
+
+ const topic = 'persistent://my-tenant/my-ns/my-topic';
+
+ (async () => {
+ // Create a client
+ const client = new Pulsar.Client({
+ serviceUrl: 'pulsar://localhost:6650',
+ operationTimeoutSeconds: 30,
+ });
+
+ // Create a producer
+ const producer = await client.createProducer({
+ topic: topic,
+ sendTimeoutMs: 30000,
+ batchingEnabled: true,
+ publicKeyPath: "test_rsa_pubkey.pem",
+ encryptionKey: "encryption-key"
+ });
+
+ // Create a consumer
+ const consumer = await client.subscribe({
+ topic: topic,
+ subscription: 'my-subscriber-name',
+ subscriptionType: 'Shared',
+ ackTimeoutMs: 10000,
+ privateKeyPath: "test_rsa_privkey.pem"
+ });
+ await consumer.close();
+ await producer.close();
+ await client.close();
+ })();
+ ```
+
+ </TabItem>
+ </Tabs>
+ ````
+
+3. Optional: customize the `CryptoKeyReader` implementation.
+
+ ````mdx-code-block
+ <Tabs groupId="lang-choice"
+ defaultValue="Java"
+
values={[{"label":"Java","value":"Java"},{"label":"Python","value":"Python"},{"label":"C++","value":"C++"},{"label":"Go","value":"Go"},{"label":"Node.js","value":"Node.js"}]}>
+ <TabItem value="Java">
+
+ ```java
+ class RawFileKeyReader implements CryptoKeyReader {
String publicKeyFile = "";
String privateKeyFile = "";
@@ -73,104 +257,78 @@ class RawFileKeyReader implements CryptoKeyReader {
}
return keyInfo;
}
-}
-PulsarClient pulsarClient = PulsarClient.create("http://localhost:8080");
-
-ProducerConfiguration prodConf = new ProducerConfiguration();
-prodConf.setCryptoKeyReader(new RawFileKeyReader("test_ecdsa_pubkey.pem",
"test_ecdsa_privkey.pem"));
-prodConf.addEncryptionKey("myappkey");
-
-Producer producer =
pulsarClient.createProducer("persistent://my-tenant/my-ns/my-topic", prodConf);
-
-for (int i = 0; i < 10; i++) {
- producer.send("my-message".getBytes());
-}
-
-pulsarClient.close();
-```
+ }
+ ```
-7. Sample Consumer Application:
+ </TabItem>
+ <TabItem value="Python">
-```java
-class RawFileKeyReader implements CryptoKeyReader {
+ Currently, customizing the `CryptoKeyReader` implementation is not
supported in Python. However, you can use the default implementation by
specifying the path of the private key and public keys.
- String publicKeyFile = "";
- String privateKeyFile = "";
+ </TabItem>
+ <TabItem value="C++">
- RawFileKeyReader(String pubKeyFile, String privKeyFile) {
- publicKeyFile = pubKeyFile;
- privateKeyFile = privKeyFile;
+ ```cpp
+ class CustomCryptoKeyReader : public CryptoKeyReader {
+ public:
+ Result getPublicKey(const std::string& keyName, std::map<std::string,
std::string>& metadata,
+ EncryptionKeyInfo& encKeyInfo) const override {
+ // TODO
+ return ResultOk;
}
- @Override
- public EncryptionKeyInfo getPublicKey(String keyName, Map<String, String>
keyMeta) {
- EncryptionKeyInfo keyInfo = new EncryptionKeyInfo();
- try {
- keyInfo.setKey(Files.readAllBytes(Paths.get(publicKeyFile)));
- } catch (IOException e) {
- System.out.println("ERROR: Failed to read public key from file " +
publicKeyFile);
- e.printStackTrace();
- }
- return keyInfo;
+ Result getPrivateKey(const std::string& keyName, std::map<std::string,
std::string>& metadata,
+ EncryptionKeyInfo& encKeyInfo) const override {
+ // TODO
+ return ResultOk;
}
+ };
+ ```
- @Override
- public EncryptionKeyInfo getPrivateKey(String keyName, Map<String, String>
keyMeta) {
- EncryptionKeyInfo keyInfo = new EncryptionKeyInfo();
- try {
- keyInfo.setKey(Files.readAllBytes(Paths.get(privateKeyFile)));
- } catch (IOException e) {
- System.out.println("ERROR: Failed to read private key from file "
+ privateKeyFile);
- e.printStackTrace();
- }
- return keyInfo;
- }
-}
+ </TabItem>
+ <TabItem value="Go">
-ConsumerConfiguration consConf = new ConsumerConfiguration();
-consConf.setCryptoKeyReader(new RawFileKeyReader("test_ecdsa_pubkey.pem",
"test_ecdsa_privkey.pem"));
-PulsarClient pulsarClient = PulsarClient.create("http://localhost:8080");
-Consumer consumer =
pulsarClient.subscribe("persistent://my-tenant//my-ns/my-topic",
"my-subscriber-name", consConf);
-Message msg = null;
+ ```go
+ type CustomKeyReader struct {
+ publicKeyPath string
+ privateKeyPath string
+ }
-for (int i = 0; i < 10; i++) {
- msg = consumer.receive();
- // do something
- System.out.println("Received: " + new String(msg.getData()));
-}
+ func (c *CustomKeyReader) PublicKey(keyName string, keyMeta
map[string]string) (*EncryptionKeyInfo, error) {
+ keyInfo := &EncryptionKeyInfo{}
+ // TODO
+ return keyInfo, nil
+ }
-// Acknowledge the consumption of all messages at once
-consumer.acknowledgeCumulative(msg);
-pulsarClient.close();
-```
+ // PrivateKey read private key from the given path
+ func (c *CustomKeyReader) PrivateKey(keyName string, keyMeta
map[string]string) (*EncryptionKeyInfo, error) {
+ keyInfo := &EncryptionKeyInfo{}
+ // TODO
+ return keyInfo, nil
+ }
+ ```
-## Key rotation
-Pulsar generates a new AES data key every 4 hours or after a certain number of
messages are published. The asymmetric public key is automatically fetched by
producers every 4 hours by calling CryptoKeyReader::getPublicKey() to retrieve
the latest version.
+ </TabItem>
+ <TabItem value="Node.js">
-## Enabling encryption at the producer application:
-If you produce messages that are consumed across application boundaries, you
need to ensure that consumers in other applications have access to one of the
private keys that can decrypt the messages. This can be done in two ways:
-1. The consumer application provides you access to their public key, which you
add to your producer keys
-1. You grant access to one of the private keys from the pairs used by producers
+ Currently, customizing the `CryptoKeyReader` implementation is not
supported in Python. However, you can use the default implementation by
specifying the path of the private key and public keys.
-In some cases, the producer may want to encrypt the messages with multiple
keys. For this, add all such keys to the config. Consumers will be able to
decrypt the message, as long as it has access to at least one of the keys.
+ </TabItem>
+ </Tabs>
+ ````
-For example, to encrypt messages using 2 keys `myapp.messagekey1` and
`myapp.messagekey2`, do the following:
+## Encrypt a message with multiple keys
-```java
+:::note
-conf.addEncryptionKey("myapp.messagekey1");
-conf.addEncryptionKey("myapp.messagekey2");
+This is only available for Java clients.
-```
+:::
-## Decrypting encrypted messages at the consumer application:
-Consumers require to access one of the private keys to decrypt messages
produced by the producer. If you would like to receive encrypted messages,
create a public/private key and give your public key to the producer
application to encrypt messages using your public key.
+You can encrypt a message with more than one key. Producers add all such keys
to the config and consumers can decrypt the message as long as they have access
to at least one of the keys. Any one of the keys used for encrypting the
message is sufficient to decrypt the message.
-## Handling Failures:
-* Producer/ Consumer loses access to the key
- * Producer action will fail to indicate the cause of the failure. The
application has the option to proceed with sending unencrypted messages in such
cases. Call `conf.setCryptoFailureAction`(ProducerCryptoFailureAction) to
control the producer behavior. The default behavior is to fail the request.
- * If consumption failed due to decryption failure or missing keys in
consumers, the application has the option to consume the encrypted message or
discard it. Call conf.setCryptoFailureAction(ConsumerCryptoFailureAction) to
control the consumer behavior. The default behavior is to fail the request. The
application will never be able to decrypt the messages if the private key is
permanently lost.
-* Batch messaging
- * If decryption fails and the message contains batch messages, the client
will not be able to retrieve individual messages in the batch, hence message
consumption fails even if conf.setCryptoFailureAction() is set to CONSUME.
-* If decryption fails, the message consumption stops and the application will
notice backlog growth in addition to decryption failure messages in the client
log. If the application does not have access to the private key to decrypt the
message, the only option is to skip/discard backlogged messages.
+For example, encrypt the messages using 2 keys (`myapp.messagekey1` and
`myapp.messagekey2`):
+```java
+PulsarClient.newProducer().addEncryptionKey("myapp.messagekey1").addEncryptionKey("myapp.messagekey2");
+```
\ No newline at end of file
diff --git a/site2/docs/security-authorization.md
b/site2/docs/security-authorization.md
index 046e3361b82..2b0d9016c62 100644
--- a/site2/docs/security-authorization.md
+++ b/site2/docs/security-authorization.md
@@ -5,7 +5,7 @@ sidebar_label: "Authorization and ACLs"
---
-In Pulsar, the [authentication
provider](security-overview.md#authentication-providers) is responsible for
properly identifying clients and associating the clients with [role
tokens](security-overview.md#role-tokens). If you only enable authentication,
an authenticated role token can access all resources in the cluster.
*Authorization* is the process that determines _what_ clients can do.
+In Pulsar, the [authentication provider](security-overview.md#authentication)
is responsible for properly identifying clients and associating the clients
with role tokens. If you only enable authentication, an authenticated role
token can access all resources in the cluster. *Authorization* is the process
that determines _what_ clients can do.
The role tokens with the most privileges are the *superusers*. The
*superusers* can create and destroy tenants, along with having full access to
all tenant resources.
diff --git a/site2/docs/security-encryption.md
b/site2/docs/security-encryption.md
index f57b312baa7..af21e5a33bd 100644
--- a/site2/docs/security-encryption.md
+++ b/site2/docs/security-encryption.md
@@ -9,308 +9,40 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
````
+Applications can use Pulsar end-to-end encryption (E2EE) to encrypt messages
on the producer side and decrypt messages on the consumer side. You can use the
public and private key pair that the application configures to perform
encryption and decryption. Only the consumers with a valid key can decrypt the
encrypted messages.
-Applications can use Pulsar encryption to encrypt messages on the producer
side and decrypt messages on the consumer side. You can use the public and
private key pair that the application configures to perform encryption. Only
the consumers with a valid key can decrypt the encrypted messages.
+## How it works
-## Asymmetric and symmetric encryption
+Pulsar uses a dynamically generated symmetric AES key to encrypt messages
(data). You can use the application-provided ECDSA (Elliptic Curve Digital
Signature Algorithm) or RSA (Rivest–Shamir–Adleman) key pair to encrypt the AES
key (data key), so you do not have to share the secret with everyone.
-Pulsar uses a dynamically generated symmetric AES key to encrypt
messages(data). You can use the application-provided ECDSA (Elliptic Curve
Digital Signature Algorithm) or RSA (Rivest–Shamir–Adleman) key pair to encrypt
the AES key(data key), so you do not have to share the secret with everyone.
+The application configures the producer with the public key for encryption.
You can use this key to encrypt the AES data key. The encrypted data key is
sent as part of the message header. Only entities with the private key (in this
case the consumer) can decrypt the data key which is used to decrypt the
message.
-Key is a public and private key pair used for encryption or decryption. The
producer key is the public key of the key pair, and the consumer key is the
private key of the key pair.
+The following figure illustrates how Pulsar encrypts messages on the producer
side and decrypts messages on the consumer side.
-The application configures the producer with the public key. You can use this
key to encrypt the AES data key. The encrypted data key is sent as part of the
message header. Only entities with the private key (in this case the consumer)
can decrypt the data key which is used to decrypt the message.
+
-You can encrypt a message with more than one key. Any one of the keys used for
encrypting the message is sufficient to decrypt the message.
-
-Pulsar does not store the encryption key anywhere in the Pulsar service. If
you lose or delete the private key, your message is irretrievably lost and
unrecoverable.
-
-## Producer
-
-
-## Consumer
-
-
-## Get started
-
-1. Create your ECDSA or RSA public and private key pair by using the following
commands.
- * ECDSA(for Java clients only)
-
- ```shell
- openssl ecparam -name secp521r1 -genkey -param_enc explicit -out
test_ecdsa_privkey.pem
- openssl ec -in test_ecdsa_privkey.pem -pubout -outform pem -out
test_ecdsa_pubkey.pem
- ```
-
- * RSA (for C++, Python and Node.js clients)
-
- ```shell
- openssl genrsa -out test_rsa_privkey.pem 2048
- openssl rsa -in test_rsa_privkey.pem -pubout -outform pkcs8 -out
test_rsa_pubkey.pem
- ```
-
-2. Add the public and private keys to the key management and configure your
producers to retrieve public keys and consumer clients to retrieve private keys.
-
-3. Implement the `CryptoKeyReader` interface, specifically
`CryptoKeyReader.getPublicKey()` for producer and
`CryptoKeyReader.getPrivateKey()` for consumer, which Pulsar client invokes to
load the key.
-
-4. Add the encryption key name to the producer builder:
PulsarClient.newProducer().addEncryptionKey("myapp.key").
-
-5. Configure a `CryptoKeyReader` to a producer, consumer or reader.
-
-````mdx-code-block
-<Tabs groupId="lang-choice"
- defaultValue="Java"
-
values={[{"label":"Java","value":"Java"},{"label":"C++","value":"C++"},{"label":"Python","value":"Python"},{"label":"Node.js","value":"Node.js"}]}>
-<TabItem value="Java">
-
-```java
-PulsarClient pulsarClient =
PulsarClient.builder().serviceUrl("pulsar://localhost:6650").build();
-String topic = "persistent://my-tenant/my-ns/my-topic";
-// RawFileKeyReader is just an example implementation that's not provided by
Pulsar
-CryptoKeyReader keyReader = new RawFileKeyReader("test_ecdsa_pubkey.pem",
"test_ecdsa_privkey.pem");
-
-Producer<byte[]> producer = pulsarClient.newProducer()
- .topic(topic)
- .cryptoKeyReader(keyReader)
- .addEncryptionKey("myappkey")
- .create();
-
-Consumer<byte[]> consumer = pulsarClient.newConsumer()
- .topic(topic)
- .subscriptionName("my-subscriber-name")
- .cryptoKeyReader(keyReader)
- .subscribe();
-
-Reader<byte[]> reader = pulsarClient.newReader()
- .topic(topic)
- .startMessageId(MessageId.earliest)
- .cryptoKeyReader(keyReader)
- .create();
-```
-
-</TabItem>
-<TabItem value="C++">
-
-```cpp
-Client client("pulsar://localhost:6650");
-std::string topic = "persistent://my-tenant/my-ns/my-topic";
-// DefaultCryptoKeyReader is a built-in implementation that reads public key
and private key from files
-auto keyReader =
std::make_shared<DefaultCryptoKeyReader>("test_rsa_pubkey.pem",
"test_rsa_privkey.pem");
-
-Producer producer;
-ProducerConfiguration producerConf;
-producerConf.setCryptoKeyReader(keyReader);
-producerConf.addEncryptionKey("myappkey");
-client.createProducer(topic, producerConf, producer);
-
-Consumer consumer;
-ConsumerConfiguration consumerConf;
-consumerConf.setCryptoKeyReader(keyReader);
-client.subscribe(topic, "my-subscriber-name", consumerConf, consumer);
-
-Reader reader;
-ReaderConfiguration readerConf;
-readerConf.setCryptoKeyReader(keyReader);
-client.createReader(topic, MessageId::earliest(), readerConf, reader);
-```
-
-</TabItem>
-<TabItem value="Python">
-
-```python
-from pulsar import Client, CryptoKeyReader
-
-client = Client('pulsar://localhost:6650')
-topic = 'persistent://my-tenant/my-ns/my-topic'
-# CryptoKeyReader is a built-in implementation that reads public key and
private key from files
-key_reader = CryptoKeyReader('test_rsa_pubkey.pem', 'test_rsa_privkey.pem')
-
-producer = client.create_producer(
- topic=topic,
- encryption_key='myappkey',
- crypto_key_reader=key_reader
-)
-
-consumer = client.subscribe(
- topic=topic,
- subscription_name='my-subscriber-name',
- crypto_key_reader=key_reader
-)
-
-reader = client.create_reader(
- topic=topic,
- start_message_id=MessageId.earliest,
- crypto_key_reader=key_reader
-)
-
-client.close()
-```
-
-</TabItem>
-<TabItem value="Node.js">
-
-```javascript
-const Pulsar = require('pulsar-client');
-
-(async () => {
-// Create a client
-const client = new Pulsar.Client({
- serviceUrl: 'pulsar://localhost:6650',
- operationTimeoutSeconds: 30,
-});
-
-// Create a producer
-const producer = await client.createProducer({
- topic: 'persistent://public/default/my-topic',
- sendTimeoutMs: 30000,
- batchingEnabled: true,
- publicKeyPath: "public-key.client-rsa.pem",
- encryptionKey: "encryption-key"
-});
-
-// Create a consumer
-const consumer = await client.subscribe({
- topic: 'persistent://public/default/my-topic',
- subscription: 'sub1',
- subscriptionType: 'Shared',
- ackTimeoutMs: 10000,
- privateKeyPath: "private-key.client-rsa.pem"
-});
-
-// Send messages
-for (let i = 0; i < 10; i += 1) {
- const msg = `my-message-${i}`;
- producer.send({
- data: Buffer.from(msg),
- });
- console.log(`Sent message: ${msg}`);
-}
-await producer.flush();
-
-// Receive messages
-for (let i = 0; i < 10; i += 1) {
- const msg = await consumer.receive();
- console.log(msg.getData().toString());
- consumer.acknowledge(msg);
-}
-
-await consumer.close();
-await producer.close();
-await client.close();
-})();
-```
-
-</TabItem>
-
-</Tabs>
-````
-
-6. Below is an example of a **customized** `CryptoKeyReader` implementation.
-
-````mdx-code-block
-<Tabs groupId="lang-choice"
- defaultValue="Java"
-
values={[{"label":"Java","value":"Java"},{"label":"C++","value":"C++"},{"label":"Python","value":"Python"},{"label":"Node.js","value":"Node.js"}]}>
-<TabItem value="Java">
-
-```java
-class RawFileKeyReader implements CryptoKeyReader {
-
- String publicKeyFile = "";
- String privateKeyFile = "";
-
- RawFileKeyReader(String pubKeyFile, String privKeyFile) {
- publicKeyFile = pubKeyFile;
- privateKeyFile = privKeyFile;
- }
-
- @Override
- public EncryptionKeyInfo getPublicKey(String keyName, Map<String, String>
keyMeta) {
- EncryptionKeyInfo keyInfo = new EncryptionKeyInfo();
- try {
- keyInfo.setKey(Files.readAllBytes(Paths.get(publicKeyFile)));
- } catch (IOException e) {
- System.out.println("ERROR: Failed to read public key from file " +
publicKeyFile);
- e.printStackTrace();
- }
- return keyInfo;
- }
-
- @Override
- public EncryptionKeyInfo getPrivateKey(String keyName, Map<String, String>
keyMeta) {
- EncryptionKeyInfo keyInfo = new EncryptionKeyInfo();
- try {
- keyInfo.setKey(Files.readAllBytes(Paths.get(privateKeyFile)));
- } catch (IOException e) {
- System.out.println("ERROR: Failed to read private key from file "
+ privateKeyFile);
- e.printStackTrace();
- }
- return keyInfo;
- }
-}
-```
-
-</TabItem>
-<TabItem value="C++">
-
-```cpp
-class CustomCryptoKeyReader : public CryptoKeyReader {
- public:
- Result getPublicKey(const std::string& keyName, std::map<std::string,
std::string>& metadata,
- EncryptionKeyInfo& encKeyInfo) const override {
- // TODO:
- return ResultOk;
- }
-
- Result getPrivateKey(const std::string& keyName, std::map<std::string,
std::string>& metadata,
- EncryptionKeyInfo& encKeyInfo) const override {
- // TODO:
- return ResultOk;
- }
-};
-
-auto keyReader = std::make_shared<CustomCryptoKeyReader>(/* ... */);
-// TODO: create producer, consumer or reader based on keyReader here
-```
-
-Besides, you can use the **default** implementation of `CryptoKeyReader` by
specifying the paths of `private key` and `public key`.
-
-</TabItem>
-<TabItem value="Python">
-
-Currently, **customized** `CryptoKeyReader` implementation is not supported in
Python. However, you can use the **default** implementation by specifying the
path of `private key` and `public key`.
-
-</TabItem>
-<TabItem value="Node.js">
+If produced messages are consumed across application boundaries, you need to
ensure that consumers in other applications have access to one of the private
keys that can decrypt the messages. You can do this in two ways:
+1. The consumer application provides you access to the public key, which you
add to your producer keys.
+2. You grant access to one of the private keys from the pairs that the
producer uses.
-Currently, **customized** `CryptoKeyReader` implementation is not supported in
Node.js. However, you can use the **default** implementation by specifying the
path of `private key` and `public key`.
+:::tip
-</TabItem>
+* Pulsar does not store the encryption key anywhere in the Pulsar service. If
you lose or delete the private key, your message is irretrievably lost and
unrecoverable.
+* Pulsar generates a new AES data key every 4 hours or after publishing a
certain number of messages. Producers fetch the asymmetric public key every 4
hours by calling `CryptoKeyReader.getPublicKey()` to retrieve the latest
version.
-</Tabs>
-````
+:::
-## Key rotation
-Pulsar generates a new AES data key every 4 hours or after publishing a
certain number of messages. A producer fetches the asymmetric public key every
4 hours by calling `CryptoKeyReader.getPublicKey()` to retrieve the latest
version.
-## Enable encryption at the producer application
-If you produce messages that are consumed across application boundaries, you
need to ensure that consumers in other applications have access to one of the
private keys that can decrypt the messages. You can do this in two ways:
-1. The consumer application provides you access to their public key, which you
add to your producer keys.
-2. You grant access to one of the private keys from the pairs that the
producer uses.
-
-When producers want to encrypt the messages with multiple keys, producers add
all such keys to the config. Consumers can decrypt the message as long as they
have access to at least one of the keys.
+## Get started
-If you need to encrypt the messages using 2 keys (`myapp.messagekey1` and
`myapp.messagekey2`), refer to the following example.
+Pulsar encryption allows applications to encrypt messages on the producer side
and decrypt messages on the consumer side. See
[cookbook](cookbooks-encryption.md) for detailed instructions.
-```java
-PulsarClient.newProducer().addEncryptionKey("myapp.messagekey1").addEncryptionKey("myapp.messagekey2");
-```
-## Decrypt encrypted messages at the consumer application
-Consumers require to access one of the private keys to decrypt messages that
the producer produces. If you want to receive encrypted messages, create a
public or private key and give your public key to the producer application to
encrypt messages using your public key.
+## Troubleshoot
-## Handle failures
* Producer/Consumer loses access to the key
* Producer action fails to indicate the cause of the failure. Application
has the option to proceed with sending unencrypted messages in such cases. Call
`PulsarClient.newProducer().cryptoFailureAction(ProducerCryptoFailureAction)`
to control the producer behavior. The default behavior is to fail the request.
* If consumption fails due to decryption failure or missing keys in the
consumer, the application has the option to consume the encrypted message or
discard it. Call
`PulsarClient.newConsumer().cryptoFailureAction(ConsumerCryptoFailureAction)`
to control the consumer behavior. The default behavior is to fail the request.
Application is never able to decrypt the messages if the private key is
permanently lost.
* Batch messaging
- * If decryption fails and the message contains batch messages, client is not
able to retrieve individual messages in the batch, hence message consumption
fails even if cryptoFailureAction() is set to
`ConsumerCryptoFailureAction.CONSUME`.
+ * If decryption fails and the message contains batch messages, client is not
able to retrieve individual messages in the batch, hence message consumption
fails even if `cryptoFailureAction()` is set to
`ConsumerCryptoFailureAction.CONSUME`.
* If decryption fails, the message consumption stops and the application
notices backlog growth in addition to decryption failure messages in the client
log. If the application does not have access to the private key to decrypt the
message, the only option is to skip or discard backlogged messages.
diff --git a/site2/docs/security-overview.md b/site2/docs/security-overview.md
index 27b8521c5e9..658612df4c7 100644
--- a/site2/docs/security-overview.md
+++ b/site2/docs/security-overview.md
@@ -4,33 +4,53 @@ title: Pulsar security overview
sidebar_label: "Overview"
---
-As the central message bus for a business, Apache Pulsar is frequently used
for storing mission-critical data. Therefore, enabling security features in
Pulsar is crucial.
+As the central message bus for a business, Apache Pulsar is frequently used
for storing mission-critical data. Therefore, enabling security features in
Pulsar is crucial. This chapter describes the main security controls that
Pulsar uses to help protect your data.
-By default, Pulsar configures no encryption, authentication, or authorization.
Any client can communicate to Apache Pulsar via plain text service URLs. So we
must ensure that Pulsar accessing via these plain text service URLs is
restricted to trusted clients only. In such cases, you can use Network
segmentation and/or authorization ACLs to restrict access to trusted IPs. If
you use neither, the state of the cluster is wide open and anyone can access
the cluster.
+Pulsar security is based on the following core pillars.
+* [Encryption](#encryption)
+* [Authentication](#authentication)
+* [Authorization](#authorization)
-Pulsar supports a pluggable authentication mechanism. And Pulsar clients use
this mechanism to authenticate with brokers and proxies. You can also configure
Pulsar to support multiple authentication sources.
+By default, Pulsar configures no encryption, authentication, or authorization.
Any clients can communicate to Pulsar via plain text service URLs. So you must
ensure that Pulsar accessing via these plain text service URLs is restricted to
trusted clients only. In such cases, you can use network segmentation and/or
authorization ACLs to restrict access to trusted IPs. If you use neither, the
state of the cluster is wide open and anyone can access the cluster.
-The Pulsar broker validates the authentication credentials when a connection
is established. After the initial connection is authenticated, the "principal"
token is stored for authorization though the connection is not
re-authenticated. The broker periodically checks the expiration status of every
`ServerCnx` object. You can set the `authenticationRefreshCheckSeconds` on the
broker to control the frequency to check the expiration status. By default, the
`authenticationRefreshCheckSeconds [...]
+Apache Pulsar uses an [Authentication Provider](#authentication) or an
[Authentication Provider
Chain](security-extending.md/#proxybroker-authentication-plugin) to establish
the identity of a client and then assign a *role token* (a string like `admin`
or `app1`)to that client. This role token can represent a single client or
multiple clients and is then used for
[Authorization](security-authorization.md) to determine what the client is
authorized to do. You can use roles to control perm [...]
-The broker supports learning whether a particular client supports
authentication refreshing. If a client supports authentication refreshing and
the credential is expired, the authentication provider calls the
`refreshAuthentication` method to initiate the refreshing process. If a client
does not support authentication refreshing and the credential is expired, the
broker disconnects the client.
+## Encryption
-You had better secure the service components in your Apache Pulsar deployment.
+Encryption ensures that if an attacker gets access to your data, the attacker
cannot read the data without also having access to the encryption keys.
Encryption provides an important mechanism for protecting your data at-rest and
in-transit to meet your security requirements for cryptographic algorithms and
key management.
-## Role tokens
+**What's next?**
+* To configure end-to-end encryption, see [End-to-end
encryption](security-encryption.md) for more details.
+* To configure transport layer encryption, see [TLS
encryption](security-tls-transport.md) for more details.
-In Pulsar, a *role* is a string, like `admin` or `app1`, which can represent a
single client or multiple clients. You can use roles to control permission for
clients to produce or consume from certain topics, administer the configuration
for tenants, and so on.
+## Authentication
-Apache Pulsar uses an [Authentication Provider](#authentication-providers) or
an [Authentication Provider
Chain](security-extending.md/#proxybroker-authentication-plugin) to establish
the identity of a client and then assign a *role token* to that client. This
role token is then used for [Authorization and ACLs](security-authorization.md)
to determine what the client is authorized to do.
+Authentication is the process of verifying the identity of clients. In Pulsar,
the authentication provider is responsible for properly identifying clients and
associating the clients with role tokens. If you only enable authentication, an
authenticated role token can access all resources in the cluster.
-## Authentication providers
+Pulsar supports a pluggable authentication mechanism, and Pulsar clients use
this mechanism to authenticate with brokers and proxies.
-Currently, Pulsar supports the following authentication providers:
+Pulsar broker validates the authentication credentials when a connection is
established. After the initial connection is authenticated, the "principal"
token is stored for authorization though the connection is not
re-authenticated. The broker periodically checks the expiration status of every
`ServerCnx` object. By default, the `authenticationRefreshCheckSeconds` is set
to 60s. When the authentication is expired, the broker re-authenticates the
connection. If the re-authentication fails [...]
+
+Pulsar broker supports learning whether a particular client supports
authentication refreshing. If a client supports authentication refreshing and
the credential is expired, the authentication provider calls the
`refreshAuthentication` method to initiate the refreshing process. If a client
does not support authentication refreshing and the credential is expired, the
broker disconnects the client.
+**What's next?**
+Currently, Pulsar supports the following authentication providers:
- [TLS authentication](security-tls-authentication.md)
- [Athenz authentication](security-athenz.md)
- [Kerberos authentication](security-kerberos.md)
- [JSON Web Token (JWT) authentication](security-jwt.md)
- [OAuth 2.0 authentication](security-oauth2.md)
- [HTTP basic authentication](security-basic-auth.md)
+You can also configure Pulsar to support multiple authentication providers.
+
+:::note
+
+Starting from 2.11.0, [TLS authentication](security-tls-authentication.md)
includes [TLS encryption](security-tls-transport.md) by default. If you
configure TLS authentication first, then TLS encryption automatically applies;
if you configure TLS encryption first, you can select any one of the above
authentication providers.
+
+:::
+
+## Authorization
+[Authorization](security-authorization.md) is the process of giving
permissions to clients and determining what clients can do.
+The role tokens with the most permissions are the superusers who can create
and delete tenants, along with full access to all tenant resources. When a
superuser creates a tenant, that tenant is assigned an admin role token. A
client with the admin role token can then create, modify and destroy
namespaces, and grant and revoke permissions to other role tokens on those
namespaces.