安全
Qdrant 支持多种安全功能,旨在帮助您保护其实例。为了使您的实例达到生产就绪状态,其中大多数功能需要进行明确配置。请仔细阅读以下部分。
保障实例安全
默认情况下,所有自托管的 Qdrant 实例都是不安全的。它们向所有网络接口开放,且未配置任何身份验证。它们可能会在没有任何限制的情况下向互联网上的任何人开放。因此,您必须采取安全措施,使您的实例达到生产就绪状态。请仔细阅读本节,了解如何保护您的实例。
通过 Qdrant Cloud 部署的实例默认始终是安全的。请参阅身份验证和客户端 IP 限制。
为了妥善保护您自己的实例,我们强烈建议采取以下步骤
- 身份验证:设置 API 密钥以防止未经授权的访问。
这是防止未经身份验证的参与者访问您数据的最重要步骤。 - 网络绑定:绑定到特定的网络接口或 IP 地址。
本地开发时,请绑定到127.0.0.1以防止所有外部访问。部署到生产环境时,请绑定到私有网络接口或 IP。 - TLS:使用 TLS 在所有地方启用加密流量。
认证
自 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 请求中。所有官方的 Python、Go、Rust、.NET 和 Java 的 Qdrant 客户端均支持 API 密钥参数。
curl \
-X GET https://:6333 \
--header 'api-key: your_secret_api_key_here'
from qdrant_client import QdrantClient
client = QdrantClient(
url="https://:6333",
api_key="your_secret_api_key_here",
)
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({
url: "https://",
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 密钥可以同时使用。
轮换 API 密钥
从 v1.17.0 开始提供
在分布式部署中,您可以轮换 API 密钥而无需停机。使用 alt_api_key 设置临时配置第二个 API 密钥,其作用与主 api_key 相同,从而允许旧密钥和新密钥同时处于活动状态。
service:
api_key: your_current_api_key_here
alt_api_key: your_new_api_key_here
要在不中断服务的情况下轮换 API 密钥
- 为每个对等节点配置新密钥作为
alt_api_key。每次仅重启一个节点以避免停机(滚动重启)。在轮换窗口期间,接受使用任一密钥进行身份验证的请求。 - 将客户端切换到新密钥。
- 对对等节点执行另一次滚动重启,将新密钥提升为
api_key并移除alt_api_key。
基于 JWT 的细粒度访问控制
v1.9.0 版本起提供
对于更复杂的情况,Qdrant 支持使用 JSON Web Tokens (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 标头中以 Bearer Token 的形式提供它,或者在 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 Tokens
由于 JWT 的特性,任何知道 api_key 的人都可以使用现有的库和工具生成令牌,他们无需访问 Qdrant 实例即可生成。
为方便起见,我们在 Qdrant Web UI 的 🔑 选项卡下添加了一个 JWT 生成工具。如果您使用默认 URL,它位于 https://: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
- CLI: jwt-cli
以下是使用 jwt-cli 的示例
jwt encode --payload '{
"access": "r",
"exp": 1766055305
}' --secret 'your-api-key'
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 将检查集合中是否存在具有指定键值的点 (point)。如果存在,则令牌有效。
如果您希望能够在不更改
api_key的情况下撤销令牌,此声明特别有用。考虑这样一种情况:您拥有一个用户集合,并且想要撤销对特定用户的访问权限。{ "value_exists": { "collection": "users", "matches": [ { "key": "user_id", "value": "andrey" }, { "key": "role", "value": "manager" } ], }, }您可以创建一个带有此声明的令牌,当您想要撤销访问权限时,可以将该用户的
role更改为其他内容,令牌将失效。access- 此声明定义了令牌的访问级别。如果存在此声明,Qdrant 将检查令牌是否具有执行该操作所需的访问级别。如果不存在此声明,则假定为 manage(管理)访问权限。它可以提供全局访问权限,使用
r表示只读,或m表示管理。例如{ "access": "r" }它也可以针对一个或多个集合。每个集合的
access级别为r(只读)或rw(读写),如下所示{ "access": [ { "collection": "my_collection", "access": "rw" } ] }
访问权限表
查看此表以了解根据访问级别允许或拒绝哪些操作。
这也适用于使用 API 密钥而非令牌的情况。在这种情况下,api_key 映射到 manage(管理),而 read_only_api_key 映射到 read-only(只读)。
| 操作 | 管理 (manage) | 只读 (read-only) | 集合读写 (collection read-write) | 集合只读 (collection read-only) |
|---|---|---|---|---|
| 列出集合 | ✅ | ✅ | 🟡 | 🟡 |
| 获取集合信息 | ✅ | ✅ | ✅ | ✅ |
| 创建集合 | ✅ | ❌ | ❌ | ❌ |
| 删除集合 | ✅ | ❌ | ❌ | ❌ |
| 更新集合参数 | ✅ | ❌ | ❌ | ❌ |
| 获取集合集群信息 | ✅ | ✅ | ✅ | ✅ |
| 检查集合是否存在 | ✅ | ✅ | ✅ | ✅ |
| 更新集合集群设置 | ✅ | ❌ | ❌ | ❌ |
| 更新别名 | ✅ | ❌ | ❌ | ❌ |
| 列出集合别名 | ✅ | ✅ | 🟡 | 🟡 |
| 列出所有别名 | ✅ | ✅ | 🟡 | 🟡 |
| 创建分片键 | ✅ | ❌ | ❌ | ❌ |
| 删除分片键 | ✅ | ❌ | ❌ | ❌ |
| 创建负载索引 | ✅ | ❌ | ✅ | ❌ |
| 删除负载索引 | ✅ | ❌ | ✅ | ❌ |
| 列出集合快照 | ✅ | ✅ | ✅ | ✅ |
| 创建集合快照 | ✅ | ❌ | ✅ | ❌ |
| 删除集合快照 | ✅ | ❌ | ✅ | ❌ |
| 下载集合快照 | ✅ | ✅ | ✅ | ✅ |
| 上传集合快照 | ✅ | ❌ | ❌ | ❌ |
| 恢复集合快照 | ✅ | ❌ | ❌ | ❌ |
| 列出分片快照 | ✅ | ✅ | ✅ | ✅ |
| 创建分片快照 | ✅ | ❌ | ✅ | ❌ |
| 删除分片快照 | ✅ | ❌ | ✅ | ❌ |
| 下载分片快照 | ✅ | ✅ | ✅ | ✅ |
| 上传分片快照 | ✅ | ❌ | ❌ | ❌ |
| 恢复分片快照 | ✅ | ❌ | ❌ | ❌ |
| 列出完整快照 | ✅ | ✅ | ❌ | ❌ |
| 创建完整快照 | ✅ | ❌ | ❌ | ❌ |
| 删除完整快照 | ✅ | ❌ | ❌ | ❌ |
| 下载完整快照 | ✅ | ✅ | ❌ | ❌ |
| 获取集群信息 | ✅ | ✅ | ❌ | ❌ |
| 恢复 Raft 状态 | ✅ | ❌ | ❌ | ❌ |
| 删除节点 | ✅ | ❌ | ❌ | ❌ |
| 获取点 (Get point) | ✅ | ✅ | ✅ | ✅ |
| 获取多个点 (Get points) | ✅ | ✅ | ✅ | ✅ |
| 更新点 (Upsert points) | ✅ | ❌ | ✅ | ❌ |
| 批量更新点 | ✅ | ❌ | ✅ | ❌ |
| 删除点 | ✅ | ❌ | ✅ | ❌ |
| 更新向量 | ✅ | ❌ | ✅ | ❌ |
| 删除向量 | ✅ | ❌ | ✅ | ❌ |
| 设置负载 (Set payload) | ✅ | ❌ | ✅ | ❌ |
| 覆盖负载 (Overwrite payload) | ✅ | ❌ | ✅ | ❌ |
| 删除负载 | ✅ | ❌ | ✅ | ❌ |
| 清除负载 | ✅ | ❌ | ✅ | ❌ |
| 滚动扫描点 (Scroll points) | ✅ | ✅ | ✅ | ✅ |
| 查询点 (Query points) | ✅ | ✅ | ✅ | ✅ |
| 搜索点 (Search points) | ✅ | ✅ | ✅ | ✅ |
| 搜索分组 (Search groups) | ✅ | ✅ | ✅ | ✅ |
| 推荐点 (Recommend points) | ✅ | ✅ | ✅ | ✅ |
| 推荐分组 (Recommend groups) | ✅ | ✅ | ✅ | ✅ |
| 发现点 (Discover points) | ✅ | ✅ | ✅ | ✅ |
| 计数点 (Count points) | ✅ | ✅ | ✅ | ✅ |
| 版本 | ✅ | ✅ | ✅ | ✅ |
| readyz, healthz, livez | ✅ | ✅ | ✅ | ✅ |
| 遥测 | ✅ | ✅ | ❌ | ❌ |
| 指标 | ✅ | ✅ | ❌ | ❌ |
审计日志
从 v1.17.0 开始提供
审计日志记录所有需要身份验证或授权的 API 操作,并将它们以 JSON 格式写入日志文件。
默认情况下不启用审计日志。要启用它,请使用以下配置选项
audit:
enabled: false
dir: ./storage/audit
rotation: daily
max_log_files: 7
默认情况下,审计日志每日轮换,并保留最近七个日志文件。要配置按小时轮换,请将 rotation 设置为 hourly。当日志文件数量超过 max_log_files 时,最旧的日志文件将被删除。
网络绑定
默认情况下,自定义的 Qdrant 部署会绑定到所有网络接口。您的实例可能会向互联网上的任何人开放。在本地开发机器上,您可能有防火墙来防止公共访问,但在公共 VPS 或专用服务器上情况可能并非如此。
强烈建议绑定到特定的接口或 IP 地址以防止未经授权的访问
- 本地开发时,绑定到
127.0.0.1,这样无法进行外部访问 - 或者,部署到生产环境时,绑定到私有网络接口或 IP
使用 Docker 时,您可以使用发布标志 (publish flag) 绑定到特定接口。例如
docker run -p 127.0.0.1:6333:6333 qdrant/qdrant
如果使用其他类型的部署,您可以在 Qdrant 本身中配置绑定地址。要么在配置中设置 service.host: 127.0.0.1,要么使用环境变量,如下所示
QDRANT__SERVICE__HOST=127.0.0.1 ./qdrant
托管的 Qdrant Cloud 部署默认始终是安全的。它们是公开可访问的,并绑定到分配给集群的端点。您可以使用API 密钥配置身份验证,并通过客户端 IP 限制将访问权限限制为特定 IP 地址。混合云 (Hybrid Cloud) 和私有云 (Private Cloud) 部署有其各自的配置方式。
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://:6333
from qdrant_client import QdrantClient
client = QdrantClient(
url="https://:6333",
)
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ url: "https://", port: 6333 });
use qdrant_client::Qdrant;
let client = Qdrant::from_url("https://: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
加固 (Hardening)
我们建议减少授予 Qdrant 容器的权限量,以降低利用风险。以下是减少 Qdrant 容器权限的一些方法
以非 root 用户身份运行 Qdrant。这有助于降低未来容器逃逸漏洞的风险。Qdrant 无需 root 用户权限即可运行。
- 您可以使用镜像
qdrant/qdrant:<version>-unprivileged而不是默认的 Qdrant 镜像。 - 运行
docker run时,可以使用--user=1000:2000标志。 - 使用 Docker Compose 时,可以设置
user: 1000。 - 在 Kubernetes 中运行时,可以设置
runAsUser: 1000(我们的 Helm 图表默认执行此操作)。
- 您可以使用镜像
使用只读根文件系统运行 Qdrant。这有助于缓解需要修改系统文件才能利用的漏洞,而 Qdrant 不需要此权限。只要容器使用挂载卷进行存储(默认情况下为
/qdrant/storage和/qdrant/snapshots),Qdrant 就可以继续运行,同时被阻止写入这些卷之外的数据。- 运行
docker run时,可以使用--read-only标志。 - 使用 Docker Compose 时,可以设置
read_only: true。 - 在 Kubernetes 中运行时,可以设置
readOnlyRootFilesystem: true(我们的 Helm 图表默认执行此操作)。
- 运行
阻止 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),但上述方法是最重要的。