安全性
请仔细阅读本页面。虽然有多种方法可以保护您的 Qdrant 实例,但它们**默认是不安全的**。在生产环境使用之前,您需要启用安全措施。否则,它们将对任何人完全开放
身份验证
从 v1.2.0 版本开始可用
Qdrant 支持使用静态 API 密钥进行简单的客户端身份验证。这可用于保护您的实例。
要在您自己的 Qdrant 实例中启用基于 API 密钥的身份验证,必须在配置中指定一个密钥
service:
# Set an api-key.
# If set, all requests must include a header with the api-key.
# example header: `api-key: <API-KEY>`
#
# If you enable this you should also enable TLS.
# (Either above or via an external service like nginx.)
# Sending an api-key over an unencrypted channel is insecure.
api_key: your_secret_api_key_here
或者,您可以使用环境变量
docker run -p 6333:6333 \
-e QDRANT__SERVICE__API_KEY=your_secret_api_key_here \
qdrant/qdrant
关于在 Qdrant Cloud 中使用基于 API 密钥的身份验证,请参阅云身份验证部分。
API 密钥必须出现在所有发送到您实例的 REST 或 gRPC 请求中。所有官方 Qdrant 客户端(包括 Python、Go、Rust、.NET 和 Java)都支持 API 密钥参数。
curl \
-X GET https://localhost:6333 \
--header 'api-key: your_secret_api_key_here'
from qdrant_client import QdrantClient
client = QdrantClient(
url="https://localhost:6333",
api_key="your_secret_api_key_here",
)
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({
url: "http://localhost",
port: 6333,
apiKey: "your_secret_api_key_here",
});
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()?;
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());
using Qdrant.Client;
var client = new QdrantClient(
host: "xyz-example.eu-central.aws.cloud.qdrant.io",
https: true,
apiKey: "<paste-your-api-key-here>"
);
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,
})
只读 API 密钥
从 v1.7.0 版本开始可用
除了常规 API 密钥外,Qdrant 还支持只读 API 密钥。此密钥可用于访问实例上的只读操作。
service:
read_only_api_key: your_secret_read_only_api_key_here
或者使用环境变量
export QDRANT__SERVICE__READ_ONLY_API_KEY=your_secret_read_only_api_key_here
两个 API 密钥可以同时使用。
使用 JWT 进行细粒度访问控制
从 v1.9.0 版本开始可用
对于更复杂的用例,Qdrant 支持使用 JSON Web Token (JWT) 进行细粒度访问控制。这允许您创建令牌,限制对集群中存储的数据的访问,并在此基础上构建基于角色的访问控制 (RBAC)。通过这种方式,您可以定义用户的权限并限制对敏感端点的访问。
要在您自己的 Qdrant 实例中启用基于 JWT 的身份验证,您需要在配置中指定 api-key
并启用 jwt_rbac
功能
service:
api_key: you_secret_api_key_here
jwt_rbac: true
或者使用环境变量
export QDRANT__SERVICE__API_KEY=your_secret_api_key_here
export QDRANT__SERVICE__JWT_RBAC=true
您在配置中设置的 api_key
将用于编码和解码 JWT,所以——毋庸置疑——请确保其安全。如果您的 api_key
发生变化,所有现有令牌都将失效。
要使用基于 JWT 的身份验证,您需要在请求的 Authorization
头部中提供作为持有者令牌,或者在 Api-Key
头部中提供作为密钥。
Authorization: Bearer <JWT>
// or
Api-Key: <JWT>
from qdrant_client import QdrantClient
qdrant_client = QdrantClient(
"xyz-example.eu-central.aws.cloud.qdrant.io",
api_key="<JWT>",
)
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({
host: "xyz-example.eu-central.aws.cloud.qdrant.io",
apiKey: "<JWT>",
});
use qdrant_client::Qdrant;
let client = Qdrant::from_url("https://xyz-example.eu-central.aws.cloud.qdrant.io:6334")
.api_key("<JWT>")
.build()?;
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());
using Qdrant.Client;
var client = new QdrantClient(
host: "xyz-example.eu-central.aws.cloud.qdrant.io",
https: true,
apiKey: "<JWT>"
);
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,
})
生成 JSON Web Token
由于 JWT 的特性,任何知道 api_key
的人都可以使用现有的库和工具生成令牌,他们无需访问 Qdrant 实例即可生成。
为了方便起见,我们在 Qdrant Web UI 的 🔑 选项卡下添加了一个 JWT 生成工具,如果您使用默认 URL,它将位于 http://localhost:6333/dashboard#/jwt
。
JWT 头部 - Qdrant 使用
HS256
算法来解码令牌。{ "alg": "HS256", "typ": "JWT" }
JWT 载荷 - 您可以在载荷中包含可用参数的任意组合。请继续阅读以了解更多详情。
{ "exp": 1640995200, // Expiration time "value_exists": ..., // Validate this token by looking for a point with a payload value "access": "r", // Define the access level. }
签名令牌 - 为了确认生成的令牌有效,需要使用您在配置中设置的 api_key
进行签名。这意味着,知道 api_key
的人授权新令牌在 Qdrant 实例中使用。Qdrant 可以验证签名,因为它知道 api_key
并可以解码令牌。
令牌生成过程可以在客户端离线完成,无需与 Qdrant 实例进行任何通信。
以下是一些可用于生成 JWT 令牌的库示例
- Python:PyJWT
- JavaScript:jsonwebtoken
- Rust:jsonwebtoken
JWT 配置
这些是可用选项,在 JWT 术语中称为**声明(claims)**。您可以在 JWT 载荷中使用它们来定义其功能。
exp
- 令牌的过期时间。这是一个以秒为单位的 Unix 时间戳。令牌在此时间之后将失效。对该声明的检查包含 30 秒的宽限期,以应对时钟偏差。{ "exp": 1640995200, // Expiration time }
value_exists
- 这是可用于根据集合中存储的数据验证令牌的声明。此声明的结构如下:{ "value_exists": { "collection": "my_validation_collection", "matches": [ { "key": "my_key", "value": "value_that_must_exist" } ], }, }
如果存在此声明,Qdrant 将检查集合中是否存在具有指定键值对的点。如果存在,则令牌有效。
如果您想在不更改
api_key
的情况下撤销令牌,此声明特别有用。考虑您有一个用户集合,并且想要撤销对特定用户的访问权限。{ "value_exists": { "collection": "users", "matches": [ { "key": "user_id", "value": "andrey" }, { "key": "role", "value": "manager" } ], }, }
您可以使用此声明创建一个令牌,当您想撤销访问权限时,可以更改用户的
role
为其他值,然后令牌将失效。access
- 此声明定义了令牌的访问级别。如果存在此声明,Qdrant 将检查令牌是否具有执行操作所需的访问级别。如果此声明**不存在**,则假定为**管理**访问。它可以提供全局访问:
r
表示只读,m
表示管理。例如:{ "access": "r" }
它也可以特定于一个或多个集合。每个集合的
access
级别是r
表示只读,rw
表示读写,如下所示:{ "access": [ { "collection": "my_collection", "access": "rw" } ] }
您还可以通过指定点必须具有的
payload
限制来指定用户可以访问集合的哪个子集。{ "access": [ { "collection": "my_collection", "access": "r", "payload": { "user_id": "user_123456" } } ] }
此
payload
声明将用于隐式过滤集合中的点。它相当于将此过滤器附加到每个请求中:{ "filter": { "must": [{ "key": "user_id", "match": { "value": "user_123456" } }] } }
访问权限表
查看此表,了解基于访问级别的允许或拒绝的操作。
这适用于使用 API 密钥而非令牌。在这种情况下,api_key
对应于**管理**权限,而 read_only_api_key
对应于**只读**权限。
操作 | 管理 | 只读 | 集合读写 | 集合只读 | 带有载荷声明的集合 (r / rw) |
---|---|---|---|---|---|
列出集合 | ✅ | ✅ | 🟡 | 🟡 | 🟡 |
获取集合信息 | ✅ | ✅ | ✅ | ✅ | ❌ |
创建集合 | ✅ | ❌ | ❌ | ❌ | ❌ |
删除集合 | ✅ | ❌ | ❌ | ❌ | ❌ |
更新集合参数 | ✅ | ❌ | ❌ | ❌ | ❌ |
获取集合集群信息 | ✅ | ✅ | ✅ | ✅ | ❌ |
集合是否存在 | ✅ | ✅ | ✅ | ✅ | ✅ |
更新集合集群配置 | ✅ | ❌ | ❌ | ❌ | ❌ |
更新别名 | ✅ | ❌ | ❌ | ❌ | ❌ |
列出集合别名 | ✅ | ✅ | 🟡 | 🟡 | 🟡 |
列出别名 | ✅ | ✅ | 🟡 | 🟡 | 🟡 |
创建分片键 | ✅ | ❌ | ❌ | ❌ | ❌ |
删除分片键 | ✅ | ❌ | ❌ | ❌ | ❌ |
创建载荷索引 | ✅ | ❌ | ✅ | ❌ | ❌ |
删除载荷索引 | ✅ | ❌ | ✅ | ❌ | ❌ |
列出集合快照 | ✅ | ✅ | ✅ | ✅ | ❌ |
创建集合快照 | ✅ | ❌ | ✅ | ❌ | ❌ |
删除集合快照 | ✅ | ❌ | ✅ | ❌ | ❌ |
下载集合快照 | ✅ | ✅ | ✅ | ✅ | ❌ |
上传集合快照 | ✅ | ❌ | ❌ | ❌ | ❌ |
恢复集合快照 | ✅ | ❌ | ❌ | ❌ | ❌ |
列出分片快照 | ✅ | ✅ | ✅ | ✅ | ❌ |
创建分片快照 | ✅ | ❌ | ✅ | ❌ | ❌ |
删除分片快照 | ✅ | ❌ | ✅ | ❌ | ❌ |
下载分片快照 | ✅ | ✅ | ✅ | ✅ | ❌ |
上传分片快照 | ✅ | ❌ | ❌ | ❌ | ❌ |
恢复分片快照 | ✅ | ❌ | ❌ | ❌ | ❌ |
列出完整快照 | ✅ | ✅ | ❌ | ❌ | ❌ |
创建完整快照 | ✅ | ❌ | ❌ | ❌ | ❌ |
删除完整快照 | ✅ | ❌ | ❌ | ❌ | ❌ |
下载完整快照 | ✅ | ✅ | ❌ | ❌ | ❌ |
获取集群信息 | ✅ | ✅ | ❌ | ❌ | ❌ |
恢复 Raft 状态 | ✅ | ❌ | ❌ | ❌ | ❌ |
删除对等节点 | ✅ | ❌ | ❌ | ❌ | ❌ |
获取点 | ✅ | ✅ | ✅ | ✅ | ❌ |
获取点 | ✅ | ✅ | ✅ | ✅ | ❌ |
插入或更新点 | ✅ | ❌ | ✅ | ❌ | ❌ |
批量更新点 | ✅ | ❌ | ✅ | ❌ | ❌ |
删除点 | ✅ | ❌ | ✅ | ❌ | ❌ / 🟡 |
更新向量 | ✅ | ❌ | ✅ | ❌ | ❌ |
删除向量 | ✅ | ❌ | ✅ | ❌ | ❌ / 🟡 |
设置载荷 | ✅ | ❌ | ✅ | ❌ | ❌ |
覆盖载荷 | ✅ | ❌ | ✅ | ❌ | ❌ |
删除载荷 | ✅ | ❌ | ✅ | ❌ | ❌ |
清空载荷 | ✅ | ❌ | ✅ | ❌ | ❌ |
滚动点 | ✅ | ✅ | ✅ | ✅ | 🟡 |
查询点 | ✅ | ✅ | ✅ | ✅ | 🟡 |
搜索点 | ✅ | ✅ | ✅ | ✅ | 🟡 |
搜索分组 | ✅ | ✅ | ✅ | ✅ | 🟡 |
推荐点 | ✅ | ✅ | ✅ | ✅ | ❌ |
推荐分组 | ✅ | ✅ | ✅ | ✅ | ❌ |
发现点 | ✅ | ✅ | ✅ | ✅ | ❌ |
统计点数 | ✅ | ✅ | ✅ | ✅ | 🟡 |
版本 | ✅ | ✅ | ✅ | ✅ | ✅ |
readyz, healthz, livez | ✅ | ✅ | ✅ | ✅ | ✅ |
遥测 | ✅ | ✅ | ❌ | ❌ | ❌ |
指标 | ✅ | ✅ | ❌ | ❌ | ❌ |
更新锁 | ✅ | ❌ | ❌ | ❌ | ❌ |
获取锁 | ✅ | ✅ | ❌ | ❌ | ❌ |
TLS
从 v1.2.0 版本开始可用
您可以在 Qdrant 实例上启用 TLS 来保护加密连接。
首先确保您拥有 TLS 证书和私钥,通常是 .pem
格式。在您的本地机器上,您可以使用 mkcert 生成自签名证书。
要启用 TLS,请在 Qdrant 配置中设置以下属性并指定正确的路径,然后重启:
service:
# Enable HTTPS for the REST and gRPC API
enable_tls: true
# TLS configuration.
# Required if either service.enable_tls or cluster.p2p.enable_tls is true.
tls:
# Server certificate chain file
cert: ./tls/cert.pem
# Server private key file
key: ./tls/key.pem
在集群模式下运行时,可以通过以下方式启用内部通信的 TLS:
cluster:
# Configuration of the inter-cluster communication
p2p:
# Use TLS for communication between peers
enable_tls: true
启用 TLS 后,您必须开始使用 HTTPS 连接。例如:
curl -X GET https://localhost:6333
from qdrant_client import QdrantClient
client = QdrantClient(
url="https://localhost:6333",
)
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ url: "https://localhost", port: 6333 });
use qdrant_client::Qdrant;
let client = Qdrant::from_url("http://localhost:6334").build()?;
证书轮换已启用,默认刷新时间为一小时。Qdrant 运行期间,每小时重新加载证书文件。这样,外部更新的证书更改将被及时获取。您可以通过修改 tls.cert_ttl
设置来调整刷新时间。即使您不打算更新证书,也可以保持此设置开启。目前这仅支持 REST API。
(可选)您可以在服务器上针对本地证书颁发机构启用客户端证书验证。设置以下属性并重启:
service:
# Check user HTTPS client certificate against CA file specified in tls config
verify_https_client_certificate: false
# TLS configuration.
# Required if either service.enable_tls or cluster.p2p.enable_tls is true.
tls:
# Certificate authority certificate file.
# This certificate will be used to validate the certificates
# presented by other nodes during inter-cluster communication.
#
# If verify_https_client_certificate is true, it will verify
# HTTPS client certificate
#
# Required if cluster.p2p.enable_tls is true.
ca_cert: ./tls/cacert.pem
加固
我们建议减少授予 Qdrant 容器的权限数量,以降低被利用的风险。以下是减少 Qdrant 容器权限的一些方法:
以非 root 用户身份运行 Qdrant。这有助于减轻未来容器逃逸漏洞的风险。Qdrant 不需要 root 用户的任何特权。
- 您可以使用镜像
qdrant/qdrant:<version>-unprivileged
而不是默认的 Qdrant 镜像。 - 运行
docker run
时,您可以使用标志--user=1000:2000
。 - 使用 Docker Compose 时,您可以设置
user: 1000
。 - 在 Kubernetes 中运行时,您可以设置
runAsUser: 1000
(我们的 Helm chart 默认执行此操作)。
- 您可以使用镜像
使用只读根文件系统运行 Qdrant。这有助于减轻需要修改系统文件的漏洞风险,Qdrant 不需要此权限。只要容器使用挂载卷进行存储(默认为
/qdrant/storage
和/qdrant/snapshots
),Qdrant 就可以继续运行,同时阻止向这些卷外部写入数据。- 运行
docker run
时,您可以使用标志--read-only
。 - 使用 Docker Compose 时,您可以设置
read_only: true
。 - 在 Kubernetes 中运行时,您可以设置
readOnlyRootFilesystem: true
(我们的 Helm chart 默认执行此操作)。
- 运行
阻止 Qdrant 的外部网络访问。这有助于减轻服务器端请求伪造攻击(SSRF)的风险,例如通过快照恢复 API。单节点 Qdrant 集群不需要任何出站网络访问。多节点 Qdrant 集群只需要能够通过 TCP 端口 6333、6334 和 6335 连接到其他 Qdrant 节点。
- 您可以使用
docker network create --internal <name>
创建网络,并在运行docker run --network <name>
时使用该网络。 - 使用 Docker Compose 时,您可以创建内部网络。
- 使用 Kubernetes 时,您可以创建 NetworkPolicy。请注意,多节点 Qdrant 集群还需要在 Kubernetes 中访问集群 DNS。
- 您可以使用
还有其他减少权限的技术,例如根据您的部署方法删除Linux capabilities,但上面提到的方法是最重要的。