#dns-server #dns-records #dns #redis #record #http-api #api-server

app constellation-server

可插拔的权威DNS服务器。可以通过HTTP REST API添加和删除条目。

39个稳定版本

1.15.0 2024年7月22日
1.14.3 2023年9月1日
1.14.1 2022年7月7日
1.14.0 2021年11月2日
0.0.1 2018年4月10日

#85 in Web编程

Download history 91/week @ 2024-07-18 102/week @ 2024-07-25 1/week @ 2024-08-01

每月下载 194次

MPL-2.0 许可证

255KB
5.5K SLoC

Constellation

Test and Build Build and Release dependency status Buy Me A Coffee

可插拔的权威DNS服务器。可以通过HTTP REST API添加和删除条目。

Constellation是一个小巧的权威服务器,允许您以通用的方式通过HTTP REST API管理DNS条目。它可以插入到现有的基础设施中,以管理您服务的用户DNS记录,例如配置无法在传统DNS服务器中轻松通配符的出站邮件记录(DKIM、DMARC、SPF记录)。

DNS条目存储在Redis中。因此,DNS数据库可以轻松修改和转储以备备份。

在Rust版本1.79.0 (129f3b996 2024-06-10)上测试过。

🇫🇷 在法国安格斯制作。

Constellation

谁在使用它?

Crisp

👋 您使用Constellation并希望在列表中列出? 联系我.

功能

  • 可插拔的权威DNS服务器,如果您需要为用户生成例如电子邮件子域(带有DKIM、DMARC和SPF记录),它将非常方便。
  • HTTP REST API 以在线检查、读取、插入、修改和删除DNS记录。
  • 持久层 在Redis中。这意味着您可以在网络上运行多个Constellation,对同一数据库进行操作。您甚至可以分片Redis,如果需要DNS数据存储的容错能力。
  • Geo-DNS 基于地理位置提供记录。例如,为所有北美用户提供美国服务器的IP地址,其余的回退到欧洲。基于MaxMind GeoLite2免费数据库,必要时自动更新。
  • CNAME扁平化 以节省为用户提供解析往返。这可以按记录设置启用。

支持的记录类型

Constellation支持所有最广泛使用的DNS记录类型

类型 支持? CNAME扁平化?
A
AAAA
CNAME
MX
TXT
CAA
PTR

👉 如果您希望我添加此处未列出的记录类型支持,请提交一个issue

如何使用它?

安装

Constellation是用Rust构建的。要安装它,可以下载Constellation发行版页面上的版本,使用cargo install,或者从master拉取源代码。

👉 每个发布二进制文件都附带一个.asc签名文件,可以使用@valeriansaliou GPG公钥进行验证:🔑valeriansaliou.gpg.pub.asc

从软件包安装

Constellation为基于Debian的系统(Debian、Ubuntu等)提供了预构建的软件包

重要:目前Constellation仅提供针对Debian 10、11 & 12的64位软件包(代号:busterbullseye & bookworm)。您仍然可以在其他Debian版本以及Ubuntu上使用它们。

首先,添加Constellation APT仓库(例如,对于Debian bookworm

echo "deb [signed-by=/usr/share/keyrings/valeriansaliou_constellation.gpg] https://packagecloud.io/valeriansaliou/constellation/debian/ bookworm main" > /etc/apt/sources.list.d/valeriansaliou_constellation.list
curl -fsSL https://packagecloud.io/valeriansaliou/constellation/gpgkey | gpg --dearmor -o /usr/share/keyrings/valeriansaliou_constellation.gpg
apt-get update

然后,安装Constellation软件包

apt-get install constellation

然后,编辑预填充的Constellation配置文件

nano /etc/constellation.cfg

最后,重新启动Constellation

service constellation restart

从源代码安装

如果您从Git拉取了源代码,可以使用cargo构建它

cargo build --release

您可以在./target/release目录中找到构建的二进制文件。

在编译Constellation之前,安装libssl-dev(即OpenSSL头文件)。SSL依赖对于Geo-DNS数据库更新器和DNS健康检查系统(HTTPS探测器)是必需的。

从Cargo安装

您可以使用cargo install直接安装Constellation

cargo install constellation-server

确保您的$PATH已正确配置以源Crate二进制文件,然后使用constellation命令运行Constellation。

从Docker Hub安装

您可能希望通过Docker运行Constellation。您可以在Docker Hub上找到预构建的Constellation镜像,地址为valeriansaliou/constellation

预构建的Docker版本可能不是可用的Constellation最新版本。

首先,拉取valeriansaliou/constellation镜像

docker pull valeriansaliou/constellation:v1.15.0

然后,创建一个配置文件并运行它(将/path/to/your/constellation/config.cfg替换为您配置文件的路径)

docker run -p 53:53/udp -p 8080:8080 -v /path/to/your/constellation/config.cfg:/etc/constellation.cfg -v /path/to/your/constellation/res/:/var/lib/constellation/ valeriansaliou/constellation:v1.15.0

在配置文件中,请确保

  • dns.inets设置为[0.0.0.0:53](这允许Constellation DNS从容器外部访问)
  • http.inet设置为0.0.0.0:8080(这允许Constellation REST API从容器外部访问)
  • geo.database_path设置为/var/lib/constellation/geo/(GeoIP数据库将存储在此处)

星座服务将通过DNS解析器从udp://localhost:53可访问;其内部REST API可通过https://127.0.0.1:8080访问。

此外,不要忘记在./res/geo/文件夹中初始化GeoIP数据库(参考下文的如何初始化GeoIP)。

配置

使用示例配置文件config.cfg,并根据您的环境进行调整。

以下列出了可用的配置选项及其允许值

[server]

  • log_level(类型:字符串,允许值:debuginfowarnerror,默认:error)—— 日志记录的详细程度,在生产环境中设置为error
  • identifier(类型:字符串,允许值:文本值,默认:constellation/0)—— 此星座服务器在副本池中的标识符(用于标识和通知目的)。

[dns]

  • inets(类型:字符串数组,允许值:IPs + 端口,默认:[0.0.0.0:53, [::]:53])—— DNS服务器应监听的宿主机和UDP/TCP端口。
  • tcp_timeout(类型:整数,允许值:秒,默认:2)—— DNS通过TCP连接的超时时间。
  • nameservers(类型:字符串数组,允许值:域名,默认:无默认值)—— 所服务域名的域名服务器。
  • soa_master(类型:字符串,允许值:域名,默认:无默认值)—— 所服务区域的SOA主域名(主NS服务器的名称)。
  • soa_responsible(类型:字符串,允许值:作为域名使用的电子邮件地址,默认:无默认值)—— 所服务区域的SOA负责人的电子邮件。
  • soa_refresh(类型:整数,允许值:秒,默认:10000)—— SOA记录刷新值。
  • soa_retry(类型:整数,允许值:秒,默认:2400)—— SOA记录重试值。
  • soa_expire(类型:整数,允许值:秒,默认:604800)—— SOA记录过期值。
  • soa_ttl(类型:整数,允许值:秒,默认:3600)—— SOA记录TTL值。
  • record_ttl(类型:整数,允许值:秒,默认:3600)—— DNS记录TTL值。

[[dns.zone.'{name}']]

指定您的区域名称,例如:[[dns.zone.'relay.crisp.chat']]对于区域基础:relay.crisp.chat

dns.flatten

  • resolvers(类型:字符串数组,允许:主机名,IPv4,IPv6,默认:无默认值)— 在扁平化CNAME记录时应使用的DNS解析器

dns.health

  • check_enable(类型:布尔值,允许:truefalse,默认:false)— 是否执行周期性健康检查
  • check_interval(类型:整数,允许:秒,默认:60)— 健康检查的间隔时间(建议从1分钟到5分钟)

dns.health.notify

  • slack_hook_url(类型:字符串,允许:URL,默认:无默认值)— 通知的Slack钩子URL(例如:https://hooks.slack.com/[..]

[dns.health.http]

  • zone(类型:字符串,允许:任何区域根域名,默认:无默认值)— 要检查的区域根域名(例如:relay.crisp.chat
  • name(类型:字符串,允许:区域上的任何子域名,默认:无默认值)— 要检查的区域的子域名(例如:client.@,对于扩展域名 client.relay.crisp.chat
  • method(类型:字符串,允许:HEADGET,默认:GET)— HTTP健康探测请求所使用的HTTP方法
  • host(类型:字符串,允许:HTTP虚拟主机,默认:空)— 检查时请求的HTTP虚拟主机(如果没有设置,则从zonename生成)
  • path(类型:字符串,允许:HTTP路径,默认:/)— 检查时请求的HTTP路径
  • port(类型:整数,允许:TCP端口,默认:443)— 用于HTTP检查的TCP端口(如果使用HTTP,端口号可能为80
  • secure(类型:布尔值,允许:truefalse,默认:true)— 是否通过安全的HTTPS或纯HTTP执行健康检查
  • timeout(类型:整数,允许:秒,默认:10)— 单个HTTP检查尝试的超时时间(秒)
  • max_attempts(类型:整数,允许:数字,默认:3)— 在健康检查失败的情况下,尝试给定健康检查的最大连续次数(即,既不匹配预期状态也不匹配预期主体的健康检查)
  • expected_status(类型:整数数组,允许:HTTP状态码,默认:200)— 预期状态码列表
  • expected_body(类型:字符串数组,允许:文本值,默认:空)— 预期正文内容列表(子字符串可以包含在响应体中;仅当method设置为GET时适用)

[geo]

  • database_path (类型:字符串,允许:文件夹路径,默认:./res/geo/) — 存放 GeoIP 数据库的文件夹路径
  • database_file (类型:字符串,允许:文件名,默认:GeoLite2-Country.mmdb) — 数据库文件夹中 GeoIP2 MMDB 数据库的文件名(可以是免费的 GeoLite2 或付费的 GeoIP2;如果想要从远程下载服务器自动更新此文件,请启用 geo.update_enable
  • update_enable (类型:布尔值,允许:truefalse,默认:false) — 是否启用 GeoIP 数据库更新器
  • update_interval (类型:整数,允许:秒数,默认:864000) — GeoIP 数据库刷新的间隔时间(推荐一周或更长时间)
  • update_url (类型:字符串,允许:HTTP URL,默认:为空) — 压缩的 GeoIP MMDB 文件的 URL(支持:tar.gz),在刷新时下载(如果启用了 geo.update_enable,则需要此值)

[http]

  • inet (类型:字符串,允许:IPv4 / IPv6 + 端口,默认:[::1]:8080) — HTTP API 服务器应监听的宿主机和 TCP 端口
  • workers (类型:整数,允许:任何数字,默认:2) — HTTP API 服务器运行的工人数
  • record_token (类型:字符串,允许:秘密令牌,默认:无默认值) — 管理API访问的记录秘密令牌(即秘密密码)

[redis]

  • database (类型:整数,允许:0255,默认:0) — 目标 Redis 数据库
  • pool_size (类型:整数,允许:0(2^32)-1,默认:8) — Redis 连接池大小
  • max_lifetime_seconds (类型:整数,允许:秒数,默认:20) — Redis 连接的最大生命周期(您希望它低于 5 分钟,因为这将影响断开连接时重新连接 Redis 的延迟)
  • idle_timeout_seconds (类型:整数,允许:秒数,默认:600) — Redis 空闲/死连接的超时时间
  • connection_timeout_seconds (类型:整数,允许:秒数,默认:2) — Redis 死连接的秒数超时,拒绝 DNS 和 HTTP API 查询
  • delinquency_seconds (类型:整数,允许:秒数,默认:10) — 标记失败的 Redis 连接为欠款的秒数,这意味着在故障期间不会反复尝试。它应该是连接超时的一个因子,建议的因子是 3。
  • cache_refresh_seconds (类型:整数,允许:秒,默认:60)— 在本地缓存的记录从Redis刷新后的秒数(应保持较低值)
  • cache_expire_seconds (类型:整数,允许:秒,默认:600)— 本地缓存的记录在Redis中过期后应刷新的秒数(应保持较低值)

redis.master

  • host (类型:字符串,允许:主机名,IPv4,IPv6,默认:localhost)— 目标主Redis主机
  • port (类型:整数,允许:TCP端口,默认:6379)— 目标主Redis TCP端口
  • password (类型:字符串,允许:密码值,默认:无) — 主Redis密码(如果没有密码,不要设置此键)

[redis.rescue]

  • host (类型:字符串,允许:主机名,IPv4,IPv6,默认:localhost)— 只读救援Redis主机
  • port (类型:整数,允许:TCP端口,默认:6379)— 只读救援Redis TCP端口
  • password (类型:字符串,允许:密码值,默认:无) — 只读救援Redis密码(如果没有密码,不要设置此键)

初始化GeoIP

由于Constellation在其仓库中没有分发GeoIP数据库,您在第一次运行Constellation之前需要从MaxMind获取它(否则Constellation将拒绝启动)。

执行提供的脚本

./scripts/init_geoip.sh --许可证密钥=YOUR_GEOLITE2_LICENSE_KEY

YOUR_GEOLITE2_LICENSE_KEY应替换为有效的GeoLite2许可证密钥。请遵循MaxMind提供的说明以获取许可证密钥。

请注意,一旦Constellation从您手动初始化的GeoIP数据库启动,它将通过在后台自动检查和应用更新来保持数据库的最新状态。数据库初始化是一次性操作。请确保您的许可证密钥也设置在配置中的GeoIP更新URL。

运行Constellation

Constellation可以这样运行

./constellation -c/path/to/config.cfg

测试Constellation

运行后,可以在本地网络中对Constellation进行DNS查询(使用默认配置)

dig subdomain.中继.crisp.聊天@::1

请注意,可以使用dig实用工具通过@修饰符指向特定服务器,这里使用IPv6 localhost: ::1

🛰 HTTP REST API

Constellation HTTP REST API监听配置文件config.cfg中的http.inet接口。您可以使用它来满足您的管理和监控需求。

如果您想轻松地尝试API,一个最新的Paw文件包含所有API路由和示例请求。在此处下载适用于您的Mac的Paw应用程序(Paw是开发者用来测试他们API的工具)。

1. DNS记录管理

要检查、读取、插入、修改和删除DNS记录,您可以使用zone API资源。

API概述

端点URL

HTTPhttp://constellation.local:8080/zone/<zone_name>/record/<record_name>/<record_type>/

其中

  • zone_name:区域名称(即基础域名),例如:relay.crisp.chat
  • record_name:要读取或修改的记录名称(即子域名或基础域名),例如:client.@ 对于 client.relay.crisp.chat FQDN,或 @ 对于 relay.crisp.chat FQDN
  • record_type:要读取或修改 record_name 的 DNS 记录类型;可以是:aaaaacnamemxtxtcaaptr(如果您需要其他记录类型支持,请提交问题

请求头

  • 添加一个带有 Authorization 的请求头,使用 Basic 认证,密码为您的配置 http.record_token

Geo-DNS 区域

如果您想使用 Geo-DNS 功能向最近的服务器提供记录,您将需要通过 API 设置 regions,其中

  • 美洲

    • nnam:北美北部
    • snam:北美南部
    • nsam:南美北部
    • ssam:南美南部
  • 欧洲

    • weu:西欧
    • ceu:中欧
    • eeu:东欧
    • ru:俄罗斯
  • 中东

    • me:中东
  • 非洲

    • naf:北非
    • maf:中非
    • saf:南非
  • 亚洲

    • in:印度
    • seas:东南亚
    • neas:东北亚
  • 大洋洲

    • oc:大洋洲

Geo-DNS 黑洞

如果您想使用 Geo-DNS 功能针对被阻止的国家返回空的 DNS 响应,您将需要通过 API 设置 blackhole,为一个黑名单 ISO-3166 Alpha-2 国家代码 列表(例如,FR 代表法国)。

健康检查的救援记录

如果您正在对区域域使用健康检查,您可能需要指定救援记录,在所有常规记录(标准和 Geo-DNS)都被视为无效的情况下,这些记录将被提供给 DNS 客户端。您可以在 API 中设置 rescue 属性以确保在默认服务器失败的情况下,只有故障转移服务器被提供并连接。

如果您没有设置任何 rescue 记录;在所有常规记录都被报告为无效的情况下,DNS 客户端将收到一个空的响应。因此,您仍然应该提供备用记录。

CNAME 层叠

CNAME记录可以方便地在单个DNS条目中集中记录值,并在多个DNS CNAME条目中重复使用。然而,它也有一些限制,例如,按照DNS RFC规定,它不能与同一子域上的其他记录共享。在域根目录下设置CNAME也是非法的。此外,CNAME需要DNS解析器执行第二次解析步骤来解析平面值(例如,AAAAATXT等记录),这并不高效,因为它增加了用户通过CNAME解析域名时的额外延迟。

如果遇到DNS RFC中CNAME记录类型的边缘情况,CNAME扁平化可以帮助您。它允许Constellation解析实际平面值,并立即提供服务,而不是返回实际的CNAME。可以通过在API中将flatten属性设置为true来为记录启用CNAME扁平化。默认情况下,不执行CNAME扁平化。

一个专门的Constellation线程管理之前扁平化的CNAME值,并随着它们在远程DNS服务器上的更改而更新它们。此外,如果缓存的扁平化CNAME长时间未使用,则将其从缓存中删除。请注意,为了尽快响应用户,如果启用扁平化的CNAME值尚未在缓存中,则Constellation将以CNAME的形式回答,并将延迟扁平化命令委托给扁平化管理线程,以避免减慢对请求用户的初始响应。一旦扁平化管理线程完成其工作,进一步的DNS查询到CNAME将使用扁平化的值回答(例如,它将返回平面A记录值,而不是CNAME值)。

请注意,flatten选项仅适用于具有CNAME值的记录。如果在例如A记录类型上启用了扁平化,则flatten属性将没有任何效果。

API路由

检查DNS记录是否存在

HTTP HEADhttp://constellation.local:8080/zone/<zone_name>/record/<record_name>/<record_type>/

示例请求

HEAD /zone/relay.crisp.chat/record/@/a HTTP/1.1
Authorization: Basic OlJFUExBQ0VfVEhJU19XSVRIX0FfU0VDUkVUX0tFWQ==

示例响应

HTTP/1.1 200 OK
获取DNS记录

HTTP GEThttp://constellation.local:8080/zone/<zone_name>/record/<record_name>/<record_type>/

示例请求

GET /zone/relay.crisp.chat/record/@/a HTTP/1.1
Authorization: Basic OlJFUExBQ0VfVEhJU19XSVRIX0FfU0VDUkVUX0tFWQ==

示例响应

HTTP/1.1 200 OK
Content-Type: application/json

{"type":"a","name":"@","ttl":600,"blackhole": null,"regions": null,"values":["159.89.97.13","46.101.18.133"]}
写入DNS记录(或覆盖现有记录)

HTTP PUThttp://constellation.local:8080/zone/<zone_name>/record/<record_name>/<record_type>/

示例请求(标准)

PUT /zone/relay.crisp.chat/record/@/a HTTP/1.1
Authorization: Basic OlJFUExBQ0VfVEhJU19XSVRIX0FfU0VDUkVUX0tFWQ==
Content-Type: application/json; charset=utf-8

{"values":["159.89.97.13","46.101.18.133"],"ttl":600}

示例请求(地理DNS)

PUT /zone/relay.crisp.chat/record/@/cname HTTP/1.1
Authorization: Basic OlJFUExBQ0VfVEhJU19XSVRIX0FfU0VDUkVUX0tFWQ==
Content-Type: application/json; charset=utf-8

{"regions":{"nnam":["client.nnam.geo.relay.crisp.net"],"snam":["client.snam.geo.relay.crisp.net"],"nsam":["client.nsam.geo.relay.crisp.net"],"ssam":["client.ssam.geo.relay.crisp.net"],"weu":["client.weu.geo.relay.crisp.net"],"ceu":["client.ceu.geo.relay.crisp.net"],"eeu":["client.eeu.geo.relay.crisp.net"],"ru":["client.ru.geo.relay.crisp.net"],"me":["client.me.geo.relay.crisp.net"],"naf":["client.naf.geo.relay.crisp.net"],"maf":["client.maf.geo.relay.crisp.net"],"saf":["client.saf.geo.relay.crisp.net"],"in":["client.in.geo.relay.crisp.net"],"seas":["client.seas.geo.relay.crisp.net"],"neas":["client.neas.geo.relay.crisp.net"],"oc":["client.oc.geo.relay.crisp.net"]},"values":["client.default.geo.relay.crisp.net"],"ttl":600}

示例请求(健康检查)

PUT /zone/relay.crisp.chat/record/@/a HTTP/1.1
Authorization: Basic OlJFUExBQ0VfVEhJU19XSVRIX0FfU0VDUkVUX0tFWQ==
Content-Type: application/json; charset=utf-8

{"values":["159.89.97.13","46.101.18.133"],"rescue":["139.59.174.13"],"ttl":60}

示例响应

HTTP/1.1 200 OK

示例请求(CNAME扁平化)

PUT /zone/relay.crisp.chat/record/@/cname HTTP/1.1
Authorization: Basic OlJFUExBQ0VfVEhJU19XSVRIX0FfU0VDUkVUX0tFWQ==
Content-Type: application/json; charset=utf-8

{"values":["alias.crisp.net"],"flatten":true,"ttl":60}

示例响应

HTTP/1.1 200 OK
删除DNS记录

HTTP DELETEhttp://constellation.local:8080/zone/<zone_name>/record/<record_name>/<record_type>/

示例请求

DELETE /zone/relay.crisp.chat/record/@/a HTTP/1.1
Authorization: Basic OlJFUExBQ0VfVEhJU19XSVRIX0FfU0VDUkVUX0tFWQ==

示例响应

HTTP/1.1 200 OK

2. 服务器使用情况指标检索

要获取服务器使用情况指标(例如,哪些国家的DNS请求),您可以使用metrics API资源。

API概述

端点URL

HTTPhttp://constellation.local:8080/zone/<zone_name>/metrics/<metrics_timespan>/<metrics_category>/<metrics_type>

其中

  • zone_name:区域名称(即基础域名),例如:relay.crisp.chat
  • metrics_timespan:返回指标的时长(可以是:1m5m15m),代表:过去'n分钟'的指标
  • metrics_category:指标类别(可以是:queryanswer
  • metrics_type:类别中的指标类型(如果类别是query,则为typesorigins,如果类别是answer,则为codes

请求头

  • 添加一个带有 Authorization 的请求头,使用 Basic 认证,密码为您的配置 http.record_token

API路由

获取指标

HTTP GEThttp://constellation.local:8080/zone/<zone_name>/metrics/<metrics_timespan>/<metrics_category>/<metrics_type>/

示例请求

GET /zone/relay.crisp.chat/metrics/5m/query/origins HTTP/1.1
Authorization: Basic OlJFUExBQ0VfVEhJU19XSVRIX0FfU0VDUkVUX0tFWQ==

示例响应

HTTP/1.1 200 OK
Content-Type: application/json

{"fr":1203,"us":899,"lv":23,"gb":10,"other":2}

🔥 报告漏洞

如果您在Constellation中发现漏洞,您非常欢迎通过发送加密电子邮件到[email protected]直接向@valeriansaliou报告。请不要在公共GitHub问题中报告漏洞,因为恶意人员可能会利用这些漏洞针对运行未打补丁的Constellation实例的生产服务器。

⚠️ 您必须使用 @valeriansaliou GPG 公钥加密您的电子邮件:🔑valeriansaliou.gpg.pub.asc

依赖项

~33–47MB
~1M SLoC