mirror of
https://github.com/qdrant/landing_page.git
synced 2026-10-03 01:48:32 +02:00
Update Security docs (#2368)
* Rewrite security page intro to be feature-forward Replaces the generic opening paragraph with one that names each security feature (API key auth, read-only keys, JWT RBAC, network binding, TLS, audit logging) and links directly to their sections, so scanning readers see the full capability surface before hitting the warning block. Also updates the checklist items to surface read-only keys and JWT RBAC as explicit options under Authentication. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add Security FAQ section to Qdrant Fundamentals Adds three new Q&A entries covering default security posture, read-only API keys, and JWT collection-scoped access control — the exact questions users ask in Discord. Also adds Security to the page nav table and fixes the heading depth on the collection-per-user entry (## → ###). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add Security section to production checklist Inserts a new section 2 covering the five key security steps — API key auth, read-only keys, JWT access control, TLS, and network binding — with direct links to the Security page. Renumbers existing sections 2–4 to 3–5. Closes the gap where a user following the checklist step-by-step could go to production with an open, unauthenticated instance. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Link production checklist from both quickstart pages Adds a production checklist callout to the "Next Steps" section of the local quickstart and a bullet to the "What's Next?" section of the cloud quickstart, so users completing either tutorial have a clear path to production readiness. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Make code snippets testable * Lead Security page by listing all the features; Rename API keys->Admin API keys, and 'Granular Access Control with JWT' section into 'Granular Access API Keys' * Update links * Update meta description * Fix C# snippet --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
e64179bce0
commit
72ea635755
@@ -108,7 +108,7 @@ Qdrant is an AI-native vector search and a semantic search engine. You can use i
|
||||
||||
|
||||
|:-|:-|:-|
|
||||
|[Filterable HNSW](/documentation/search/filtering/) </br> Single-stage payload filtering | [Recommendations & Context Search](/documentation/search/explore/#explore-the-data) </br> Exploratory advanced search| [Pure-Vector Hybrid Search](/documentation/search/hybrid-queries/)</br>Full text and semantic search in one|
|
||||
|[Multitenancy](/documentation/manage-data/multitenancy/) </br> Payload-based partitioning|[Custom Sharding](/documentation/distributed_deployment/#sharding) </br> For data isolation and distribution|[Role Based Access Control](/documentation/security/?q=jwt#granular-access-control-with-jwt)</br>Secure JWT-based access |
|
||||
|[Multitenancy](/documentation/manage-data/multitenancy/) </br> Payload-based partitioning|[Custom Sharding](/documentation/distributed_deployment/#sharding) </br> For data isolation and distribution|[Role Based Access Control](/documentation/security/?q=jwt#granular-access-api-keys)</br>Secure JWT-based access |
|
||||
|[Quantization](/documentation/manage-data/quantization/) </br> Compress data for drastic speedups|[Multivector Support](/documentation/manage-data/vectors/?q=multivect#multivectors) </br> For ColBERT late interaction |[Built-in IDF](/documentation/manage-data/indexing/?q=inverse+docu#idf-modifier) </br> Advanced similarity calculation|
|
||||
|
||||
## Developer guidebooks:
|
||||
|
||||
@@ -1040,4 +1040,5 @@ You've just performed semantic search on real menu item data. The query "vegetar
|
||||
|
||||
- Explore [filtering](/documentation/search/filtering/) to combine semantic search with structured queries
|
||||
- Learn about [collections](/documentation/manage-data/collections/) and advanced configuration options
|
||||
- Check out more [examples and tutorials](/documentation/tutorials-lp-overview/)
|
||||
- Check out more [examples and tutorials](/documentation/tutorials-lp-overview/)
|
||||
- Run through the [Production Checklist](/documentation/production-checklist/) before deploying to production
|
||||
@@ -204,7 +204,7 @@ The account owner will receive automatic alerts via email if your cluster has an
|
||||
|
||||
Learn about the SDKs [here](/documentation/interfaces/).
|
||||
|
||||
Learn more about JWT Keys and permissions [here](/documentation/security/?q=jwt#granular-access-control-with-jwt).
|
||||
Learn more about JWT Keys and permissions [here](/documentation/security/?q=jwt#granular-access-api-keys).
|
||||
|
||||
- title: A Node is CPU Throttled
|
||||
content: |
|
||||
|
||||
@@ -8,9 +8,9 @@ aliases:
|
||||
---
|
||||
|
||||
# Frequently Asked Questions: General Topics
|
||||
||||||
|
||||
|-|-|-|-|-|
|
||||
|[Vectors](/documentation/faq/qdrant-fundamentals/#vectors)|[Search](/documentation/faq/qdrant-fundamentals/#search)|[Collections](/documentation/faq/qdrant-fundamentals/#collections)|[Compatibility](/documentation/faq/qdrant-fundamentals/#compatibility)|[Cloud](/documentation/faq/qdrant-fundamentals/#cloud)|
|
||||
|||||||
|
||||
|-|-|-|-|-|-|
|
||||
|[Vectors](/documentation/faq/qdrant-fundamentals/#vectors)|[Search](/documentation/faq/qdrant-fundamentals/#search)|[Collections](/documentation/faq/qdrant-fundamentals/#collections)|[Compatibility](/documentation/faq/qdrant-fundamentals/#compatibility)|[Security](/documentation/faq/qdrant-fundamentals/#security)|[Cloud](/documentation/faq/qdrant-fundamentals/#cloud)|
|
||||
|
||||
## Vectors
|
||||
|
||||
@@ -297,11 +297,25 @@ You should always index first if you know your filters upfront. If you need to i
|
||||
|
||||
Changing `m` or `ef_construct` automatically triggers a full background HNSW rebuild. For cases where only a payload index is being added, make a minimal change to `ef_construct` (for example, from 100 to 101). Queries continue to be served by the old index until the new index is complete, so there is no downtime. Don't immediately change the value of `ef_construct` back to its original value, but keep it set to the new value.
|
||||
|
||||
## Should I create one Qdrant collection per user?
|
||||
### Should I create one Qdrant collection per user?
|
||||
No. Creating one collection per user is more resource intensive.
|
||||
|
||||
Instead of creating separate collections for each user, we recommend creating a [single collection](/documentation/manage-data/multitenancy/) and separate access using payloads. Each Qdrant point can have a payload as metadata. For multitenancy, you can include a `user_id` or `tenant_id` for each point. To optimize storage further, you can enable [tenant indexing](/documentation/manage-data/indexing/#tenant-index) for payload fields.
|
||||
|
||||
## Security
|
||||
|
||||
### Is my Qdrant cluster secure by default?
|
||||
|
||||
It depends on your deployment. Qdrant Cloud clusters are always secure by default. Self-hosted deployments are not: they're open to all network interfaces and have no authentication configured until you set it up. See [Security](/documentation/security/) for a full configuration guide.
|
||||
|
||||
### Does Qdrant support read-only access?
|
||||
|
||||
Yes. You can configure a read-only API key that permits queries but blocks all write operations. Both keys can be active at the same time, so you can issue a read-only key to consumers without rotating your primary key. See [Read-Only API Key](/documentation/security/#read-only-api-key).
|
||||
|
||||
### Can I restrict access to specific collections?
|
||||
|
||||
Yes. Qdrant supports JWT-based access control that lets you issue signed tokens scoped to individual collections with read or write permissions. This is useful for multi-tenant deployments where each user or service should only access their own data. See [Granular Access API Keys](/documentation/security/#granular-access-api-keys).
|
||||
|
||||
## Cloud
|
||||
|
||||
### Is it possible to scale down a Qdrant Cloud cluster?
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
Authenticate to a Qdrant instance by passing an API key in the `api-key` request header.
|
||||
@@ -0,0 +1,2 @@
|
||||
curl -X GET https://xyz-example.eu-central.aws.cloud.qdrant.io:6333 \
|
||||
--header 'api-key: your_api_key_here'
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
using Qdrant.Client;
|
||||
|
||||
public class Snippet
|
||||
{
|
||||
public static async Task Run()
|
||||
{
|
||||
var client = new QdrantClient(
|
||||
host: "xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
port: 6334,
|
||||
https: true,
|
||||
apiKey: "your_api_key_here");
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
```bash
|
||||
curl -X GET https://xyz-example.eu-central.aws.cloud.qdrant.io:6333 \
|
||||
--header 'api-key: your_api_key_here'
|
||||
```
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
|
||||
var client = new QdrantClient(
|
||||
host: "xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
port: 6334,
|
||||
https: true,
|
||||
apiKey: "your_api_key_here");
|
||||
```
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
```go
|
||||
import (
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
Port: 6334,
|
||||
APIKey: "your_api_key_here",
|
||||
UseTLS: true,
|
||||
})
|
||||
```
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
```java
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
|
||||
QdrantClient client = new QdrantClient(
|
||||
QdrantGrpcClient.newBuilder("xyz-example.eu-central.aws.cloud.qdrant.io", 6334, true)
|
||||
.withApiKey("your_api_key_here")
|
||||
.build());
|
||||
```
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
```python
|
||||
from qdrant_client import QdrantClient
|
||||
|
||||
client = QdrantClient(
|
||||
url="https://xyz-example.eu-central.aws.cloud.qdrant.io:6333",
|
||||
api_key="your_api_key_here",
|
||||
)
|
||||
```
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
```rust
|
||||
use qdrant_client::Qdrant;
|
||||
|
||||
let client = Qdrant::from_url("https://xyz-example.eu-central.aws.cloud.qdrant.io:6334")
|
||||
.api_key("your_api_key_here")
|
||||
.build()?;
|
||||
```
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({
|
||||
url: "https://xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
port: 6333,
|
||||
apiKey: "your_api_key_here",
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,17 @@
|
||||
package snippet
|
||||
|
||||
import (
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
func Main() {
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
Port: 6334,
|
||||
APIKey: "your_api_key_here",
|
||||
UseTLS: true,
|
||||
})
|
||||
|
||||
if err != nil { panic(err) } // @hide
|
||||
_ = client // @hide
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.example.snippets_amalgamation;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
|
||||
public class Snippet {
|
||||
public static void run() throws Exception {
|
||||
QdrantClient client = new QdrantClient(
|
||||
QdrantGrpcClient.newBuilder("xyz-example.eu-central.aws.cloud.qdrant.io", 6334, true)
|
||||
.withApiKey("your_api_key_here")
|
||||
.build());
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
from qdrant_client import QdrantClient
|
||||
|
||||
client = QdrantClient(
|
||||
url="https://xyz-example.eu-central.aws.cloud.qdrant.io:6333",
|
||||
api_key="your_api_key_here",
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
use qdrant_client::Qdrant;
|
||||
|
||||
pub async fn main() -> anyhow::Result<()> {
|
||||
let client = Qdrant::from_url("https://xyz-example.eu-central.aws.cloud.qdrant.io:6334")
|
||||
.api_key("your_api_key_here")
|
||||
.build()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({
|
||||
url: "https://xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
port: 6333,
|
||||
apiKey: "your_api_key_here",
|
||||
});
|
||||
+1
@@ -0,0 +1 @@
|
||||
Authenticate to a Qdrant instance by passing a Bearer token in the `Authorization` request header.
|
||||
@@ -0,0 +1,2 @@
|
||||
curl -X GET https://xyz-example.eu-central.aws.cloud.qdrant.io:6333 \
|
||||
--header 'Authorization: Bearer your_token_here'
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
using Qdrant.Client;
|
||||
|
||||
public class Snippet
|
||||
{
|
||||
public static async Task Run()
|
||||
{
|
||||
var client = new QdrantClient(
|
||||
host: "xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
port: 6334,
|
||||
https: true,
|
||||
apiKey: null,
|
||||
grpcTimeout: default,
|
||||
loggerFactory: null,
|
||||
headers: new Dictionary<string, string>
|
||||
{
|
||||
{ "authorization", "Bearer your_token_here" }
|
||||
});
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
```bash
|
||||
curl -X GET https://xyz-example.eu-central.aws.cloud.qdrant.io:6333 \
|
||||
--header 'Authorization: Bearer your_token_here'
|
||||
```
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
|
||||
var client = new QdrantClient(
|
||||
host: "xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
port: 6334,
|
||||
https: true,
|
||||
apiKey: null,
|
||||
grpcTimeout: default,
|
||||
loggerFactory: null,
|
||||
headers: new Dictionary<string, string>
|
||||
{
|
||||
{ "authorization", "Bearer your_token_here" }
|
||||
});
|
||||
```
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
```go
|
||||
import (
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
Port: 6334,
|
||||
UseTLS: true,
|
||||
Headers: map[string]string{
|
||||
"authorization": "Bearer your_token_here",
|
||||
},
|
||||
})
|
||||
```
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
```java
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import java.util.Map;
|
||||
|
||||
QdrantClient client = new QdrantClient(
|
||||
QdrantGrpcClient.newBuilder("xyz-example.eu-central.aws.cloud.qdrant.io", 6334, true)
|
||||
.withHeaders(Map.of("authorization", "Bearer your_token_here"))
|
||||
.build());
|
||||
```
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
```python
|
||||
from qdrant_client import QdrantClient
|
||||
|
||||
client = QdrantClient(
|
||||
url="https://xyz-example.eu-central.aws.cloud.qdrant.io:6333",
|
||||
auth_token_provider=lambda: "your_token_here",
|
||||
)
|
||||
```
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
```rust
|
||||
use qdrant_client::Qdrant;
|
||||
|
||||
let client = Qdrant::from_url("https://xyz-example.eu-central.aws.cloud.qdrant.io:6334")
|
||||
.header("authorization", "Bearer your_token_here")
|
||||
.build()?;
|
||||
```
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({
|
||||
url: "https://xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
port: 6333,
|
||||
headers: {
|
||||
authorization: "Bearer your_token_here",
|
||||
},
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,19 @@
|
||||
package snippet
|
||||
|
||||
import (
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
func Main() {
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
Port: 6334,
|
||||
UseTLS: true,
|
||||
Headers: map[string]string{
|
||||
"authorization": "Bearer your_token_here",
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil { panic(err) } // @hide
|
||||
_ = client // @hide
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.example.snippets_amalgamation;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import java.util.Map;
|
||||
|
||||
public class Snippet {
|
||||
public static void run() throws Exception {
|
||||
QdrantClient client = new QdrantClient(
|
||||
QdrantGrpcClient.newBuilder("xyz-example.eu-central.aws.cloud.qdrant.io", 6334, true)
|
||||
.withHeaders(Map.of("authorization", "Bearer your_token_here"))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
from qdrant_client import QdrantClient
|
||||
|
||||
client = QdrantClient(
|
||||
url="https://xyz-example.eu-central.aws.cloud.qdrant.io:6333",
|
||||
auth_token_provider=lambda: "your_token_here",
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
use qdrant_client::Qdrant;
|
||||
|
||||
pub async fn main() -> anyhow::Result<()> {
|
||||
let client = Qdrant::from_url("https://xyz-example.eu-central.aws.cloud.qdrant.io:6334")
|
||||
.header("authorization", "Bearer your_token_here")
|
||||
.build()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({
|
||||
url: "https://xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
port: 6333,
|
||||
headers: {
|
||||
authorization: "Bearer your_token_here",
|
||||
},
|
||||
});
|
||||
@@ -66,7 +66,7 @@ If you want to retrieve the secret again, you can also use `kubectl`:
|
||||
kubectl get secret qdrant-api-key -o jsonpath="{.data.api-key}" --namespace the-qdrant-namespace | base64 --decode
|
||||
```
|
||||
|
||||
After configuring the API key secret, you can create JWTs with granular access control in the Qdrant Cluster UI. Please refer to the [Granular access control with JWT](/documentation/security/#granular-access-control-with-jwt) documentation for more details.
|
||||
After configuring the API key secret, you can create JWTs with granular access control in the Qdrant Cluster UI. Please refer to the [Granular access API Keys](/documentation/security/#granular-access-api-keys) documentation for more details.
|
||||
|
||||
#### Watch the Video
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ spec:
|
||||
jwt_rbac: true
|
||||
```
|
||||
|
||||
If you set the `jwt_rbac` flag, you will also be able to create granular [JWT tokens for role based access control](/documentation/security/#granular-access-control-with-jwt).
|
||||
If you set the `jwt_rbac` flag, you will also be able to create granular [JWT tokens for role based access control](/documentation/security/#granular-access-api-keys).
|
||||
|
||||
### Configuring TLS for Database Access
|
||||
|
||||
|
||||
@@ -29,7 +29,28 @@ Distribute incoming requests evenly across cluster nodes to ensure consistent pe
|
||||
|
||||
---
|
||||
|
||||
## 2. Quantization
|
||||
## 2. Security
|
||||
|
||||
Lock down access before exposing your instance to any network. By default, self-hosted open source Qdrant instances are open to all interfaces without any authentication. On Qdrant Cloud, security features are enabled by default.
|
||||
|
||||
- **[Set Up an API Key](/documentation/security/#authentication).**
|
||||
The minimum step required before any production deployment. Without it, an open source instance accepts requests from anyone who can reach it. On Qdrant Cloud, API key authentication is enabled by default.
|
||||
|
||||
- **Use a Read-Only API Key for Query-Only Consumers.**
|
||||
Issue a separate [read-only key](/documentation/security/#read-only-api-key) for services or users that only need to query data. This limits the blast radius if a key is compromised. Both keys can be active simultaneously.
|
||||
|
||||
- **Use [Fine-Grained Access Control](/documentation/security/#granular-access-api-keys).**
|
||||
Grant read or write permissions on individual collections, giving each client access only to what it needs.
|
||||
|
||||
- **Enable TLS.**
|
||||
[Encrypt traffic](/documentation/security/#tls) between clients and your Qdrant instance (enabled by default on Qdrant Cloud).
|
||||
|
||||
- **Bind to a Private Network Interface.**
|
||||
When self-hosting, prevent external access by [binding Qdrant to a private IP or loopback address](/documentation/security/#network-bind).
|
||||
|
||||
---
|
||||
|
||||
## 3. Quantization
|
||||
|
||||
Compress vectors to reduce memory footprint. [Quantization](/documentation/manage-data/quantization/) is one of the most impactful changes you can make before going to production.
|
||||
|
||||
@@ -40,7 +61,7 @@ Some models produce embeddings that can't be quantized efficiently. [Verify](/do
|
||||
|
||||
---
|
||||
|
||||
## 3. Storage and Hardware
|
||||
## 4. Storage and Hardware
|
||||
|
||||
Right-size your RAM, disk type, and storage mode. These decisions are difficult to change once you're in production.
|
||||
|
||||
@@ -61,7 +82,7 @@ When storing vectors and the HNSW index on disk, improve search performance by [
|
||||
|
||||
---
|
||||
|
||||
## 4. Query Optimization
|
||||
## 5. Query Optimization
|
||||
|
||||
Ensure your search is fast, accurate, and efficient under production load.
|
||||
|
||||
|
||||
@@ -608,4 +608,6 @@ Now you know how Qdrant works. Getting started with [Qdrant Cloud](/documentatio
|
||||
|
||||
To move onto some more complex examples of vector search, read our [Tutorials](/documentation/tutorials-lp-overview/) and create your own app with the help of our [Examples](/documentation/examples/).
|
||||
|
||||
When you're ready to deploy, run through the [Production Checklist](/documentation/production-checklist/) to make sure your instance is secure and optimized.
|
||||
|
||||
**Note:** There is another way of running Qdrant locally. If you are a Python developer, we recommend that you try Local Mode in [Qdrant Client](https://github.com/qdrant/qdrant-client), as it only takes a few moments to get setup.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Security
|
||||
short_description: "Harden self-hosted Qdrant with API keys, network binding, TLS encryption, and JWT-based access control."
|
||||
description: "Secure a Qdrant instance with API key authentication, network binding, TLS encryption, and fine-grained JWT-based access control for production use."
|
||||
description: "Secure a Qdrant instance with Admin API keys, read-only API keys, JWT-based access control for collection-scoped permissions, network binding, and TLS encryption."
|
||||
partition: deploy
|
||||
weight: 145
|
||||
aliases:
|
||||
@@ -9,45 +9,59 @@ aliases:
|
||||
- /documentation/operations/security
|
||||
---
|
||||
|
||||
# Security
|
||||
# Security & Access Control
|
||||
|
||||
Qdrant supports various security features to help you secure your instance. Most
|
||||
of these must to be explicitly configured to make your instance production
|
||||
ready. Please read the following section carefully.
|
||||
Securing a Qdrant deployment means controlling who can access your data, encrypting traffic, and keeping an audit trail for compliance. To secure your deployments, Qdrant supports [API key authentication](#authentication) (including [read-only API keys](#read-only-api-key) for query-only consumers and [granular access API keys](#granular-access-api-keys) with per-collection read/write scoping), [network binding](#network-bind), [TLS](#tls) for encrypted connections, and [audit logging](#audit-logging) for compliance. On Qdrant Cloud, these features are enabled by default. On self-hosted open source deployments, they must be explicitly configured before going to production.
|
||||
|
||||
## Secure Your Instance
|
||||
|
||||
<aside role="alert">Custom deployments are <b>not</b> secure by default and are <b>not</b> production ready. Qdrant Cloud deployments are always secure and production ready.</aside>
|
||||
<aside role="alert">Self-hosted open source deployments are <b>not</b> secure by default and are <b>not</b> production-ready. Qdrant Cloud deployments are always secure and production-ready.</aside>
|
||||
|
||||
By default, all self-deployed Qdrant instances are not secure. They are open to
|
||||
all network interfaces and do not have any kind of authentication configured. They
|
||||
may be open to everybody on the internet without any restrictions. You must
|
||||
therefore take security measures to make your instance production-ready.
|
||||
Please read through this section carefully for instructions on how to secure
|
||||
Please read this section carefully for instructions on how to secure
|
||||
your instance.
|
||||
|
||||
Instances deployed via Qdrant Cloud are always secure by default. Refer to
|
||||
Qdrant Cloud deployments are always secure by default. Refer to
|
||||
[Authentication](/documentation/cloud/authentication/) and [Client IP
|
||||
Restrictions](/documentation/cloud/configure-cluster/#client-ip-restrictions).
|
||||
|
||||
To properly secure your own instance, we strongly recommend taking the following steps:
|
||||
|
||||
1. [Authentication](#authentication): set up an API key to prevent unauthorized access.
|
||||
The most important step to prevent unauthenticated actors from accessing your data.
|
||||
2. [Network Bind](#network-bind): bind to a specific network interface or IP address.
|
||||
1. [Authentication](#authentication): Set up an API key to prevent unauthorized access.
|
||||
1. [Audit Logging](#audit-logging): Record all API operations to a log file for compliance and forensics.
|
||||
1. [Network Bind](#network-bind): Bind to a specific network interface or IP address.
|
||||
When developing locally, bind to `127.0.0.1` to prevent all external access.
|
||||
When deploying to production, bind to a private network interface or IP.
|
||||
3. [TLS](#tls): enable encrypted traffic everywhere using TLS.
|
||||
1. [TLS](#tls): Encrypt traffic everywhere using TLS.
|
||||
|
||||
## Authentication
|
||||
|
||||
By default, an open source Qdrant deployment accepts requests from anyone who can reach it. To secure your instance, enable API key authentication. On Qdrant Cloud, API key authentication is enabled by default.
|
||||
|
||||
Qdrant supports three types of API key:
|
||||
|
||||
- **[Admin API Key](#admin-api-key)**: Grants full access to all operations and collections.
|
||||
- **[Read-Only API Key](#read-only-api-key)**: Grants read-only access to all operations and collections. This key can be used for services or users that only need to query data.
|
||||
- **[Granular Access API Keys](#granular-access-api-keys)**: For more granular access control, you can use API keys that specify read or write permissions on individual collections.
|
||||
|
||||
### Authenticate with an API Key
|
||||
|
||||
To authenticate with an API key, whether it's an admin key, read-only key, or granular access token, provide it in the `api-key` request header:
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/authentication/api-key/" >}}
|
||||
|
||||
Alternatively, use the `Authorization: Bearer` header:
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/authentication/bearer/" >}}
|
||||
|
||||
### Admin API Key
|
||||
|
||||
*Available as of v1.2.0*
|
||||
|
||||
Qdrant supports a simple form of client authentication using a static API key.
|
||||
This can be used to secure your instance.
|
||||
|
||||
To enable API key based authentication in your own Qdrant instance you must
|
||||
specify a key in the configuration:
|
||||
The admin API key is the primary key that grants full access to all operations and collections. It is configured with the `api_key` setting in the configuration file.
|
||||
|
||||
```yaml
|
||||
service:
|
||||
@@ -61,7 +75,7 @@ service:
|
||||
api_key: your_secret_api_key_here
|
||||
```
|
||||
|
||||
Or alternatively, you can use the environment variable:
|
||||
Or alternatively, you can use the `QDRANT__SERVICE__API_KEY` environment variable:
|
||||
|
||||
```bash
|
||||
docker run -p 6333:6333 \
|
||||
@@ -71,112 +85,18 @@ docker run -p 6333:6333 \
|
||||
|
||||
<aside role="alert"><a href="#tls">TLS</a> must be used to prevent leaking the API key over an unencrypted connection.</aside>
|
||||
|
||||
For using API key based authentication in Qdrant Cloud see the cloud
|
||||
For using API key based authentication on Qdrant Cloud, see the Cloud
|
||||
[Authentication](/documentation/cloud/authentication/)
|
||||
section.
|
||||
|
||||
The API key then needs to be present in all REST or gRPC requests to your instance.
|
||||
All official Qdrant clients for Python, Go, Rust, .NET and Java support the API key parameter.
|
||||
|
||||
<!---
|
||||
Examples with clients
|
||||
-->
|
||||
|
||||
```bash
|
||||
curl \
|
||||
-X GET https://localhost:6333 \
|
||||
--header 'api-key: your_secret_api_key_here'
|
||||
```
|
||||
|
||||
```python
|
||||
from qdrant_client import QdrantClient
|
||||
|
||||
client = QdrantClient(
|
||||
url="https://localhost:6333",
|
||||
api_key="your_secret_api_key_here",
|
||||
)
|
||||
```
|
||||
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({
|
||||
url: "http://localhost",
|
||||
port: 6333,
|
||||
apiKey: "your_secret_api_key_here",
|
||||
});
|
||||
```
|
||||
|
||||
```rust
|
||||
use qdrant_client::Qdrant;
|
||||
|
||||
let client = Qdrant::from_url("https://xyz-example.eu-central.aws.cloud.qdrant.io:6334")
|
||||
.api_key("<paste-your-api-key-here>")
|
||||
.build()?;
|
||||
```
|
||||
|
||||
```java
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
|
||||
QdrantClient client =
|
||||
new QdrantClient(
|
||||
QdrantGrpcClient.newBuilder(
|
||||
"xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
6334,
|
||||
true)
|
||||
.withApiKey("<paste-your-api-key-here>")
|
||||
.build());
|
||||
```
|
||||
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
|
||||
var client = new QdrantClient(
|
||||
host: "xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
https: true,
|
||||
apiKey: "<paste-your-api-key-here>"
|
||||
);
|
||||
```
|
||||
|
||||
```go
|
||||
import "github.com/qdrant/go-client/qdrant"
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
Port: 6334,
|
||||
APIKey: "<paste-your-api-key-here>",
|
||||
UseTLS: true,
|
||||
})
|
||||
```
|
||||
|
||||
<aside role="alert">Internal communication channels are <strong>never</strong> protected by an API key nor bearer tokens. Internal gRPC uses port 6335 by default if running in distributed mode. You must ensure that this port is not publicly reachable and can only be used for node communication. By default, this setting is disabled for Qdrant Cloud and the Qdrant Helm chart.</aside>
|
||||
|
||||
### Read-Only API Key
|
||||
|
||||
*Available as of v1.7.0*
|
||||
|
||||
In addition to the regular API key, Qdrant also supports a read-only API key.
|
||||
This key can be used to access read-only operations on the instance.
|
||||
|
||||
```yaml
|
||||
service:
|
||||
read_only_api_key: your_secret_read_only_api_key_here
|
||||
```
|
||||
|
||||
Or with the environment variable:
|
||||
|
||||
```bash
|
||||
export QDRANT__SERVICE__READ_ONLY_API_KEY=your_secret_read_only_api_key_here
|
||||
```
|
||||
|
||||
Both API keys can be used simultaneously.
|
||||
|
||||
### Rotate an API Key
|
||||
#### Rotate an Admin API Key
|
||||
|
||||
*Available as of v1.17.0*
|
||||
|
||||
In a distributed deployment, you can rotate an API key without downtime. Use the `alt_api_key` setting to temporarily configure a second API key that acts identically to the primary `api_key`, allowing both the old and new API keys to be active at the same time.
|
||||
In a distributed deployment, you can rotate an admin API key without downtime. Use the `alt_api_key` setting to temporarily configure a second API key that acts identically to the primary `api_key`, allowing both the old and new API keys to be active at the same time.
|
||||
|
||||
```yaml
|
||||
service:
|
||||
@@ -192,15 +112,33 @@ To rotate an API key without downtime:
|
||||
|
||||
<aside role="alert">JWT tokens are tied to the key they were signed with and are <strong>not</strong> automatically migrated. They must be re-created after switching to the new key.</aside>
|
||||
|
||||
### Granular Access Control with JWT
|
||||
### Read-Only API Key
|
||||
|
||||
*Available as of v1.7.0*
|
||||
|
||||
Qdrant also supports a read-only API key.
|
||||
This key can be used to access read-only operations on the instance.
|
||||
|
||||
```yaml
|
||||
service:
|
||||
read_only_api_key: your_secret_read_only_api_key_here
|
||||
```
|
||||
|
||||
Or with the environment variable:
|
||||
|
||||
```bash
|
||||
export QDRANT__SERVICE__READ_ONLY_API_KEY=your_secret_read_only_api_key_here
|
||||
```
|
||||
|
||||
Admin and read-only API keys can be used simultaneously.
|
||||
|
||||
### Granular Access API Keys
|
||||
|
||||
*Available as of v1.9.0*
|
||||
|
||||
For more complex cases, Qdrant supports granular access control with [JSON Web Tokens (JWT)](https://jwt.io/).
|
||||
This allows you to create tokens which restrict access to data stored in your cluster, and build [Role-based access control (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control) on top of that.
|
||||
In this way, you can define permissions for users and restrict access to sensitive endpoints.
|
||||
Granular access API keys let you assign read or write permissions on individual collections, enabling [Role-based access control (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control). They're built on the [JSON Web Tokens (JWT)](https://jwt.io/) standard.
|
||||
|
||||
To enable JWT-based authentication in your own Qdrant instance you need to specify the `api-key` and enable the `jwt_rbac` feature in the configuration:
|
||||
On Qdrant Cloud, granular access API key authentication is enabled by default. To enable granular access API key authentication on open source Qdrant instances, specify an `api-key` and enable the `jwt_rbac` feature in the configuration:
|
||||
|
||||
```yaml
|
||||
service:
|
||||
@@ -208,7 +146,7 @@ service:
|
||||
jwt_rbac: true
|
||||
```
|
||||
|
||||
Or with the environment variables:
|
||||
Or with environment variables:
|
||||
|
||||
```bash
|
||||
export QDRANT__SERVICE__API_KEY=your_secret_api_key_here
|
||||
@@ -217,81 +155,9 @@ export QDRANT__SERVICE__JWT_RBAC=true
|
||||
|
||||
The `api_key` you set in the configuration will be used to encode and decode the JWTs, so –needless to say– keep it secure. If your `api_key` changes, all existing tokens will be invalid.
|
||||
|
||||
To use JWT-based authentication, you need to provide it as a bearer token in the `Authorization` header, or as an key in the `Api-Key` header of your requests.
|
||||
|
||||
```http
|
||||
Authorization: Bearer <JWT>
|
||||
|
||||
// or
|
||||
|
||||
Api-Key: <JWT>
|
||||
```
|
||||
|
||||
```python
|
||||
from qdrant_client import QdrantClient
|
||||
|
||||
qdrant_client = QdrantClient(
|
||||
"xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
api_key="<JWT>",
|
||||
)
|
||||
```
|
||||
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({
|
||||
host: "xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
apiKey: "<JWT>",
|
||||
});
|
||||
```
|
||||
|
||||
```rust
|
||||
use qdrant_client::Qdrant;
|
||||
|
||||
let client = Qdrant::from_url("https://xyz-example.eu-central.aws.cloud.qdrant.io:6334")
|
||||
.api_key("<JWT>")
|
||||
.build()?;
|
||||
```
|
||||
|
||||
```java
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
|
||||
QdrantClient client =
|
||||
new QdrantClient(
|
||||
QdrantGrpcClient.newBuilder(
|
||||
"xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
6334,
|
||||
true)
|
||||
.withApiKey("<JWT>")
|
||||
.build());
|
||||
```
|
||||
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
|
||||
var client = new QdrantClient(
|
||||
host: "xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
https: true,
|
||||
apiKey: "<JWT>"
|
||||
);
|
||||
```
|
||||
|
||||
```go
|
||||
import "github.com/qdrant/go-client/qdrant"
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "xyz-example.eu-central.aws.cloud.qdrant.io",
|
||||
Port: 6334,
|
||||
APIKey: "<JWT>",
|
||||
UseTLS: true,
|
||||
})
|
||||
```
|
||||
#### Generating JSON Web Tokens
|
||||
|
||||
Due to the nature of JWT, anyone who knows the `api_key` can generate tokens by using any of the existing libraries and tools, it is not necessary for them to have access to the Qdrant instance to generate them.
|
||||
|
||||
For convenience, we have added a JWT generation tool the Qdrant Web UI under the 🔑 tab, if you're using the default url, it will be at `http://localhost:6333/dashboard#/jwt`.
|
||||
JWTs can be generated with the admin API key. You can use any of the existing libraries and tools to generate tokens. You can also use the Qdrant Web UI to generate JWTs by selecting **Access Tokens**.
|
||||
|
||||
- **JWT Header** - Qdrant uses the `HS256` algorithm to decode the tokens.
|
||||
|
||||
@@ -302,7 +168,7 @@ For convenience, we have added a JWT generation tool the Qdrant Web UI under the
|
||||
}
|
||||
```
|
||||
|
||||
- **JWT Payload** - You can include any combination of the [parameters available](#jwt-configuration) in the payload. Keep reading for more info on each one.
|
||||
- **JWT Payload** - You can include any combination of the [available parameters](#jwt-configuration) in the payload.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -312,11 +178,11 @@ For convenience, we have added a JWT generation tool the Qdrant Web UI under the
|
||||
}
|
||||
```
|
||||
|
||||
**Signing the token** - To confirm that the generated token is valid, it needs to be signed with the `api_key` you have set in the configuration.
|
||||
That would mean, that someone who knows the `api_key` gives the authorization for the new token to be used in the Qdrant instance.
|
||||
Qdrant can validate the signature, because it knows the `api_key` and can decode the token.
|
||||
**Signing the token** - To confirm that the generated token is valid, it needs to be signed with the `api_key` set in the configuration.
|
||||
This means that someone who knows the admin API key can authorize the new token for use with the Qdrant instance.
|
||||
Qdrant can validate the signature because it knows the admin API key and can decode the token.
|
||||
|
||||
The process of token generation can be done on the client side offline, and doesn't require any communication with the Qdrant instance.
|
||||
The process of token generation can be done on the client side offline and doesn't require any communication with the Qdrant instance.
|
||||
|
||||
Here is an example of libraries that can be used to generate JWT tokens:
|
||||
|
||||
@@ -346,7 +212,7 @@ These are the available options, or **claims** in the JWT lingo. You can use the
|
||||
}
|
||||
```
|
||||
|
||||
- **`value_exists`** - This is a claim that can be used to validate the token against the data stored in a collection. Structure of this claim is as follows:
|
||||
- **`value_exists`** - This is a claim that can be used to validate the token against the data stored in a collection. The structure of this claim is as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -359,7 +225,7 @@ These are the available options, or **claims** in the JWT lingo. You can use the
|
||||
}
|
||||
```
|
||||
|
||||
If this claim is present, Qdrant will check if there is a point in the collection with the specified key-values. If it does, the token is valid.
|
||||
If this claim is present, Qdrant will check if there is a point in the collection with the specified key-values. If such a point exists, the token is valid.
|
||||
|
||||
This claim is especially useful if you want to have an ability to revoke tokens without changing the `api_key`.
|
||||
Consider a case where you have a collection of users, and you want to revoke access to a specific user.
|
||||
@@ -376,7 +242,7 @@ These are the available options, or **claims** in the JWT lingo. You can use the
|
||||
}
|
||||
```
|
||||
|
||||
You can create a token with this claim, and when you want to revoke access, you can change the `role` of the user to something else, and the token will be invalid.
|
||||
You can create a token with this claim, and when you want to revoke access, you can change the `role` of the user to something else, and the token will become invalid.
|
||||
|
||||
- **`access`** - This claim defines the [access level](#table-of-access) of the token. If this claim is present, Qdrant will check if the token has the required access level to perform the operation. If this claim is **not** present, **manage** access is assumed.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user