安全
请仔细阅读本页。尽管有多种方法可以保护您的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 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头中,或者作为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令牌
由于JWT的性质,任何知道api_key的人都可以使用现有的库和工具生成令牌,他们不需要访问Qdrant实例来生成这些令牌。
为了方便起见,我们在Qdrant Web UI的🔑标签下添加了一个JWT生成工具,如果您使用的是默认URL,它将在http://localhost:6333/dashboard#/jwt。
JWT 头部 - Qdrant 使用
HS256算法来解码令牌。{ "alg": "HS256", "typ": "JWT" }JWT Payload - 你可以在payload中包含任何可用参数的组合。继续阅读以获取每个参数的更多信息。
{ "exp": 1640995200, // 过期时间 "value_exists": ..., // 通过查找具有payload值的点来验证此令牌 "access": "r", // 定义访问级别 }
签署令牌 - 为了确认生成的令牌是有效的,需要使用您在配置中设置的api_key对其进行签名。
这意味着,知道api_key的人授权新令牌在Qdrant实例中使用。
Qdrant可以验证签名,因为它知道api_key并且可以解码令牌。
令牌生成的过程可以在客户端离线完成,不需要与Qdrant实例进行任何通信。
以下是可用于生成JWT令牌的库的示例:
- Python: PyJWT
- JavaScript: jsonwebtoken
- Rust: jsonwebtoken
JWT 配置
这些是可用的选项,或者在JWT术语中称为声明。您可以在JWT有效负载中使用它们来定义其功能。
exp- 令牌的过期时间。这是一个以秒为单位的Unix时间戳。令牌在此时间后将无效。对此声明的检查包括30秒的余地,以考虑时钟偏差。{ "exp": 1640995200, // 过期时间 }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 映射到 只读。
| 操作 | 管理 | 只读 | 集合读写 | 集合只读 | 带有有效载荷声明的集合(只读/读写) |
|---|---|---|---|---|---|
| 列出集合 | ✅ | ✅ | 🟡 | 🟡 | 🟡 |
| 获取集合信息 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 创建集合 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 删除集合 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 更新集合参数 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 获取集合集群信息 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 集合存在 | ✅ | ✅ | ✅ | ✅ | ✅ |
| 更新集合集群设置 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 更新别名 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 列出集合别名 | ✅ | ✅ | 🟡 | 🟡 | 🟡 |
| 列出别名 | ✅ | ✅ | 🟡 | 🟡 | 🟡 |
| 创建分片键 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 删除分片键 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 创建有效载荷索引 | ✅ | ❌ | ✅ | ❌ | ❌ |
| 删除有效载荷索引 | ✅ | ❌ | ✅ | ❌ | ❌ |
| 列出集合快照 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 创建集合快照 | ✅ | ❌ | ✅ | ❌ | ❌ |
| 删除集合快照 | ✅ | ❌ | ✅ | ❌ | ❌ |
| 下载集合快照 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 上传集合快照 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 恢复集合快照 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 列出分片快照 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 创建分片快照 | ✅ | ❌ | ✅ | ❌ | ❌ |
| 删除分片快照 | ✅ | ❌ | ✅ | ❌ | ❌ |
| 下载分片快照 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 上传分片快照 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 恢复分片快照 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 列出完整快照 | ✅ | ✅ | ❌ | ❌ | ❌ |
| 创建完整快照 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 删除完整快照 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 下载完整快照 | ✅ | ✅ | ❌ | ❌ | ❌ |
| 获取集群信息 | ✅ | ✅ | ❌ | ❌ | ❌ |
| 恢复raft状态 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 删除对等节点 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 获取点 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 获取积分 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 更新插入点 | ✅ | ❌ | ✅ | ❌ | ❌ |
| 批量更新积分 | ✅ | ❌ | ✅ | ❌ | ❌ |
| 删除点 | ✅ | ❌ | ✅ | ❌ | ❌ / 🟡 |
| 更新向量 | ✅ | ❌ | ✅ | ❌ | ❌ |
| 删除向量 | ✅ | ❌ | ✅ | ❌ | ❌ / 🟡 |
| 设置有效载荷 | ✅ | ❌ | ✅ | ❌ | ❌ |
| 覆盖有效载荷 | ✅ | ❌ | ✅ | ❌ | ❌ |
| 删除有效载荷 | ✅ | ❌ | ✅ | ❌ | ❌ |
| 清除有效载荷 | ✅ | ❌ | ✅ | ❌ | ❌ |
| 滚动点 | ✅ | ✅ | ✅ | ✅ | 🟡 |
| 查询点 | ✅ | ✅ | ✅ | ✅ | 🟡 |
| 搜索点 | ✅ | ✅ | ✅ | ✅ | 🟡 |
| 搜索群组 | ✅ | ✅ | ✅ | ✅ | 🟡 |
| 推荐点 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 推荐群组 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 发现点 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 计数点 | ✅ | ✅ | ✅ | ✅ | 🟡 |
| 版本 | ✅ | ✅ | ✅ | ✅ | ✅ |
| readyz, healthz, livez | ✅ | ✅ | ✅ | ✅ | ✅ |
| 遥测 | ✅ | ✅ | ❌ | ❌ | ❌ |
| 指标 | ✅ | ✅ | ❌ | ❌ | ❌ |
| 更新锁 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 获取锁 | ✅ | ✅ | ❌ | ❌ | ❌ |
传输层安全性
自 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:代替默认的Qdrant镜像。-unprivileged - 您可以在运行
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的外部网络访问。这有助于减轻服务器端请求伪造攻击,例如通过快照恢复API。单节点Qdrant集群不需要任何出站网络访问。多节点Qdrant集群只需要通过TCP端口6333、6334和6335连接到其他Qdrant节点的能力。
- 您可以使用
docker network create --internal并在运行docker run --network时使用该网络。 - 在使用Docker Compose时,您可以创建一个内部网络。
- 在使用Kubernetes时,您可以创建一个网络策略。请注意,多节点Qdrant集群在Kubernetes中还需要访问集群DNS。
- 您可以使用
还有其他技术可以减少权限,例如根据您的部署方法删除Linux 能力,但上述方法是最重要的。
