vault backup: 2026-01-05 13:03:55

This commit is contained in:
windyboy
2026-01-05 13:03:55 +08:00
parent 21460fc35d
commit be7c6cdcc9
589 changed files with 396508 additions and 27 deletions
@@ -0,0 +1,93 @@
```
sudo doveadm pw -s BLF-CRYPT
```
```
{BLF-CRYPT}$2y$05$ituMVYZPOiAnTOApslK18OB7iPpAamHv6wZSd7ed2ZozzAaKXPGZi
```
```
windyboy@2006
```
```
sudo mysql -p -u mailuser mailserver <<'SQL'
INSERT INTO domains(name) VALUES ('windy.me');
INSERT INTO users(email, domain, password, quota_mb)
VALUES ('zhiqiang@windy.me','windy.me','{BLF-CRYPT}$2y$05$cbKWGCrttpBPHpN8R4DFFeGSUF82Upf4EsREifW0jm96A.59IfYfO', 2048);
-- optional alias
INSERT INTO aliases(source, destination) VALUES ('vnet@windy.me','zhiqiang@windy.me');
SQL
```
```
doveadm auth test zhiqiang@windy.me 'windyboy2006'
```
```
# ---- Dovecot 2.4 SQL authentication ----
sql_driver = mysql
# Debian/MariaDB socket (or use 'mysql localhost { ... }' for TCP)
mysql /run/mysqld/mysqld.sock {
user = vmail
password = CHANGE_ME_STRONG
dbname = mailserver
}
# PASSDB: verify credentials (hash in DB, e.g. {BLF-CRYPT}...)
passdb sql {
passdb_default_password_scheme = BLF-CRYPT
query = SELECT email AS username, password AS password \
FROM users \
WHERE email = '%{user}' AND active = 1
}
# USERDB: return uid/gid/home/mail
# Change 5000:5000 if your vmail UID/GID differ: check with "id vmail"
userdb sql {
query = SELECT 5000 AS uid, 5000 AS gid, \
CONCAT('/var/mail/vhosts/', SUBSTRING_INDEX(email,'@',-1), '/', SUBSTRING_INDEX(email,'@',1)) AS home, \
CONCAT('maildir:/var/mail/vhosts/', SUBSTRING_INDEX(email,'@',-1), '/', SUBSTRING_INDEX(email,'@',1), '/Maildir') AS mail \
FROM users \
WHERE email = '%{user}' AND active = 1
iterate_query = SELECT email AS username FROM users WHERE active = 1
}
```
```
swaks --server your.mx.name --port 587 --tls --auth LOGIN -au 'zhiqiang@windy.me' -ap 'windyboy@2006' --h-Subject "SASL test"
```
```
sudo postconf -n | grep -E '^(smtpd_.*restrictions|smtpd_milters|milter_.*|policyd|policy|check_policy_service)'
sudo grep -nE '^(submission|smtps)\b' -n /etc/postfix/master.cf -n
```
```
sudo postconf -P submission/inet/smtpd_recipient_restrictions
sudo postconf -P submission/inet/smtpd_client_restrictions
sudo postconf -P submission/inet/smtpd_helo_restrictions
sudo postconf -P smtps/inet/smtpd_recipient_restrictions
```
google windyboy app passwors (postfix) :
```
zfgl bdep itya kdym
```
/etc/postfix/sasl_passwd
```
[smtp.gmail.com]:587 windyboy@gmail.com:zfglbdepityakdym
```
@@ -0,0 +1,62 @@
ip :
```
38.134.41.134
```
```
imapsync --host1 mx2.windy.me --user1 zhiqiang@windy.me --password1 'windyboy@2006' \
--host2 mx.windy.me --user2 zhiqiang@windy.me --password2 'Nmq2nW!3Y223k@Ri' \
--ssl1 --ssl2 --justlogin
```
test smtp login
prepare
```
echo -n 'zhiqiang@windy.me' | base64
```
```
emhpcWlhbmdAd2luZHkubWU=
```
```
echo -n 'Nmq2nW!3Y223k@Ri' | base64
```
```
Tm1xMm5XITNZMjIza0BSaQ==
```
```
openssl s_client -starttls smtp -crlf -connect smtp.windy.me:587
```
```
EHLO windy.me
```
```
AUTH LOGIN
```
```
openssl x509 -in /opt/mail/data/assets/ssl/mx2.windy.me/cert.pem -noout -pubkey \
| openssl pkey -pubin -outform DER \
| openssl sha256
SHA2-256(stdin)= 83277f3daa67bd613c6ac7556e5f368d9e1245b2fb3209d89de43738a3f083f7
```
```
83277f3daa67bd613c6ac7556e5f368d9e1245b2fb3209d89de43738a3f083f7
```
+74
View File
@@ -0,0 +1,74 @@
icloud mail app password:
thunderbird
Your app-specific password is:
noej-ippd-yisl-uqsk
189.cn
iJ(7wA=4P#0dQ@2u
azure mailstor
url:
https://mailstor.blob.core.windows.net/debian-mail
account:
mailstor
container:
debian-mail
key:
lG7aKaNulWkq8xSgte7k3Xc6IZ56180Ec9FRP1OY2l3wfQttj+dCxVjP/R2Hd3PXKAkn4vQsW7yW+AStiJ92ag==
conn string:
DefaultEndpointsProtocol=https;AccountName=mailstor;AccountKey=lG7aKaNulWkq8xSgte7k3Xc6IZ56180Ec9FRP1OY2l3wfQttj+dCxVjP/R2Hd3PXKAkn4vQsW7yW+AStiJ92ag==;EndpointSuffix=core.windows.net
exmail.qq.com
mac pass:
acBcj9DUCyfoPRm6
disable antivirus
```
#### Re: [SOLVED] ClamAV errors even after disabled
SOLVED.
I've found someone with exactly the same problem ( [https://www.howtoforge.com/community/th … vis.52114/](https://www.howtoforge.com/community/threads/how-to-disable-clamav-or-spamassassin-check-in-amavis.52114/) )
The solution is to create a new file /etc/amavis/conf.d/90-custom with :
use strict;
@bypass_virus_checks_maps  = (1);
#------------ Do not modify anything below this line -------------
1;  # insure a defined return
And restart, this works!
```
postfix admin
```bash
php -r "echo password_hash('windyboy@2006', PASSWORD_DEFAULT);"
```
config.local
```
$CONF['setup_password'] = '$2y$10$WSt0rsujCFKjycFqugG4GuWA2HwokFr91LkG9up8CiV6QDN2EGPPO';
```
```
sudo bash /var/www/postfixadmin/scripts/postfixadmin-cli admin add zhiqiang@windy.me --superadmin 1 --active 1 --password windyboy@2006 --password2 windyboy@2006
```
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
```
route "192.168.0.0 255.255.0.0"
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS [192.168.66.36]"
```
@@ -0,0 +1,24 @@
ssh account
user:
```
zhiqiangf
```
password:
```
Ld2YudlhR3pg7hdK
```
```
dyt.jza*eht9TBD0btd
```
```
set-inform http://192.168.66.46:8080/inform
```
@@ -0,0 +1,21 @@
# 电源
## 通用DC电源
### ER-X
https://manuals.plus/zh-CN/ubiquiti/edgerouterx-manual
12V 0.5A
|---|---|
|**端口**|**产品描述**|
|eth0/PoE 输入|RJ45 端口接受 24V 无源 PoE 并支持 10/100/1000 以太网连接。|
|eth1-3|RJ45 端口支持 10/100/1000 以太网连接。|
|eth4/PoE 输出|RJ45 端口支持无源 PoE 直通和 10/100/1000 以太网连接。|
### 联果2.5G 8口
12V 1A
### Netgear ProSafe GS108PE
48V, 1.25A
@@ -0,0 +1,10 @@
# 狗狗加速
https://panel.dg5.biz
windyboy@gmail.com
半年:90
支付时间:2024-10-28 10:17:35
创建时间:2024-10-28 10:16:12
@@ -0,0 +1,10 @@
https://ausu.autos?uuid=2fb14704-2b87-4f46-a073-2b8828b5e6e9&hmac=04b55a908b662a860b203469a3fade19034a93c6fe8c26d8a9c671077950a771
58.8USD
Due Date: 2024-03-14
android:
https://api.inforun.work/v1/service/10004950?hmac=51EA681FE0EF28CB8766BA258D2555D8C0CBF849E1DCA1183C6C4C59585C1607&lang=&templateId=22
@@ -0,0 +1,18 @@
postgresql:
dendrite/windyboy2006
reCAPTCHA
key: 6LemvrUlAAAAAPqUuH_1V-lWdKAeEORzeAEhor46
secret: 6LemvrUlAAAAAOzHkBnRH3Qxiw2q3YI0ZHdLf6Bh
admin:
windy/catalog@2006
AccessToken: D0lJWbpHRSO4s3zfqxSvO9ZmdWJuV2iPTC5K9VijuuU
matrix media repo:
media_repo:windyboy2006@localhost:matrix_media_repo
@@ -0,0 +1,55 @@
打开http://192.168.1.1直接用超级管理员账户telecomadmin 密码nE7jA%5m登录;
### 设备基本信息
| | |
|---|---|
|设备类型:|YMe 2+1 wifi|
|生产厂家:|SCTY|
|设备型号:|TEWA-600AGM|
|设备标识号:|40F420-4D84440F420AD9629|
|硬件版本:|V1.0|
|软件版本:|Tianyi_V1.0.P05|
### PON信息
| | |
|---|---|
|线路协议:|GPON|
|连接状态:|成功-已注册已认证|
|连接时间:|717326|
|发送光功率:|1.7|
|接收光功率:|-19.5|
### 网关注册信息
| | |
| ------- | --------------- |
| 逻辑ID: | GZ0153330711821 |
### 业务信息
| | | | | | |
| -------- | ---- | -------------- | ------------------------------ | ------------------------------ | ---------------------- |
| 业务类型 | 状态 | IP协议 | 连接方式 | 可用端口 | 连接名称 |
| 上网业务 | 可用 | IPV4 | 桥接(电脑拨号) | 有线:网口1,无线:ChinaNet-vKRJ, | 1_INTERNET_B_VID_41 |
| 可用 | IPV6 | 桥接(电脑拨号) | 有线:网口1,无线:ChinaNet-vKRJ, | 1_INTERNET_B_VID_41 | |
| iTV | 可用 | IPV4 | 桥接 | iTV, | 1_Other_B_VID_45 |
| 可用 | IPV6 | 桥接 | iTV, | 1_Other_B_VID_45 | |
| 语音 | 可用 | IPV4 | 路由 | 电话 | 1_TR069_VOICE_R_VID_46 |
| 管理 | 可用 | IPV4 | 路由 | | 1_TR069_VOICE_R_VID_46 |
| | | | | | |
| | | | | | |
| | | | | | |
|
internet:
vlan:41
802.lp:0
iptv:
vlan_id: 45
802.1p: 5
@@ -0,0 +1,424 @@
# Matrix ESS (Community) — SingleNode Install on Debian 13 (K3s + Traefik + certmanager)
_Last updated: 2025-09-25 08:45 UTC_
This guide installs **Element Server Suite (ESS) Community** (Synapse + MAS + Element Web + Matrix RTC) on a **single Debian 13** node using **K3s**, **Traefik** (default in K3s), and **certmanager** with **Lets Encrypt**. It is tailored to your domain choices:
- **serverName**: `chans.xyz`
- **Hosts**: `synapse.chans.xyz`, `account.chans.xyz`, `chat.chans.xyz`, `mrtc.chans.xyz`
> Tip: if you already have K3s and certmanager installed and working, you can jump to **5. Values files** and **6. Install ESS**.
---
## 0) Requirements & Ports
- Debian 13 (root/sudo), public IPv4 (and optional IPv6).
- DNS control for `chans.xyz`.
- Open/forward these ports to this node:
- **80/tcp**, **443/tcp** (ACME + HTTPS + federation)
- **30881/tcp**, **30882/udp** (Matrix RTC SFU)
- Time in sync (`systemd-timesyncd` or equivalent).
---
## 1) DNS Setup
Create A/AAAA records that point to your nodes public IP(s):
```
chans.xyz A / AAAA -> <YOUR_IP>
synapse.chans.xyz A / AAAA -> <YOUR_IP>
account.chans.xyz A / AAAA -> <YOUR_IP>
chat.chans.xyz A / AAAA -> <YOUR_IP>
mrtc.chans.xyz A / AAAA -> <YOUR_IP>
```
Notes:
- **Do not** use a `CNAME` at the **apex** (`chans.xyz`)—use `A/AAAA`. Subdomains can be `CNAME`s if you prefer.
- Federation relies on `https://chans.xyz/.well-known/matrix/server` which the chart serves for you.
---
## 2) (Optional) CloudInit (without firewalld)
If you build the node via cloudinit, this minimal config installs K3s & Helm and disables swap:
```yaml
#cloud-config
package_update: true
package_upgrade: true
packages: [curl, ca-certificates, gnupg, lsb-release]
runcmd:
- swapoff -a
- sed -ri 's/^[^#].*\sswap\s/## &/g' /etc/fstab
- curl -sfL https://get.k3s.io | sh -s - server
- mkdir -p /home/windy/.kube
- cp /etc/rancher/k3s/k3s.yaml /home/windy/.kube/config
- chown windy:windy /home/windy/.kube/config && chmod 600 /home/windy/.kube/config
- bash -lc 'echo export KUBECONFIG=$HOME/.kube/config >> /home/windy/.bashrc'
- su - windy -c "curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash"
```
You can manage ports at the cloud firewall or your router (no `firewalld` required).
---
## 3) Manual K3s + Helm (if not using cloudinit)
```bash
# Install latest K3s
curl -sfL https://get.k3s.io | sh -s - server
# kubeconfig for your user (replace 'windy' if needed)
mkdir -p ~windy/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~windy/.kube/config
sudo chown windy:windy ~windy/.kube/config
chmod 600 ~windy/.kube/config
echo 'export KUBECONFIG=$HOME/.kube/config' | sudo tee -a ~windy/.bashrc
# Helm
sudo -iu windy bash -lc 'curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash'
```
Verify:
```bash
kubectl get nodes -o wide
kubectl get pods -A
```
You should see the node `Ready` and `traefik` running in `kube-system`.
---
## 4) certmanager + Lets Encrypt (ClusterIssuer)
If you havent installed certmanager yet:
```bash
helm repo add jetstack https://charts.jetstack.io --force-update
kubectl create namespace cert-manager 2>/dev/null || true
helm install cert-manager jetstack/cert-manager -n cert-manager --set crds.enabled=true
```
Create a production ClusterIssuer (`letsencrypt-prod`):
```yaml
# clusterissuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-prod-private-key
solvers:
- http01:
ingress:
class: traefik
```
Apply:
```bash
kubectl apply -f clusterissuer.yaml
kubectl get clusterissuer
```
You should see `letsencrypt-prod READY=True`.
---
## 5) Values files (hosts + TLS)
Create the directory and values files:
```bash
mkdir -p ~/ess-config-values
```
**`~/ess-config-values/hostnames.yaml`**
```yaml
serverName: chans.xyz
elementWeb:
ingress:
host: chat.chans.xyz
synapse:
ingress:
host: synapse.chans.xyz
matrixAuthenticationService:
ingress:
host: account.chans.xyz
matrixRTC:
ingress:
host: mrtc.chans.xyz
```
**`~/ess-config-values/tls.yaml`**
```yaml
global:
ingress:
className: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
tls:
enabled: true
issuer: letsencrypt-prod
```
> The TLS values ensure your Ingresses are annotated for certmanager and include TLS host entries so Certificates are created automatically.
---
## 6) Install ESS (matrixstack chart)
```bash
kubectl create namespace ess 2>/dev/null || true
helm upgrade --install ess oci://ghcr.io/element-hq/ess-helm/matrix-stack -n ess -f ~/ess-config-values/hostnames.yaml -f ~/ess-config-values/tls.yaml --wait
```
Check status:
```bash
kubectl get pods -n ess
kubectl get ingress -n ess
```
You should see ingresses for `synapse`, `account`, `chat`, `mrtc`, and `well-known` with `CLASS=traefik`.
---
## 7) Certificates issuance
Confirm the ingresses have TLS + issuer:
```bash
kubectl -n ess get ingress -o jsonpath='{range .items[*]}{.metadata.name}{" issuer="}{.metadata.annotations.cert-manager\.io/cluster-issuer}{" tlsHosts="}{range .spec.tls[*].hosts}{.}{" "}{end}{"\n"}{end}'
```
Then watch certs:
```bash
kubectl get certificate -n ess
kubectl get order,challenge -n ess
```
When ready, confirm live certs:
```bash
for h in synapse.chans.xyz account.chans.xyz chat.chans.xyz mrtc.chans.xyz chans.xyz; do
echo "=== $h ==="
openssl s_client -connect "$h:443" -servername "$h" </dev/null 2>/dev/null | openssl x509 -noout -issuer -subject -dates
done
```
---
## 8) WellKnown verification (federation & clients)
```bash
curl -s https://chans.xyz/.well-known/matrix/server | jq .
curl -s https://chans.xyz/.well-known/matrix/client | jq .
```
Expected:
- `server``{ "m.server": "synapse.chans.xyz:443" }`
- `client``{ "m.homeserver": { "base_url": "https://synapse.chans.xyz" }, ... }`
Optional federation tester: <https://federationtester.matrix.org/#chans.xyz>
---
## 9) Create the first admin account
Interactive:
```bash
kubectl exec -n ess -it deploy/ess-matrix-authentication-service -- mas-cli manage register-user --admin
```
Noninteractive example:
```bash
kubectl exec -n ess deploy/ess-matrix-authentication-service -- mas-cli manage register-user --yes --admin --username admin --password 'CHANGE_ME_strong_password'
```
Login at **https://chat.chans.xyz**.
---
## 10) Enable selfregistration (optional)
```yaml
# ~/ess-config-values/mas-registration.yaml
matrixAuthenticationService:
additional:
registration.yaml:
config: |
account:
password_registration_enabled: true
password_recovery_enabled: true
login_with_email_allowed: true
```
Apply (include this file):
```bash
helm upgrade --install ess oci://ghcr.io/element-hq/ess-helm/matrix-stack -n ess -f ~/ess-config-values/hostnames.yaml -f ~/ess-config-values/tls.yaml -f ~/ess-config-values/mas-registration.yaml --wait
```
---
## 11) Outbound email (MAS required, Synapse optional)
### 11.1 MAS SMTP (required for signup/reset)
**Option A — inline values (simple):**
```yaml
# ~/ess-config-values/mas-email.yaml
matrixAuthenticationService:
additional:
user-config.yaml:
config: |
email:
from: '"Matrix @ chans.xyz" <noreply@chans.xyz>'
reply_to: '"Support" <support@chans.xyz>'
transport: smtp
mode: starttls
hostname: smtp.windy.me
port: 587
username: noreply@chans.xyz # authenticate as the sender
password: "MAILBOX_PASSWORD"
account:
password_registration_enabled: true
password_recovery_enabled: true
login_with_email_allowed: true
```
**Option B — secret ref (keeps password out of Git):**
```bash
cat > /tmp/mas-user-config.yaml <<'YAML'
email:
from: '"Matrix @ chans.xyz" <noreply@chans.xyz>'
reply_to: '"Support" <support@chans.xyz>'
transport: smtp
mode: starttls
hostname: smtp.windy.me
port: 587
username: noreply@chans.xyz
password: "MAILBOX_PASSWORD"
account:
password_registration_enabled: true
password_recovery_enabled: true
login_with_email_allowed: true
YAML
kubectl -n ess create secret generic mas-extra-config --from-file=user-config.yaml=/tmp/mas-user-config.yaml
```
Then reference it:
```yaml
# ~/ess-config-values/mas-email-secretref.yaml
matrixAuthenticationService:
additional:
user-config.yaml:
configSecret: mas-extra-config
configSecretKey: user-config.yaml
```
Apply (include one of the two files above):
```bash
helm upgrade --install ess oci://ghcr.io/element-hq/ess-helm/matrix-stack -n ess -f ~/ess-config-values/hostnames.yaml -f ~/ess-config-values/tls.yaml -f ~/ess-config-values/mas-email.yaml --wait
# or replace mas-email.yaml with mas-email-secretref.yaml if you used a Secret
```
> **Mailcow 553 fix**: If authenticating as `zhiqiang@windy.me` and sending as `noreply@chans.xyz`, Mailcow rejects with `553 5.7.1 Sender address rejected`. Either (a) **authenticate as** `noreply@chans.xyz` by creating that mailbox in Mailcow and publishing SPF/DKIM/DMARC for `chans.xyz`; or (b) allow “send as” in Mailcows **Sender ACL** for `zhiqiang@windy.me`. Hosting the `chans.xyz` mailbox gives best deliverability (DKIM/DMARC alignment).
Monitor while testing:
```bash
kubectl -n ess logs deploy/ess-matrix-authentication-service -f | grep -iE 'smtp|email|send'
```
### 11.2 Synapse email notifications (optional)
```yaml
# ~/ess-config-values/synapse-email.yaml
synapse:
additional:
email.yaml:
config: |
email:
smtp_host: "smtp.windy.me"
smtp_port: 587
smtp_user: "noreply@chans.xyz"
smtp_pass: "MAILBOX_PASSWORD"
require_transport_security: true
notif_from: "Matrix on chans.xyz <noreply@chans.xyz>"
enable_notifs: true
```
Include this file in your next Helm upgrade.
---
## 12) Health checks & troubleshooting
**Basic:**
```bash
kubectl get pods,svc,ingress,certificate -n ess -o wide
```
**Certs flow:**
```bash
kubectl get certificate,order,challenge -n ess
kubectl describe challenge -n ess <name>
kubectl logs -n kube-system deploy/traefik --tail=200
```
**Wellknown + federation:**
```bash
curl -s https://chans.xyz/.well-known/matrix/server | jq .
curl -s https://chans.xyz/.well-known/matrix/client | jq .
```
**Common pitfalls:**
- Ingresses lack TLS + `cert-manager.io/cluster-issuer` → fix `tls.yaml`.
- `553 Sender address rejected` from Mailcow → align SMTP auth user with sender or allow “send as”, and set SPF/DKIM/DMARC for `chans.xyz`.
- Port 80 blocked → Lets Encrypt HTTP01 fails (check challenges).
- Apex `chans.xyz` not pointing at the node → `.well-known` fails → federation fails.
---
## 13) Upgrades / Uninstall
Upgrade to latest chart:
```bash
helm repo update # if using repos
helm upgrade --install ess oci://ghcr.io/element-hq/ess-helm/matrix-stack -n ess -f ~/ess-config-values/hostnames.yaml -f ~/ess-config-values/tls.yaml --wait
```
Uninstall ESS (keeps PVCs unless you delete them):
```bash
helm uninstall ess -n ess
kubectl delete namespace ess
```
Reset K3s (if ever needed):
```bash
sudo /usr/local/bin/k3s-uninstall.sh
```
---
## 14) Quick copypaste checklist
1. DNS A/AAAA for: `chans.xyz`, `synapse.`, `account.`, `chat.`, `mrtc.` → your IP.
2. K3s running with Traefik; certmanager installed; `ClusterIssuer letsencrypt-prod` **Ready**.
3. `hostnames.yaml` with `*.ingress.host` set to your subdomains.
4. `tls.yaml` with `global.ingress.annotations.cert-manager.io/cluster-issuer=letsencrypt-prod` and TLS enabled.
5. `helm upgrade --install ess …` with both files.
6. `kubectl get certificate -n ess``READY=True`.
7. `/.well-known` returns correct JSON; federation tester OK.
8. Create admin via MAS CLI; log in at `https://chat.chans.xyz`.
9. Configure SMTP for MAS (and optionally Synapse), fix Mailcow sender policy if needed.
@@ -0,0 +1,294 @@
docker compose
```yaml
services:
networks:
proxy:
driver: bridge
services:
traefik:
image: "traefik"
restart: "unless-stopped"
command:
- "--api=true"
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web" # Ensure HTTP challenge uses the web entry point
- "--certificatesresolvers.myresolver.acme.email=zhiqiang@windy.me" # Set your email for Let's Encrypt
- "--certificatesresolvers.myresolver.acme.storage=/certs/acme.json" # Path to store certs
- "--entrypoints.web.address=:80" # Entry point for HTTP
- "--entrypoints.websecure.address=:443" # Entry point for HTTPS
- "--log.level=DEBUG" # Set the log level (optional)
ports:
- "80:80" # Ensure port 80 is exposed for HTTP challenge
- "443:443" # Port 443 for HTTPS
- "8080:8080" # Dashboard (Optional)
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./certs/acme.json:/certs/acme.json"
networks:
- proxy
well-known:
image: "nginx"
restart: "unless-stopped"
volumes:
- ./well-known:/etc/nginx/conf.d
labels:
- "traefik.enable=true"
- "traefik.http.routers.well-known.entrypoints=websecure"
- "traefik.http.routers.well-known.rule=Host(`chans.xyz`) && PathPrefix(`/.well-known`)"
- "traefik.http.routers.well-known.tls=true"
- "traefik.http.routers.well-known.tls.certresolver=myresolver"
networks:
- proxy
synapse:
image: docker.io/matrixdotorg/synapse
restart: unless-stopped
environment:
- SYNAPSE_CONFIG_PATH=/data/homeserver.yaml
volumes:
- ./data:/data
healthcheck:
test: ["CMD", "nc", "-z", "db", "5432"]
interval: 10s
retries: 5
start_period: 10s
timeout: 2s
depends_on:
- db
labels:
- "traefik.enable=true"
- "traefik.http.routers.synapse.rule=Host(`synapse.chans.xyz`)" # Router for synapse.chans.xyz
- "traefik.http.routers.synapse.entrypoints=websecure" # HTTPS traffic
- "traefik.http.routers.synapse.tls=true" # Enable TLS
- "traefik.http.routers.synapse.tls.certresolver=myresolver" # Use Let's Encrypt resolver
- "traefik.http.services.synapse.loadbalancer.server.port=8008" # Synapse backend port
networks:
- proxy
db:
image: docker.io/postgres:14-alpine
restart: unless-stopped
environment:
- POSTGRES_USER=synapse
- POSTGRES_PASSWORD=ucdN6Upc|J,V*J0?
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
volumes:
- ./db:/var/lib/postgresql/data
networks:
- proxy
```
well-known
default.conf
```conf
location /.well-known/matrix/server {
access_log off;
add_header Access-Control-Allow-Origin *;
default_type application/json;
return 200 '{"m.server": "matrix.chans.xyz:443"}';
}
location /.well-known/matrix/client {
access_log off;
add_header Access-Control-Allow-Origin *;
default_type application/json;
return 200 '{"m.homeserver": {"base_url": "https://app.chans.xyz"}}';
}
```
generate config:
```bash
docker run -it --rm --volume ./data:/data -e SYNAPSE_SERVER_NAME=chans.xyz -e SYNAPSE_REPORT_STATS=yes matrixdotorg/synapse generate
```
homeserver.yml
database:
```yaml
name: psycopg2
txn_limit: 10000
args:
user: synapse
password: ucdN6Upc|J,V*J0?
database: synapse
host: synapse_db
port: 5432
cp_min: 5
cp_max: 10
```
```yaml
#
# This is a YAML file: see [1] for a quick introduction. Note in particular
# that *indentation is important*: all the elements of a list or dictionary
# should have the same indentation.
#
# [1] https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html
#
# For more information on how to configure Synapse, including a complete accounting of
# each option, go to docs/usage/configuration/config_documentation.md or
# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html
server_name: "chans.xyz"
pid_file: /data/homeserver.pid
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
resources:
- names: [client, federation]
compress: false
database:
name: psycopg2
txn_limit: 10000
args:
user: synapse
password: ucdN6Upc|J,V*J0?
database: synapse
host: synapse_db
port: 5432
cp_min: 5
cp_max: 10
log_config: "/data/chans.xyz.log.config"
media_store_path: /data/media_store
registration_shared_secret: "lTjbS&oVJ7==Co+4YdbDxR,u7.:d+3qgofIR@9c#*1ULc;M2,*"
report_stats: true
macaroon_secret_key: "fe@vZvVnFFA3j:;hK;DI27;vZk@lHHk~w7foB*Q0D0nd.;tGho"
form_secret: "G*bdHINrFR+@,A3^P=IpayYU3aluiAKcI5@L&E-f#Du:s@MgB6"
signing_key_path: "/data/chans.xyz.signing.key"
trusted_key_servers:
- server_name: "matrix.org"
```
```
sudo certbot --nginx -d chans.xyz -d synapse.chans.xyz
```
```
register_new_matrix_user -c /data/homeserver.yaml http://localhost:8008
```
key:
```
EsT1 s6mK hgBT 3Cnv iYbW SNBD Bf3C LwPs nPbq dXJ8 cbbg aiEs
```
```yaml
# The Matrix integration
matrix:
homeserver: https://chans.xyz
username: "@zhiqiang:chans.xyz"
password: "vaz6PQV5vjg1aya-mvr"
rooms:
- "#hass:chans.xyz"
commands:
- word: testword
name: testword
rooms:
- "#hass:chans.xyz"
- expression: "My name is (?P<name>.*)"
name: introduction
notify:
- name: matrix_notify
platform: matrix
default_room: "#hass:chans.xyz"
automation:
- alias: "React to !testword"
triggers:
- trigger: event
event_type: matrix_command
event_data:
command: testword
actions:
- action: notify.matrix_notify
data:
message: "It looks like you wrote !testword"
- alias: "React to an introduction"
triggers:
- trigger: event
event_type: matrix_command
event_data:
command: introduction
actions:
- action: notify.matrix_notify
data:
message: "Hello {{trigger.event.data.args['name']}}"
```
get token
```
curl -X POST -H "Content-Type: application/json" -d '{
"type": "m.login.password",
"user": "hass",
"password": ".P.fPdJL6.wz77q*9VjD"
}' "https://chans.xyz/_matrix/client/r0/login"
```
```
syt_aGFzcw_cBpXCxWpUSawmWXXmZFL_0v4BCE
```
```
curl -XPOST "https://synapse.chans.xyz/_matrix/client/v3/login" \
-H "Content-Type: application/json" \
-d '{
"type": "m.login.password",
"identifier": {
"type": "m.id.user",
"user": "zhiqiang"
},
"password": "vaz6PQV5vjg1aya-mvr"
}'
```
```
{"access_token":"mct_yDGcVmMw2QyTiPPq4DVEHr5BPjQeqh_w1qQx1","device_id":"MryevHEy6k","user_id":"@zhiqiang:chans.xyz"}%
```
```
mct_yDGcVmMw2QyTiPPq4DVEHr5BPjQeqh_w1qQx1
```
```
matrix:
homeserver: chans.xyz
secret: 'wqfJ1r4cyaQbRNzGUUxjOyFf1g2hvC8F'
endpoint: https://synapse.chans.xyz/
```
@@ -0,0 +1,312 @@
docker compose
```yaml
services:
networks:
proxy:
driver: bridge
services:
traefik:
image: "traefik"
restart: "unless-stopped"
command:
- "--api=true"
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web" # Ensure HTTP challenge uses the web entry point
- "--certificatesresolvers.myresolver.acme.email=zhiqiang@windy.me" # Set your email for Let's Encrypt
- "--certificatesresolvers.myresolver.acme.storage=/certs/acme.json" # Path to store certs
- "--entrypoints.web.address=:80" # Entry point for HTTP
- "--entrypoints.websecure.address=:443" # Entry point for HTTPS
- "--log.level=DEBUG" # Set the log level (optional)
ports:
- "80:80" # Ensure port 80 is exposed for HTTP challenge
- "443:443" # Port 443 for HTTPS
- "8080:8080" # Dashboard (Optional)
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./certs/acme.json:/certs/acme.json"
networks:
- proxy
well-known:
image: "nginx"
restart: "unless-stopped"
volumes:
- ./well-known:/etc/nginx/conf.d
labels:
- "traefik.enable=true"
- "traefik.http.routers.well-known.entrypoints=websecure"
- "traefik.http.routers.well-known.rule=Host(`chans.xyz`) && PathPrefix(`/.well-known`)"
- "traefik.http.routers.well-known.tls=true"
- "traefik.http.routers.well-known.tls.certresolver=myresolver"
networks:
- proxy
synapse:
image: docker.io/matrixdotorg/synapse
restart: unless-stopped
environment:
- SYNAPSE_CONFIG_PATH=/data/homeserver.yaml
volumes:
- ./data:/data
healthcheck:
test: ["CMD", "nc", "-z", "db", "5432"]
interval: 10s
retries: 5
start_period: 10s
timeout: 2s
depends_on:
- db
labels:
- "traefik.enable=true"
- "traefik.http.routers.synapse.rule=Host(`synapse.chans.xyz`)" # Router for synapse.chans.xyz
- "traefik.http.routers.synapse.entrypoints=websecure" # HTTPS traffic
- "traefik.http.routers.synapse.tls=true" # Enable TLS
- "traefik.http.routers.synapse.tls.certresolver=myresolver" # Use Let's Encrypt resolver
- "traefik.http.services.synapse.loadbalancer.server.port=8008" # Synapse backend port
networks:
- proxy
db:
image: docker.io/postgres:14-alpine
restart: unless-stopped
environment:
- POSTGRES_USER=synapse
- POSTGRES_PASSWORD=ucdN6Upc|J,V*J0?
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
volumes:
- ./db:/var/lib/postgresql/data
networks:
- proxy
```
well-known
default.conf
```conf
location /.well-known/matrix/server {
access_log off;
add_header Access-Control-Allow-Origin *;
default_type application/json;
return 200 '{"m.server": "matrix.chans.xyz:443"}';
}
location /.well-known/matrix/client {
access_log off;
add_header Access-Control-Allow-Origin *;
default_type application/json;
return 200 '{"m.homeserver": {"base_url": "https://app.chans.xyz"}}';
}
```
generate config:
```bash
docker run -it --rm --volume ./data:/data -e SYNAPSE_SERVER_NAME=chans.xyz -e SYNAPSE_REPORT_STATS=yes matrixdotorg/synapse generate
```
homeserver.yml
database:
```yaml
name: psycopg2
txn_limit: 10000
args:
user: synapse
password: ucdN6Upc|J,V*J0?
database: synapse
host: synapse_db
port: 5432
cp_min: 5
cp_max: 10
```
```yaml
#
# This is a YAML file: see [1] for a quick introduction. Note in particular
# that *indentation is important*: all the elements of a list or dictionary
# should have the same indentation.
#
# [1] https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html
#
# For more information on how to configure Synapse, including a complete accounting of
# each option, go to docs/usage/configuration/config_documentation.md or
# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html
server_name: "chans.xyz"
pid_file: /data/homeserver.pid
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
resources:
- names: [client, federation]
compress: false
database:
name: psycopg2
txn_limit: 10000
args:
user: synapse
password: ucdN6Upc|J,V*J0?
database: synapse
host: synapse_db
port: 5432
cp_min: 5
cp_max: 10
log_config: "/data/chans.xyz.log.config"
media_store_path: /data/media_store
registration_shared_secret: "lTjbS&oVJ7==Co+4YdbDxR,u7.:d+3qgofIR@9c#*1ULc;M2,*"
report_stats: true
macaroon_secret_key: "fe@vZvVnFFA3j:;hK;DI27;vZk@lHHk~w7foB*Q0D0nd.;tGho"
form_secret: "G*bdHINrFR+@,A3^P=IpayYU3aluiAKcI5@L&E-f#Du:s@MgB6"
signing_key_path: "/data/chans.xyz.signing.key"
trusted_key_servers:
- server_name: "matrix.org"
```
```
sudo certbot --nginx -d chans.xyz -d synapse.chans.xyz
```
```
register_new_matrix_user -c /data/homeserver.yaml http://localhost:8008
```
key:
```
EsT1 s6mK hgBT 3Cnv iYbW SNBD Bf3C LwPs nPbq dXJ8 cbbg aiEs
```
```yaml
# The Matrix integration
matrix:
homeserver: https://chans.xyz
username: "@zhiqiang:chans.xyz"
password: "vaz6PQV5vjg1aya-mvr"
rooms:
- "#hass:chans.xyz"
commands:
- word: testword
name: testword
rooms:
- "#hass:chans.xyz"
- expression: "My name is (?P<name>.*)"
name: introduction
notify:
- name: matrix_notify
platform: matrix
default_room: "#hass:chans.xyz"
automation:
- alias: "React to !testword"
triggers:
- trigger: event
event_type: matrix_command
event_data:
command: testword
actions:
- action: notify.matrix_notify
data:
message: "It looks like you wrote !testword"
- alias: "React to an introduction"
triggers:
- trigger: event
event_type: matrix_command
event_data:
command: introduction
actions:
- action: notify.matrix_notify
data:
message: "Hello {{trigger.event.data.args['name']}}"
```
get token
```
curl -X POST -H "Content-Type: application/json" -d '{
"type": "m.login.password",
"user": "hass",
"password": ".P.fPdJL6.wz77q*9VjD"
}' "https://chans.xyz/_matrix/client/r0/login"
```
```
syt_aGFzcw_cBpXCxWpUSawmWXXmZFL_0v4BCE
```
new matrix ess recover key
```
EsTR 895B q1wv 4ibr ZRaK 9KCK 7nLc xHCm XUGX eYjh TcE5 4XSJ
```
iris account:
```
Awa5noeW9vzLiPRY
```
hass account:
```
sgHoMmOWn8SkYJf#
```
```
kubectl -n ess exec deploy/ess-matrix-authentication-service -- mas-cli manage register-user --yes hass -p "sgHoMmOWn8SkYJf#"
Defaulted container "matrix-authentication-service" out of: matrix-authentication-service, render-config (init), db-wait (init), database-migrate (init)
User attributes
Username: hass
Matrix ID: @hass:chans.xyz
Password: ********
No email address provided, user will be prompted to add one
2025-10-22T09:25:36.174135Z WARN mas_cli::commands::manage:818 No email address provided, user will need to add one
2025-10-22T09:25:36.209840Z INFO mas_cli::commands::manage:835 User registered user.id=01K85KSXSEB2FB6MJHNKZP0BDV
```
```
matrix:
homeserver: "https://chans.xyz"
username: "@hass:chans.xyz"
password: "sgHoMmOWn8SkYJf#"
rooms:
- "#guangzhou:chans.xyz"
```
```
synapse:
additional:
config: |
auto_join_rooms_for_users_on_first_login: true
```
@@ -0,0 +1,37 @@
version: '3'
services:
squid:
image: sameersbn/squid
volumes:
- ./squid.conf:/etc/squid/squid.conf:ro
- squid_cache:/var/spool/squid
ports:
- "3128"
networks:
- proxy
nghttpx:
image: nghttpx/nghttpx
command: nghttpx -f /etc/nghttpx/nghttpx.conf
volumes:
- ./nghttpx.conf:/etc/nghttpx/nghttpx.conf:ro
environment:
- http_proxy=http://squid:3128
- https_proxy=http://squid:3128
- no_proxy=localhost,127.0.0.1,172.20.*
ports:
- "80"
networks:
- proxy
volumes:
squid_cache:
networks:
proxy:
external: true
name: npm
@@ -0,0 +1,6 @@
## dns.windy.lan
domain: dns.windy.lan
ip: 192.168.66.36
root: windyboy
user: windy/windyboy
@@ -0,0 +1,305 @@
# 🌀 从 Slave 节点恢复 PowerDNS Authoritative 主节点(5.0.0 + PostgreSQL
> 本文记录如何从 PowerDNS 从节点完整恢复主节点,包括数据库重建、Zone 导入、TSIG 同步与 DNSSEC 校验。
> 适用于 **PowerDNS Authoritative 5.0.0** + **PostgreSQL gpgsql backend** 环境。
---
## 一、系统角色
|节点|地址|角色|说明|
|---|---|---|---|
|主节点|154.36.174.161|primary|新建|
|从节点|202.91.35.141|secondary|当前持有所有 zone|
|数据库|PostgreSQL 15|backend|gpgsql|
|TSIG|mykey (hmac-sha512)|用于 AXFR 验证||
---
## 二、从 Slave 导出数据
### 1️⃣ 列出所有 zone
```bash
sudo pdnsutil zone list-all
```
### 2️⃣ 导出 zone 文件(PowerDNS 5.0 无 dump-zone
```bash
sudo pdnsutil zone list windy.me > /var/tmp/windy.me.zone
sudo pdnsutil zone list wsvc.info > /var/tmp/wsvc.info.zone
sudo pdnsutil zone list chans.xyz > /var/tmp/chans.xyz.zone
```
### 3️⃣ 导出 TSIG 密钥
```bash
sudo pdnsutil tsigkey list
```
示例:
```
mykey. hmac-sha512. 4es15ROFVNZh76mqbn7sVu1kodAdULYKp8I/jGAWvmH/uyxeyDwqoBiYYBKPro5M+TRkKYn7ulxZKskfKIBKNg==
```
---
## 三、部署主节点环境
### 1️⃣ 目录结构
```
/opt/pdns-primary/
├── docker-compose.yml
├── pdns.conf
└── db-init/
└── 01-init.sql
```
### 2️⃣ docker-compose.yml
```yaml
version: "3.8"
services:
pdns-db:
image: postgres:15
environment:
POSTGRES_USER: pdns
POSTGRES_PASSWORD: windyboy2006
POSTGRES_DB: pdns
volumes:
- ./db-init:/docker-entrypoint-initdb.d
- pdns-db-data:/var/lib/postgresql/data
restart: unless-stopped
auth:
image: powerdns/pdns-auth-50:latest
depends_on:
- pdns-db
volumes:
- ./pdns.conf:/etc/powerdns/pdns.conf:ro
- ./import:/import:ro
ports:
- "53:53/tcp"
- "53:53/udp"
- "8081:8081"
restart: unless-stopped
volumes:
pdns-db-data:
```
### 3️⃣ 初始化数据库
`db-init/01-init.sql`
```sql
CREATE USER pdns WITH PASSWORD 'windyboy2006';
CREATE DATABASE pdns OWNER pdns ENCODING 'UTF8';
```
启动数据库:
```bash
docker compose up -d pdns-db
sleep 10
```
---
## 四、主节点配置(pdns.conf
```ini
primary=yes
secondary=no
launch=gpgsql
gpgsql-host=pdns-db
gpgsql-port=5432
gpgsql-dbname=pdns
gpgsql-user=pdns
gpgsql-password=windyboy2006
gpgsql-dnssec=yes
local-address=0.0.0.0
local-port=53
setuid=pdns
setgid=pdns
loglevel=4
version-string=anonymous
api=yes
api-key=SuperSecretKey
webserver=yes
webserver-address=0.0.0.0
webserver-port=8081
default-soa-edit=INCEPTION-INCREMENT
default-soa-edit-signed=INCEPTION-INCREMENT
disable-axfr=no
```
✅ 所有字段均为 **5.0.0 有效选项**,无 `default-soa-edit-api`
---
## 五、导入 Zone 数据
### 1️⃣ 创建空 zone 并设为 master
```bash
docker compose exec auth pdnsutil zone create windy.me
docker compose exec auth pdnsutil zone set-kind windy.me master
docker compose exec auth pdnsutil zone create wsvc.info
docker compose exec auth pdnsutil zone set-kind wsvc.info master
docker compose exec auth pdnsutil zone create chans.xyz
docker compose exec auth pdnsutil zone set-kind chans.xyz master
```
### 2️⃣ 导入 zone 文件
```bash
docker compose exec auth pdnsutil zone load windy.me /import/windy.me.zone
docker compose exec auth pdnsutil zone load wsvc.info /import/wsvc.info.zone
docker compose exec auth pdnsutil zone load chans.xyz /import/chans.xyz.zone
```
### 3️⃣ 如 zone 含有 RRSIG/DNSKEY,设为 presigned
```bash
docker compose exec auth pdnsutil zone set-presigned windy.me
docker compose exec auth pdnsutil zone set-presigned wsvc.info
docker compose exec auth pdnsutil zone set-presigned chans.xyz
```
---
## 六、导入 TSIG 密钥并授权从节点
### 1️⃣ 导入 TSIG key
```bash
docker compose exec auth pdnsutil tsigkey import "mykey." hmac-sha512 "4es15ROFVNZh76mqbn7sVu1kodAdULYKp8I/jGAWvmH/uyxeyDwqoBiYYBKPro5M+TRkKYn7ulxZKskfKIBKNg=="
```
### 2️⃣ 授权从节点(202.91.35.141
```bash
docker compose exec auth pdnsutil metadata set windy.me TSIG-ALLOW-AXFR "mykey."
docker compose exec auth pdnsutil metadata set windy.me ALLOW-AXFR-FROM "202.91.35.141"
docker compose exec auth pdnsutil metadata set windy.me ALSO-NOTIFY "202.91.35.141"
docker compose exec auth pdnsutil metadata set wsvc.info TSIG-ALLOW-AXFR "mykey."
docker compose exec auth pdnsutil metadata set wsvc.info ALLOW-AXFR-FROM "202.91.35.141"
docker compose exec auth pdnsutil metadata set wsvc.info ALSO-NOTIFY "202.91.35.141"
docker compose exec auth pdnsutil metadata set chans.xyz TSIG-ALLOW-AXFR "mykey."
docker compose exec auth pdnsutil metadata set chans.xyz ALLOW-AXFR-FROM "202.91.35.141"
docker compose exec auth pdnsutil metadata set chans.xyz ALSO-NOTIFY "202.91.35.141"
```
> ⚠️ 不带 `@mykey.`,因为已全局指定 TSIG key。
---
## 七、在从节点配置新的主节点
```bash
sudo pdnsutil zone create-secondary windy.me 154.36.174.161
sudo pdnsutil metadata set windy.me AXFR-MASTER-TSIG "mykey."
sudo pdnsutil zone create-secondary wsvc.info 154.36.174.161
sudo pdnsutil metadata set wsvc.info AXFR-MASTER-TSIG "mykey."
sudo pdnsutil zone create-secondary chans.xyz 154.36.174.161
sudo pdnsutil metadata set chans.xyz AXFR-MASTER-TSIG "mykey."
```
---
## 八、触发 AXFR 同步
### 主节点发送 NOTIFY
```bash
docker compose exec auth pdns_control notify windy.me
docker compose exec auth pdns_control notify wsvc.info
docker compose exec auth pdns_control notify chans.xyz
```
### 从节点主动获取
```bash
sudo pdns_control retrieve windy.me
sudo pdns_control retrieve wsvc.info
sudo pdns_control retrieve chans.xyz
```
---
## 九、验证结果
### 检查 zone 状态
```bash
docker compose exec auth pdnsutil zone list-all
```
### 对比 SOA 序列号
```bash
dig @154.36.174.161 soa windy.me +short
dig @202.91.35.141 soa windy.me +short
```
应相同。
### 查看日志
主节点:
```
AXFR-out zone 'windy.me', client '202.91.35.141' transfer started/done
```
从节点:
```
AXFR done for 'windy.me'
```
---
## 十、常见错误与修复
|日志|原因|修复|
|---|---|---|
|Signature with TSIG key failed|双方 TSIG secret 不一致|重新导入一致的 key|
|Server Not Authoritative / Not Authorized|主节点未授权从节点|执行 metadata set ALLOW-AXFR-FROM|
|AXFR-out denied: client has no permission|同上|增加 ALLOW-AXFR-FROM|
|Trying to set unknown setting 'default-soa-edit-api'|配置无效|删除该字段|
---
## 十一、备份与维护
### 1️⃣ 数据库备份
```bash
docker compose exec pdns-db pg_dump -U pdns pdns > /backup/pdns-$(date +%F).dump
```
### 2️⃣ 导出所有 zone 文件
```bash
mkdir -p /backup/zones
for z in $(docker compose exec auth pdnsutil zone list-all | tr -d '\r'); do
docker compose exec auth pdnsutil zone list "$z" > "/backup/zones/$z-$(date +%F).zone"
done
```
---
@@ -0,0 +1,19 @@
gzzn:
410 456 544
password:
```
w42YyME_y3jVb!qa4X.c
```
win vm:
```
517 010 265
```
password:
```
uW!g6CU6kteozaHUaJX*
```
@@ -0,0 +1,445 @@
````markdown
# Soft Serve 安装指南(Docker Compose + Traefik TCP + CNAME
## 1. 目标与最终形态
- 域名:`repo.windy.me`
- DNS`repo.windy.me` **CNAME → `us2.wsvc.info`**
- 部署主机:`us2.wsvc.info` 对应的 VPS(本文称 "us2"
- Soft Serve 镜像:`ghcr.io/charmbracelet/soft-serve:latest`
- 数据持久化:宿主机 `./data` → 容器 `/var/lib/soft-serve`
- 访问方式:SSHSoft Serve SSH 服务端口为容器内 `23231`
- 暴露方式(推荐):Traefik TCP entrypoint `ssh` 监听宿主机 `2222`,转发到容器 `23231`
---
## 2. 前置条件清单
### 2.1 DNSCNAME
你已设置:
- `repo.windy.me` CNAME → `us2.wsvc.info`
关键含义:
- 用户访问 `repo.windy.me` 时,最终会解析到 **us2 的公网 IP**
- 只要 us2 上对外开放 SSH 入口端口(示例:2222),访问就成立
建议验证(任意机器):
```bash
dig +short repo.windy.me CNAME
dig +short repo.windy.me A
````
### 2.2 网络与防火墙
在 us2 上确保对外放行你用于 Soft Serve SSH 的端口(示例 2222):
- 入站允许:TCP 2222
### 2.3 Traefik 已存在并使用外部网络
你当前 compose 使用:
- external network`vw-net`
确保 Traefik 容器也在同一个 `vw-net` 网络内。
---
## 3. 准备目录与配置文件
在 us2 上:
```bash
mkdir -p /opt/soft-serve
cd /opt/soft-serve
mkdir -p data
```
最终结构:
```
/opt/soft-serve/
compose.yml
.env
data/
```
---
## 4. 准备初始化管理员公钥(必须)
Soft Serve 首次启动会根据环境变量写入初始管理员 key。你已经验证的公钥写法如下(单行):
`.env`
```env
SOFT_SERVE_INITIAL_ADMIN_KEYS=ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE9irsGu03p+1xrwIfzrzjGZCcExJ/XFEgkqsgfEN70j windy@windy-mbp
```
注意事项:
- 必须是 **完整公钥的一整行**
- 只在 **数据目录首次初始化(空目录)** 时生效
---
## 5. Docker Compose(推荐:Traefik TCP 暴露 SSH
`compose.yml`(与你当前成功的结构一致,并保留注释):
```yaml
services:
soft-serve:
image: ghcr.io/charmbracelet/soft-serve:latest
container_name: soft-serve
restart: unless-stopped
environment:
SOFT_SERVE_DATA_PATH: /var/lib/soft-serve
SOFT_SERVE_INITIAL_ADMIN: windy
SOFT_SERVE_INITIAL_ADMIN_KEYS: ${SOFT_SERVE_INITIAL_ADMIN_KEYS}
volumes:
- ./data:/var/lib/soft-serve
# 方案B:直连端口映射(不走 Traefik)
# ports:
# - "2222:23231"
networks:
- traefik
labels:
- traefik.enable=true
# SSH over TCP via Traefik (entryPoint ssh -> container port 23231)
- traefik.tcp.routers.softserve-ssh.entrypoints=ssh
- traefik.tcp.routers.softserve-ssh.rule=HostSNI(`*`)
- traefik.tcp.routers.softserve-ssh.tls=false
- traefik.tcp.services.softserve-ssh.loadbalancer.server.port=23231
networks:
traefik:
external: true
name: vw-net
```
### 关于 "SSH 不能走 Traefik 代理域名分流"的结论
- SSH 不是 HTTPTraefik 在这里是 **TCP 转发**
- 不要使用 `HostSNI(repo.windy.me)` 之类的规则来"按域名"分流 SSH(会引发 TLS/HostSNI 相关报错)
- 最稳妥的做法就是:
- `tls=false`
- `HostSNI('*')`
- 依赖端口入口(2222
---
## 6. Traefik 静态配置要求(必须有 entrypoint
你必须在 Traefik 的静态配置中定义 `ssh` entrypoint,并监听对外端口(示例:2222)。
示例(只示意关键段):
```yaml
entryPoints:
ssh:
address: ":2222"
```
如果缺失,会出现典型错误:
- `EntryPoint doesn't exist entryPointName=ssh`
---
## 7. 首次启动与"只初始化一次"的规则
### 7.1 首次启动
在 `/opt/soft-serve`
```bash
docker compose up -d
docker compose ps
```
### 7.2 初始化只发生一次(关键规则)
如需重新初始化(比如 `.env` 修改后不生效),必须清空数据目录:
```bash
docker compose down
rm -rf ./data
mkdir -p ./data
docker compose up -d
```
---
## 8. 客户端连接与"user not found"修正方法
### 8.1 强制使用指定 key(排错与首次推荐)
你最终验证成功的关键点是:**固定 key + IdentitiesOnly**。
```bash
ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 -p 2222 repo.windy.me info
```
若成功会输出类似:
```
Username: admin (或 windy)
Admin: true
Public keys: ...
```
### 8.2 把默认用户名从 `admin` 改成 `windy`
你已成功的改名命令(注意同样要固定 key):
```bash
ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 -p 2222 repo.windy.me set-username windy
ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 -p 2222 repo.windy.me info
```
**解释:**"user not found" 的真实根因通常不是 Soft Serve 没用户,而是 SSH 客户端未固定 key 时选用了另一把 key,导致 Soft Serve 无法把该连接映射到已存在的用户。
### 8.3 永久固化:写 `~/.ssh/config`
在本机写入:
```sshconfig
Host repo.windy.me
HostName repo.windy.me
Port 2222
User git
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
```
之后即可:
```bash
ssh repo.windy.me info
ssh repo.windy.me repo list
```
---
## 9. 创建仓库与 Git clone/push
### 9.1 创建仓库
```bash
ssh repo.windy.me repo create test
ssh repo.windy.me repo list
```
### 9.2 Clone(推荐写法)
写法 A(最清晰):
```bash
git clone ssh://repo.windy.me:2222/test.git
```
写法 Bscp 风格,依赖 ssh config 的 Port):
```bash
git clone repo.windy.me:test.git
```
### 9.3 Push 验证
```bash
cd test
echo "# test" > README.md
git add .
git commit -m "init"
git push
```
---
## 10. 常见故障排查(快速定位)
### 10.1 连接到错误端口
现象:你以为是 23231,但实际对外是 2222(由 Traefik entrypoint 决定)。
验证(在 us2 上):
```bash
ss -lntp | grep :2222
```
应看到 Traefik 监听 2222。
### 10.2 `EntryPoint doesn't exist entryPointName=ssh`
原因:Traefik 静态配置未定义 `entryPoints.ssh`。
修复:给 Traefik 增加:
```yaml
entryPoints:
ssh:
address: ":2222"
```
并重启 Traefik。
### 10.3 `Error: user not found`
高概率原因:SSH 客户端用了"另一把 key"。
修复(强制固定 key):
```bash
ssh -vvv -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 -p 2222 repo.windy.me info
```
观察日志中是否出现:
- `Offering public key: ... id_ed25519`
- `Server accepts key: ... id_ed25519`
---
## 11. 备份与恢复(生产建议)
### 11.1 需要备份的内容
Soft Serve 核心数据都在宿主机 `./data`(映射自 `/var/lib/soft-serve`):
- `soft-serve.db`(用户/设置)
- `repos/`(仓库数据,如存在)
- `ssh/`host keys 等)
### 11.2 最简单备份命令
在 us2 上:
```bash
cd /opt/soft-serve
tar -czf soft-serve-backup-$(date +%F).tar.gz ./data
```
恢复流程:
1. `docker compose down`
2. 解压覆盖 `./data`
3. `docker compose up -d`
---
## 12. 推荐的"最终检查清单"
- `repo.windy.me` CNAME 指向 `us2.wsvc.info`,并能解析到 us2 IP
- us2 对外开放 TCP 2222
- Traefik 静态配置存在 `entryPoints.ssh=:2222`
- Soft Serve 数据目录持久化:`./data:/var/lib/soft-serve`
- 客户端 `~/.ssh/config` 固定 `IdentityFile` + `IdentitiesOnly yes`
- `ssh repo.windy.me info` 输出 `Username: windy` 且 `Admin: true`
---
## 附录:生产级配置建议(可选)
### A.1 生产级 composehealthcheck、日志限制、只读 filesystem、资源限制)
```yaml
services:
soft-serve:
image: ghcr.io/charmbracelet/soft-serve:latest
container_name: soft-serve
restart: unless-stopped
environment:
SOFT_SERVE_DATA_PATH: /var/lib/soft-serve
SOFT_SERVE_INITIAL_ADMIN: windy
SOFT_SERVE_INITIAL_ADMIN_KEYS: ${SOFT_SERVE_INITIAL_ADMIN_KEYS}
volumes:
- ./data:/var/lib/soft-serve:rw
- /etc/localtime:/etc/localtime:ro
networks:
- traefik
labels:
- traefik.enable=true
- traefik.tcp.routers.softserve-ssh.entrypoints=ssh
- traefik.tcp.routers.softserve-ssh.rule=HostSNI(`*`)
- traefik.tcp.routers.softserve-ssh.tls=false
- traefik.tcp.services.softserve-ssh.loadbalancer.server.port=23231
# 健康检查
healthcheck:
test: ["CMD", "nc", "-z", "localhost", "23231"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# 资源限制
deploy:
resources:
limits:
memory: 512M
cpus: '0.5'
reservations:
memory: 256M
cpus: '0.25'
# 安全设置
read_only: true
tmpfs:
- /tmp:size=100M,mode=1777
# 日志限制
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
traefik:
external: true
name: vw-net
```
### A.2 Traefik 静态配置片段
示例 Traefik 静态配置(`traefik.yml` 或命令行参数):
```yaml
# traefik.yml 示例
entryPoints:
ssh:
address: ":2222"
api:
dashboard: true
insecure: true
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: vw-net
```
或通过命令行参数:
```bash
--entrypoints.ssh.address=:2222
```
---
@@ -0,0 +1,13 @@
service key:
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InFldWRtbGdvc2p2dnJzbWxkY3RrIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc2NTUwMjMyOCwiZXhwIjoyMDgxMDc4MzI4fQ.2rujZwkqatXYyvNwr4hkAWgSGi2q-FqWREicE-5sFBA
```
url:
```
https://qeudmlgosjvvrsmldctk.supabase.co
```
@@ -0,0 +1,11 @@
# WSVC
Client Secret
211073924279107589@wsvc.info
wsvc project:
211074961312382981@wsvc.info
@@ -0,0 +1,89 @@
## Docker + firewalld + iptables 关系总结
### 1. 三者分工
- **iptables**:内核防火墙引擎,真正执行包过滤和 NAT。
- **firewalld**iptables 的“策略管理层”,按 **zone / service / masquerade** 等抽象生成规则。
- **Dockeriptables=true**:在 iptables 中写入 **容器相关** 的规则:
- 容器出网 SNATMASQUERADE
- 宿主端口 → 容器端口的 DNAT
- 容器网络之间的隔离(DOCKER-ISOLATION
三者是“共用 iptables,各管一摊”,不是互相替代。
---
### 2. Docker 关键配置项
`/etc/docker/daemon.json`
```json
{
"iptables": true,
"ip-masq": true
}
```
- `"iptables": true`(默认)
- Docker 创建/维护 DOCKER 链、端口映射、容器出网 NAT 等规则。
- 必须开启,否则大多数容器网络功能会坏(包括端口映射、bridge 容器出网)。
- `"iptables": false`
- Docker 不再改 iptables**不再创建 DOCKER/NAT 规则**。
- 需要你手工写所有 NAT / 端口映射规则。
- 常见现象:宿主机 & `--network host` 容器有网,但所有 bridge 容器出不了网。
- `"ip-masq": true`
- 为 Docker 私网(如 172.17.0.0/16)自动加 MASQUERADE,容器可用宿主 IP 出网。
---
### 3. firewalld 与 Docker 的协作方式
典型做法(推荐):
1. 保持 Docker 使用 iptables
```json
{
"iptables": true,
"ip-masq": true
}
```
2. 在 firewalld 里:
- 为 `docker0`、`br-xxxx` 等网桥分配到 `docker` zone
```bash
firewall-cmd --zone=docker --add-interface=docker0 --permanent
firewall-cmd --zone=docker --add-interface=br-xxxx --permanent
```
- 打开 masquerade 与 forward
```bash
firewall-cmd --zone=docker --add-masquerade --permanent
firewall-cmd --zone=docker --add-forward --permanent
firewall-cmd --reload
```
**原则:**
- Docker 负责:**容器内部路由 + NAT + 端口映射的具体规则**;
- firewalld 负责:**哪些接口/zone 允许转发、伪装、对外开放哪些端口**。
---
### 4. 典型坑点(本次踩到的)
- 设置:
```json
{
"iptables": false
}
```
- 结果:
- 宿主机有网;
- `--network host` 容器有网;
- 所有 bridge 网络容器无外网、访问 LE 超时。
- 根因:
- Docker 停止管理 iptables,不再生成容器 NAT 规则;
- firewalld 只负责 zone 和 masquerade,但**不知道容器网络细节**,无法替 Docker 完成 SNAT/端口映射。
@@ -0,0 +1,71 @@
```
create database vaultwarden;
```
```
CREATE USER vaultwarden WITH ENCRYPTED PASSWORD 'windysecurity';
GRANT ALL PRIVILEGES ON DATABASE vaultwarden TO vaultwarden;
```
```bitwarden.load
LOAD DATABASE
FROM sqlite:///opt/vaultwarden/vw-data/db.sqlite3
INTO postgresql://vaultwarden:windysecurity@localhost:5432/vaultwarden
WITH include drop, create tables, create indexes, reset sequences
EXCLUDING TABLE NAMES LIKE '__diesel_schema_migrations'
ALTER SCHEMA 'main' RENAME TO 'public'
;
```
```
pgloader bitwarden.load
```
```
-- Grant usage and create permissions on the public schema
GRANT USAGE ON SCHEMA public TO vaultwarden;
GRANT CREATE ON SCHEMA public TO vaultwarden;
-- Optionally, grant all permissions on the public schema
GRANT ALL ON SCHEMA public TO vaultwarden;
-- Transfer ownership of the public schema to vaultwarden (optional)
ALTER SCHEMA public OWNER TO vaultwarden;
```
```.env
DOMAIN="https://auth.wsvc.info/"
DATABASE_URL=postgresql://vaultwarden:windysecurity@172.18.0.1:5432/vaultwarden
SMTP_HOST=smtp.windy.me
SMTP_FROM=<vnet@windy.me>
SMTP_PORT=587
SMTP_SECURITY=starttls
SMTP_USERNAME=vnet@windy.me
SMTP_PASSWORD=windyboy2006
```
```admin token
i8aHqBZvgTjCoHKRqMqHxmbFs3JFwWnrzPuub09sUnYKTfwZ7m1VCKXABlSxRkJ6
```
```
echo -n "VjoM4sndg4.8uCzPmodH" | argon2 "$(openssl rand -base64 32)" -e -id -k 19456 -t 2 -p 1
```
```
$argon2id$v=19$m=19456,t=2,p=1$eXhRMTBiVXRjR2pFalpRYStCQys1SmtkaGVONTFJWm9HQmNMVDg2ZGlkVT0$ssdf1xrdTwXP7S7xoRiams1R3nGeSS3dkuKcPD/sO90
```
```
ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$d3Pa5o/TrvEhaVvj/bypWSnBqIFjU/rqkRV+Th7KmHU$ZDwdhqyIrTTvnAsIAUURjN3t3bgNWJfEh8Mv2cY0gUs'
```
@@ -0,0 +1,7 @@
恩山:
[OPENWRT专版-恩山无线论坛 - Powered by Discuz! (right.com.cn)](https://www.right.com.cn/FORUM/forum-72-1.html)
[极简ImmortalWrt及L大原版极简-软路由,x86系统,openwrt(x86),Router OS 等-恩山无线论坛 - Powered by Discuz! (right.com.cn)](https://www.right.com.cn/FORUM/thread-8282522-1-1.html)
[https://www.123pan.com/s/bj1ZVv-49UHd.html](https://www.123pan.com/s/bj1ZVv-49UHd.html)
@@ -0,0 +1,85 @@
🎉 **Success!**
Your boot time improved from **39 seconds → 9 seconds** — thats a **~77% speed increase**. 🚀
Great work applying the fix correctly!
---
### ⏱ Boot Profile Now
|Phase|Before|After|Improvement|
|---|---|---|---|
|Userspace|~36s|~5.5s|🔥 -30.5s|
|Total|~39.4s|~9.0s|👍 -30.4s|
The system is now performing like a modern openSUSE should.
---
### 📌 Next: Confirm SSH Starts Early
Optional but recommended:
```bash
systemctl status sshd.service
```
It should start **right after network.target**, not waiting for full online connection.
If SSH still waits too long for DNS, you can also apply:
```bash
sudo sed -i 's/#UseDNS yes/UseDNS no/' /etc/ssh/sshd_config
sudo systemctl restart sshd
```
---
### 🧽 Optional Cleanup
To remove the leftover serial devices delay (3 seconds earlier):
```bash
sudo systemctl mask serial-getty@ttyS0.service
sudo systemctl mask serial-getty@ttyS1.service
sudo systemctl mask serial-getty@ttyS2.service
sudo systemctl mask serial-getty@ttyS3.service
```
If you **dont** use serial consoles, this is safe and saves another ~3s.
---
### 🚀 Want even faster boot?
I can help optimize:
- ZRAM for faster paging
- Disable unnecessary services (Snapper timers, Avahi if not needed)
- Enable fast boot in GRUB (skip detecting other OS)
- Parallelize or lazy-load more services
Just share:
```bash
systemctl --failed
systemd-analyze blame | head -n 10
```
and Ill provide a tailored further optimization plan.
---
If you'd like, I can also help optimize:
🟢 Startup applications
🟢 Linux network stack speed
🟢 SSD mount tuning (discard, noatime)
🟢 bootloader timeout & fast initrd
Would you like a **one-click optimization script** for openSUSE?
@@ -0,0 +1,7 @@
重要提示【小文件多的不能使用】
一年卡号:42cb7b36a5ded34
下载地址:https://wwxx.lanzouw.com/D88
(必须复制到浏览器打开链接) 下载完成 先解压 再右键管理员身份运行(按教程使用)
每天有100G下载量
图片,音频,小视频 上千小文件禁用
@@ -0,0 +1,7 @@
mount point:
/mnt/tank/Downloads
/mnt/tank/iocage/jails/transmission/root/usr/local/etc/transmission/home/Downloads
mac:
a21d4803aa46 a21d4803aa47
@@ -0,0 +1,22 @@
1、下载简体中文正式版
zh-cn_windows_11_enterprise_ltsc_2024_x64_dvd_cff9cd2d.iso
链接 hxxps://massgrave.dev/windows_ltsc_links
2、下载U盘启动工具rufus
hxxps://github.com/pbatard/rufus/releases/download/v4.6/rufus-4.6p.exe
在制作启动盘前根据图片设置
3、联网通过MAS激活或taobao购买key激活(约5-10rmb
hxxps://github.com/massgravel/Microsoft-Activation-Scripts
安装id
```
2009504 6265236 2833605 8639124 3459384 0687966 2714276 2063255 5191281
```
确认
```
092524 880890 346580 068743 201496 519312 470431 990856
```
@@ -0,0 +1,28 @@
自制精简优化Windows 10 LTSC2021简体中文版
—————————————————————————————————————————
文件名称: WIN10_LTSC2021_X64_ZH-CN_19044.1387.iso
文件大小: 3.34 GB (3,592,355,840 字节)
修改时间: 2021年11月30日
MD5: 9BFFE3984F14CC8F4ACA1F465636F2D3
SHA256: B9451EE3383DAAF3C7AC21DF18DA700F9BC9BAEF310A158B4151F668B980C9CF
CRC32: C27756CF
—————————————————————————————————————————
KMS激活命令:以管理员身份运行CMD(命令提示符)
—————————————————————————————————————————
slmgr /skms kms.03k.org
slmgr /ato
—————————————————————————————————————————
下载链接
—————————————————————————————————————————
阿里云盘:https://www.aliyundrive.com/s/rVRSBYc85Xe (下载文件后去掉后缀.PDF)
百度云盘:https://pan.baidu.com/s/1Nlh_3A-yvqZW2l6dq0hE2g(提取码: ifbs
—————————————————————————————————————————
激活
```
KC4NW-4GGX6-MFFGM-RGMFD-4GDGY
```
+124
View File
@@ -0,0 +1,124 @@
https://bandwagonhost.com/
**quick-flag-3.localdomain**
SPECIAL 80G KVM PROMO V3 - LOS ANGELES - CN2
23.105.208.126
2023-07-15
Semi-Annually: $100.88
matrix.chans.xyz
VM 1719294 — quick-flag-3.localdomain [23.105.208.126]
root: r90Bai3aQqV0
port: 27919
https://manage.hostdare.com/clientarea.php?action=services
new matrix.chans.xyz :
https://rhinotech.cc/
root: 4eFcoxKoC8Gr
2core/2g/30g
debain 10
**$139.80 USD** /year
us4.wsvc.info
mx2.windy.me
103.99.115.4
47 年
us1.wsvc.info
ns2.wsvc.info
32 /年
323-600-314
**CKVM1**
[us1.wsvc.info](http://us1.wsvc.info/)
$32.19 USD
Annually
Sunday, February 4th, 2024
Active
**CKVM2**
[us2.wsvc.info](http://us2.wsvc.info/)
$76.99 USD
Annually
Saturday, December 16th, 2023
Active
**CKVM2**
[us4.wsvc.info](http://us4.wsvc.info/)
$47.59 USD
Annually
Tuesday, November 21st, 2023
[https://10g.biz/](https://10g.biz/)
https://www.rhinotech.cc
hk2.chans.xyz
remark.windy.me
$43.92 USD/year
reinstall:
code: M26JrovIHtgp
https://clients.zgovps.com/index.php?/clientarea/services/special-offer/10527/
Domain [matrix.chans.xyz](http://matrix.chans.xyz)
Registration Date 2024-12-24
Expiry Date 2025-12-24
---
First Payment Amount $52.00 USD
https://app.dartnode.com/
38.134.41.134
https://new.contabo.com
$4.95
194.163.160.244
2a02:c207:2284:8258:0000:0000:0000:0001/64
+649
View File
@@ -0,0 +1,649 @@
### Powerdns
ns1
hk2.chans.xyz
with docker compose:
好的,按你要的思路来:**仍然用“官方 schema 自动建表”的 Compose 方案**PostgreSQL 16 + PowerDNS Authoritative),**另外提供一个“手动恢复数据”的脚本**,支持恢复纯 SQL dump 和 `pg_dump -Fc` 自定义格式 dump。
---
# 目录结构(建议)
```
pdns/
├─ docker-compose.yml
├─ auth/
│ └─ pdns.conf
├─ db-init/
│ └─ 01-pdns-schema.sql # 官方 gpgsql schema(见下文获取方式)
├─ backup/
│ └─ pdns.dump # 你的备份文件(.sql 或 -Fc)
└─ scripts/
└─ restore-pdns.sh # 手动恢复脚本
```
## 1) 获取 PowerDNS 官方 PostgreSQL schema
> **重要**:请下载与你将使用的 `powerdns/pdns-auth-49` **相同小版本**的 schema(例如 4.9.x 对应的 tag),保存为 `db-init/01-pdns-schema.sql`。
> (官方路径通常在 `modules/gpgsqlbackend/schema.pgsql.sql` 对应版本的 tag 下。)
---
## 2) `docker-compose.yml`(保持“官方 schema 建表”的方案)
```yaml
version: "3.9"
services:
db:
image: postgres:16
container_name: pdns-db
environment:
POSTGRES_DB: powerdns
POSTGRES_USER: powerdns
POSTGRES_PASSWORD: ${PDNS_DB_PASSWORD:-changeme_db}
volumes:
- dbdata:/var/lib/postgresql/data
- ./db-init:/docker-entrypoint-initdb.d:ro # 首启时自动导入 schema
- ./backup:/backup:ro # 供手动恢复脚本读取 dump
healthcheck:
test: ["CMD-SHELL", "pg_isready -U powerdns -d powerdns"]
interval: 10s
timeout: 5s
retries: 10
restart: unless-stopped
networks: [dnsnet]
auth:
# 固定到你要用的 4.9.x 小版本,例如 4.9.4
image: powerdns/pdns-auth-49:4.9.4
container_name: pdns-auth
depends_on:
db:
condition: service_healthy
ports:
- "53:53/udp"
- "53:53/tcp"
- "8081:8081" # REST API / 内置 webserver
volumes:
- ./auth/pdns.conf:/etc/powerdns/pdns.conf:ro
environment:
PDNS_API_KEY: ${PDNS_API_KEY:-changeme_api}
healthcheck:
test: ["CMD", "sh", "-c",
"wget -qO- --header='X-API-Key: ${PDNS_API_KEY:-changeme_api}' http://127.0.0.1:8081/api/v1/servers/localhost | grep -q 'version'"]
interval: 10s
timeout: 5s
retries: 12
restart: unless-stopped
networks: [dnsnet]
networks:
dnsnet:
driver: bridge
volumes:
dbdata: {}
```
## 3) `auth/pdns.conf`PostgreSQL 后端示例)
```ini
local-address=0.0.0.0
local-port=53
launch=gpgsql
gpgsql-host=pdns-db
gpgsql-port=5432
gpgsql-dbname=powerdns
gpgsql-user=powerdns
gpgsql-password=changeme_db
api=yes
api-key=changeme_api
webserver=yes
webserver-address=0.0.0.0
webserver-port=8081
webserver-allow-from=127.0.0.1,10.0.0.0/8,192.168.0.0/16
version-string=anonymous
disable-syslog=yes
loglevel=4
```
## 4) 启动
```bash
docker compose up -d
# 等 db 健康检查 OK 后,auth 会启动并可通过 8081 API 访问
```
---
# 手动恢复脚本(支持 .sql 与 -Fc)
`scripts/restore-pdns.sh`:默认**安全模式**是恢复到一个**新数据库**(避免与你现有 schema 冲突),完成后你只需把 `pdns.conf``gpgsql-dbname` 改成新库名并 `docker compose restart auth` 即可。也提供 `--inplace` 选项可“原地覆盖”(会清空原库 `public` 模式)——谨慎使用。
```bash
#!/usr/bin/env bash
set -euo pipefail
# 用法:
# scripts/restore-pdns.sh /absolute/or/relative/path/to/backup/pdns.dump
# 可选: --inplace # 原地覆盖到 powerdns 库(会清空 public schema
#
# 说明:
# - 支持两类 dump:
# 1) 纯 SQL
# 2) pg_dump -Fc 自定义格式
# - 默认行为: 恢复到新库 powerdns_restore_YYYYmmddHHMMSS
# - 容器/数据库参数需与 docker-compose.yml 一致
DB_SVC="db" # Compose 中的服务名
DB_NAME="powerdns"
DB_USER="powerdns"
INPLACE=0
if [[ "${1:-}" == "--inplace" ]]; then
INPLACE=1
shift
fi
if [[ $# -lt 1 ]]; then
echo "Usage: $0 [--inplace] <path-to-dump>"
exit 1
fi
DUMP_PATH="$1"
if [[ ! -f "$DUMP_PATH" ]]; then
echo "Dump file not found: $DUMP_PATH"
exit 1
fi
# 统一让容器内能看到该文件(compose 已挂载 ./backup -> /backup:ro
# 若传入的不是 ./backup 下的文件,临时 cp 进去容器使用
IN_CONTAINER_DUMP=""
if [[ "$DUMP_PATH" == ./backup/* || "$DUMP_PATH" == backup/* ]]; then
# 剥离前缀,映射到 /backup
BN="${DUMP_PATH##*/}"
IN_CONTAINER_DUMP="/backup/${BN}"
else
# 复制到容器临时路径
BN="$(basename "$DUMP_PATH")"
echo "Copying dump into container..."
docker compose cp "$DUMP_PATH" "${DB_SVC}:/tmp/${BN}"
IN_CONTAINER_DUMP="/tmp/${BN}"
fi
# 检测 dump 类型(在容器内使用 'file'
FILE_OUT="$(docker compose exec -T ${DB_SVC} sh -lc "file -b ${IN_CONTAINER_DUMP} || true")"
echo "Detected: ${FILE_OUT}"
IS_FC=0
if echo "$FILE_OUT" | grep -qi 'PostgreSQL custom database dump'; then
IS_FC=1
fi
if [[ $INPLACE -eq 1 ]]; then
echo ">>> INPLACE mode: dropping and recreating schema 'public' in ${DB_NAME}"
docker compose exec -T ${DB_SVC} psql -U "${DB_USER}" -d "${DB_NAME}" -v ON_ERROR_STOP=1 <<'SQL'
DO $$
BEGIN
EXECUTE 'DROP SCHEMA IF EXISTS public CASCADE';
EXECUTE 'CREATE SCHEMA public AUTHORIZATION powerdns';
EXECUTE 'GRANT ALL ON SCHEMA public TO powerdns';
END$$;
SQL
if [[ $IS_FC -eq 1 ]]; then
echo ">>> Restoring (-Fc) into ${DB_NAME} ..."
docker compose exec -T ${DB_SVC} sh -lc \
"pg_restore --clean --if-exists --no-owner -U ${DB_USER} -d ${DB_NAME} ${IN_CONTAINER_DUMP}"
else
echo ">>> Restoring (SQL) into ${DB_NAME} ..."
docker compose exec -T ${DB_SVC} sh -lc \
"psql -v ON_ERROR_STOP=1 -U ${DB_USER} -d ${DB_NAME} -f ${IN_CONTAINER_DUMP}"
fi
echo ">>> Done. INPLACE restore finished for DB: ${DB_NAME}"
else
NEWDB="powerdns_restore_$(date +%Y%m%d%H%M%S)"
echo ">>> Creating new database: ${NEWDB}"
docker compose exec -T ${DB_SVC} sh -lc \
"psql -U ${DB_USER} -d postgres -v ON_ERROR_STOP=1 -c 'CREATE DATABASE ${NEWDB} OWNER ${DB_USER};'"
if [[ $IS_FC -eq 1 ]]; then
echo ">>> Restoring (-Fc) into ${NEWDB} ..."
docker compose exec -T ${DB_SVC} sh -lc \
"pg_restore --clean --if-exists --no-owner -U ${DB_USER} -d ${NEWDB} ${IN_CONTAINER_DUMP}"
else
echo ">>> Restoring (SQL) into ${NEWDB} ..."
docker compose exec -T ${DB_SVC} sh -lc \
"psql -v ON_ERROR_STOP=1 -U ${DB_USER} -d ${NEWDB} -f ${IN_CONTAINER_DUMP}"
fi
echo ">>> Done. Now edit ./auth/pdns.conf:"
echo " gpgsql-dbname=${NEWDB}"
echo ">>> Then restart PDNS:"
echo " docker compose restart auth"
fi
```
给执行权限:
```bash
chmod +x scripts/restore-pdns.sh
```
### 使用示例
1. **默认安全模式(恢复到新库,不影响当前运行):**
```bash
./scripts/restore-pdns.sh ./backup/pdns.dump
# 按提示把 auth/pdns.conf 的 gpgsql-dbname 改为脚本输出的新库名
docker compose restart auth
```
2. **原地覆盖(会清空当前库的 public schema):**
```bash
./scripts/restore-pdns.sh --inplace ./backup/pdns.dump
```
---
## 小结 / 注意点
- **官方 schema 自动建表**:通过 `db-init/01-pdns-schema.sql` 在首次初始化时创建所需表结构,之后你随时可以用上面的脚本**手动恢复**。
- **避免冲突**:若你的 dump 是“包含 schema 的完整备份”,默认脚本会恢复到**新库**,避免与已存在的官方 schema 冲突;你也可以用 `--inplace` 覆盖恢复。
- **版本匹配**:尽量让 dump 的来源 PostgreSQL 版本 ≤ 目标版本(例如从 13/14/15 恢复到 16 一般可行);PowerDNS 版本与 schema 文件要匹配同一小版本。
- **安全回滚**:新库恢复方式便于回滚——只需把 `pdns.conf` 切回旧库名并 `restart auth`
需要我把「下载 schema 的命令」按你的目标 `pdns-auth` 版本具体写死(例如 4.9.4)吗?我可以直接给出一条 `curl` 命令放到 README 里。
## 4.9.8:
#### db init
```
curl -L \
https://raw.githubusercontent.com/PowerDNS/pdns/master/modules/gpgsqlbackend/schema.pgsql.sql \
-o db-init/01-pdns-schema.sql
```
```
docker compose exec -T db \
pg_restore --clean --if-exists --no-owner \
-U pdns -d pdns /backup/pdns.dump
```
```
docker compose exec -e PGPASSWORD=windyboy2006 -T db \
pg_restore --jobs=4 --clean --if-exists --no-owner --no-acl \
-U pdns -d pdns /backup/pdns.dump
```
```
docker compose exec -T db psql -U pdns -d pdns -c "SELECT count(*) FROM domains;"
```
db-init/02-pda.sql
```sql
-- 创建 PowerDNS-Admin 的数据库与用户(与 PDNS 库隔离)
CREATE USER pdnsadmin WITH PASSWORD 'windyboy2006';
CREATE DATABASE pdnsadmin OWNER pdnsadmin ENCODING 'UTF8';
GRANT ALL PRIVILEGES ON DATABASE pdnsadmin TO pdnsadmin;
```
```shell
docker compose exec -T db psql -U ${PDNS_DB_USER:-pdns} -d postgres -v ON_ERROR_STOP=1 \
-c "DO \$\$BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname='pdnsadmin') THEN CREATE ROLE pdnsadmin LOGIN PASSWORD 'changeme_pdapass'; END IF; END\$\$;"
docker compose exec -T db psql -U ${PDNS_DB_USER:-pdns} -d postgres -v ON_ERROR_STOP=1 \
-c "CREATE DATABASE pdnsadmin OWNER pdnsadmin;" || true
```
```shell
docker compose exec -T db psql \
-U "${PDNS_DB_USER:-powerdns}" -d postgres -v ON_ERROR_STOP=1 \
-c "ALTER ROLE \"${PDNSADMIN_DB_USER:-pdns}\" WITH PASSWORD '${PDNSADMIN_DB_PASSWORD}';"
```
```
pda:
image: powerdnsadmin/pda-legacy:latest
container_name: powerdns-admin
depends_on:
db:
condition: service_healthy
auth:
condition: service_started
ports:
- "${PDA_HTTP_PORT:-9191}:80"
environment:
SECRET_KEY: ${PDA_SECRET_KEY:-changeme_pda_secret}
SQLALCHEMY_DATABASE_URI: >-
postgresql://${PDNSADMIN_DB_USER:-pdnsadmin}:${PDNSADMIN_DB_PASSWORD:-changeme_pdapass}@db:5432/${PDNSADMIN_DB:-pdnsadmin}
restart: unless-stopped
networks: [dnsnet]
```
```
docker compose exec -T db psql -U pdns -d postgres -v ON_ERROR_STOP=1
-c "ALTER ROLE pdnsadmin WITH PASSWORD 'windyboy2006';"
```
```
docker compose exec -e PGPASSWORD="$PDNSADMIN_DB_PASSWORD" -T db \
psql -U "${PDNSADMIN_DB_USER:-pdnsadmin}" -d "${PDNSADMIN_DB:-pdnsadmin}" \
-c "select current_user, current_database();"
```
```shell
docker compose exec auth pdnsutil list-zone chans.xyz
```
```
docker compose exec auth pdnsutil add-record chans.xyz hk2 A 154.36.174.161
```
```
docker compose exec auth pdnsutil delete-rrset example.test www AAAA
```
# PowerDNS Auth 变更 IP — 简版操作手册(按本次实际操作)
> 场景回顾:主节点(Docker**154.36.174.161**)更换/确认公网 IP;二级节点 **us1.wsvc.info** 为二进制安装(非 Docker)。前端用 **Nginx Proxy Manager (NPM)** 代理 **PowerDNS-Admin (PDA)** 与 **PDNS API**。
---
## 1) 现状与目标
- **主节点(Docker**`pdns-auth-49:4.9.8` + `postgres:16` + `powerdnsadmin/pda-legacy`
- **从节点(us1**:二进制 `pdns-server`,数据库 `domains` 使用老式字段 `type/master`
- **NPM 单独 compose**,与 PDNS/PDA 通过 **共享外部网络 `npm_proxy`** 互通。
- 对外 **只开放 53/tcp, 53/udp**PDA 与 PDNS API 仅在容器网络内,由 NPM 反代并做 IP 白名单。
---
## 2) 主节点(Docker)操作
### 2.1 网络与 Compose
```bash
docker network create npm_proxy || true
```
**pdns compose 关键点:**
- `auth`
- `ports`: 只保留 `53:53/udp``53:53/tcp`
- `expose`: `8081`API 仅在容器网络可见)。
- `networks`: 加入 `default` + `npm_proxy`
- **挂载目录**`./auth:/etc/powerdns:ro`(避免 pdns.conf 丢失)。
- 健康检查可用 `curl``curl -fsS -H 'X-API-Key: ${PDNS_API_KEY}' http://127.0.0.1:${PDNS_API_PORT}/api/v1/servers/localhost`
- `pda`
- **移除** `9191:80` 的对外发布;仅 `expose: 80`,加入 `npm_proxy` 网络。
### 2.2 `auth/pdns.conf` 关键参数
```ini
api=yes
api-key=<你的长随机值>
webserver=yes
webserver-address=0.0.0.0
webserver-port=8081
webserver-allow-from=127.0.0.1,::1,172.16.0.0/12
launch=gpgsql
# gpgsql-* 与 Postgres 账户一致
```
> **提示**:若本机 `curl http://localhost:8081` 空回应,多半命中 IPv6 `::1`;用 `curl -4` 或把 `::1` 加入 `webserver-allow-from`。
### 2.3 通过 NPM 反代
- NPM `app` 服务加入 `npm_proxy`;发布 `80/443`
- 新建 Proxy Host
- **PDA** → `pda:80`(绑定域名,Access List 白名单)。
- **PDNS API** → `auth:8081`(同上)。
> 在 NPM 容器内自检:
```bash
docker compose exec app curl -sI http://pda:80/ | head
docker compose exec app curl -sI http://auth:8081/api/v1/servers/localhost | head
```
### 2.4 记录修改(本次实际)
- **ns1.wsvc.info A** 改为 `154.36.174.161`
```bash
curl -s -X PATCH -H "X-API-Key: $PDNS_API_KEY" -H 'Content-Type: application/json' \
http://127.0.0.1:8081/api/v1/servers/localhost/zones/wsvc.info. \
-d '{"rrsets":[{"name":"ns1.wsvc.info.","type":"A","changetype":"REPLACE","ttl":86400,"records":[{"content":"154.36.174.161","disabled":false}]}]}'
```
- **hk2.chans.xyz A** 移除 `.101`,保留 `.161`
```bash
curl -s -X PATCH -H "X-API-Key: $PDNS_API_KEY" -H 'Content-Type: application/json' \
http://127.0.0.1:8081/api/v1/servers/localhost/zones/chans.xyz. \
-d '{"rrsets":[{"name":"hk2.chans.xyz.","type":"A","changetype":"REPLACE","ttl":3600,"records":[{"content":"154.36.174.161","disabled":false}]}]}'
```
- 验证:`dig @127.0.0.1 ns1.wsvc.info A +short``dig @127.0.0.1 hk2.chans.xyz A +short`
---
## 3) us1(二进制从节点)操作(本次实际)
> us1 使用二进制 `pdns-server`,数据库 `domains` 为旧 schema`type/master`)。本次已验证以下两种方式皆可;**推荐优先使用 pdnsutil 命令**。
### 3.1 推荐:用 `pdnsutil` 指向新主并拉取
确保允许从区:
```
# /etc/powerdns/pdns.conf
secondary=yes # 旧版本为 slave=yes
```
把各区改为 Secondary,并设置新的主(**154.36.174.161**),然后触发 AXFR
```bash
sudo pdnsutil set-kind wsvc.info secondary
sudo pdnsutil set-kind chans.xyz secondary
sudo pdnsutil set-kind windy.me secondary
# 你的 pdnsutil 支持:change-secondary-zone-primary
sudo pdnsutil change-secondary-zone-primary wsvc.info 154.36.174.161
sudo pdnsutil change-secondary-zone-primary chans.xyz 154.36.174.161
sudo pdnsutil change-secondary-zone-primary windy.me 154.36.174.161
# 立即拉取(若无此命令可重启 pdns 替代)
sudo pdnsutil retrieve-secondary wsvc.info
sudo pdnsutil retrieve-secondary chans.xyz
sudo pdnsutil retrieve-secondary windy.me
```
日志与连通性:
```bash
journalctl -u pdns -e | egrep -i 'SOA|AXFR|IXFR|NOTIFY'
dig @154.36.174.161 wsvc.info SOA +tcp +time=2 +tries=1
```
> 失败多为 **TCP/53 未通** 或主节点未监听 TCP/53。
### 3.2 备选 A:没有该子命令时,用“重建从区”
```bash
sudo pdnsutil delete-zone wsvc.info && sudo pdnsutil create-secondary-zone wsvc.info 154.36.174.161
sudo pdnsutil delete-zone chans.xyz && sudo pdnsutil create-secondary-zone chans.xyz 154.36.174.161
sudo pdnsutil delete-zone windy.me && sudo pdnsutil create-secondary-zone windy.me 154.36.174.161
```
### 3.3 备选 B:直接改数据库后补拉取(你本次已用)
```sql
-- 在 us1 上
UPDATE domains SET type='SLAVE', master='154.36.174.161'
WHERE name IN ('wsvc.info','chans.xyz','windy.me');
```
然后:
```bash
sudo pdnsutil retrieve-secondary wsvc.info || sudo systemctl restart pdns
sudo pdnsutil retrieve-secondary chans.xyz || sudo systemctl restart pdns
sudo pdnsutil retrieve-secondary windy.me || sudo systemctl restart pdns
```
### 3.4 主节点授权(在 161 的容器上执行)
```bash
US1_IP=<us1 公网 IP>
docker compose exec auth pdnsutil set-meta wsvc.info ALLOW-AXFR-FROM $US1_IP
docker compose exec auth pdnsutil set-meta chans.xyz ALLOW-AXFR-FROM $US1_IP
docker compose exec auth pdnsutil set-meta windy.me ALLOW-AXFR-FROM $US1_IP
# 可选:
docker compose exec auth pdnsutil set-meta wsvc.info ALSO-NOTIFY $US1_IP
```
---
## 4) 常见问题(按本次排障)
- **PDA 看不到 zone**PDA 不读数据库,需连 PDNS API。PDA 服务器配置:`API URL=http://auth:8081``API Key``pdns.conf` 一致、`Server ID=localhost`。从 `pda` 容器内 `curl http://auth:8081/...` 验证。
- **`Empty reply from server`**`curl` 命中 IPv6 `::1`;改 `curl -4` 或在 `webserver-allow-from``::1`
- **`auth` Unhealthy**:多数是 API Key 不一致或健康检查命令在镜像中不可用;改用 `curl` 并确保 key 一致。
- **`Received NOTIFY ... not a primary (Refused)`**:从节点仍为 `MASTER`;将其改为 `SLAVE` 并正确设置 `master`
---
## 5) 验收清单(简版)
- 主节点:`db` healthy、`auth` API 可返回版本、PDA 通过 NPM 可访问。
- us1`domains``type=SLAVE``master=154.36.174.161``retrieve-secondary` 成功;日志有 AXFR/IXFR 记录。
- 主节点对 `US1_IP` 设置了 `ALLOW-AXFR-FROM`(必要时 `ALSO-NOTIFY`)。
- 关键记录变更已生效(`dig` 结果正确,考虑 TTL 缓存)。
```
export PGHOST=127.0.0.1
export PGPORT=5432
export PGUSER=pdns
export PGPASSWORD='windyboy2006'
export PGDATABASE=pdns
```
```
pg_dump --no-owner --no-acl --format=p --file=~/pdns_backup_$(date +%F).sql \
--single-transaction
```
```
docker compose exec -T db psql -U pdns -c "DROP DATABASE IF EXISTS pdns;"
docker compose exec -T db psql -U pdns -c "CREATE DATABASE pdns OWNER pdns;"
```
```
docker compose exec -T db psql -U pdns -d template1 -c "DROP DATABASE IF EXISTS pdns;"
docker compose exec -T db psql -U pdns -d template1 -c "CREATE DATABASE pdns OWNER pdns;"
```
```
docker compose exec -T db psql -U pdns -d pdns < backup/pdns_backup_2025-11-03.sql
```
```
docker compose exec -T db psql -U pdns -d pdns -c "UPDATE domains SET type='MASTER';"
```
```
docker compose exec -T db psql -U pdns -d pdns -c "DELETE FROM domainmetadata WHERE kind='AXFR-MASTER-TSIG';"
```
```
docker compose exec -T db psql -U pdns -d pdns -c "TRUNCATE TABLE supermasters;"
```
```
docker compose exec auth pdnsutil list-all-zones
docker compose exec auth pdnsutil check-all-zones
docker compose exec auth pdnsutil list-zone windy.me
```
```
docker compose exec -e PGPASSWORD=windyboy2006 backup psql -h db -U pdns -d pdns -c "\l"
```
@@ -0,0 +1,362 @@
# Install
## debian 13
### vps provider debian 10 install
default root:
```
8txlD7djkkKk
```
Good catch 👍 — youre right.
Debian 11 _Bullseye_ moved from the **main mirrors** to the **Debian archive repository** after its LTS phase ended in 2026. That means:
- You **can still upgrade** from Debian 10 → 11 → 12 → 13,
- But for Debian 11 youll need to point your `sources.list` to the **archive.debian.org** service, since the normal `deb.debian.org` no longer carries Bullseye.
---
## 🔧 How to Handle Missing Debian 11 (Bullseye) Repos
When upgrading **Debian 10 → 11**, change your `/etc/apt/sources.list` to use the archive:
```bash
sed -i 's|deb.debian.org|archive.debian.org|g' /etc/apt/sources.list
sed -i 's|security.debian.org|archive.debian.org|g' /etc/apt/sources.list
```
Then explicitly set it to Bullseye:
```
deb http://archive.debian.org/debian bullseye main contrib non-free
deb http://archive.debian.org/debian-security bullseye-security main contrib non-free
```
⚠️ Important: you also need to **disable `Valid-Until` checks** (since archive metadata is old). Add this to `/etc/apt/apt.conf.d/99no-check-valid-until`:
```
Acquire::Check-Valid-Until "false";
```
After that, `apt update` will work again for Debian 11 packages.
---
## 🔄 Recommended Path
- **Debian 10 → Debian 11 (Bullseye)**
using `archive.debian.org`
- **Debian 11 → Debian 12 (Bookworm)**
switch back to `deb.debian.org`
- **Debian 12 → Debian 13 (Trixie)**
upgrade normally from main repos
---
## ⚠️ Alternative Approach (Skip Hop?)
Some admins **reinstall instead of multi-hop upgrades** when theyre more than 2 releases behind.
Its often faster and cleaner to install Debian 13 fresh and migrate configs/data.
---
👉 Do you want me to prepare the **exact `sources.list` entries** for each step (10 → 11, 11 → 12, 12 → 13), so you can just copy-paste them before each upgrade?
## traefik
```compose.yml
services: 10:47:35 [24/1240]
traefik:
image: traefik:v3.4
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks: [traefik]
ports:
- "80:80"
- "443:443"
- "8080:8080" # dashboard
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
- ./dynamic:/dynamic:ro
command:
# Entrypoints
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.web.http.redirections.entrypoint.permanent=true"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.websecure.http.tls=true"
# Providers
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=traefik"
- "--providers.file.directory=/dynamic"
- "--providers.file.watch=true"
# Let's Encrypt (ACME)
- "--certificatesresolvers.letsencrypt.acme.email=admin@windy.me"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
# Dashboard
- "--api.dashboard=true"
- "--api.insecure=false"
# Logging
- "--log.level=INFO"
- "--accesslog=true"
# Metrics (optional)
- "--metrics.prometheus=true"
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`npm.chans.xyz`)"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.dashboard.middlewares=dashboard-auth@docker"
- "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$wrhTVUaG$$tcchNFj..."
networks:
traefik:
external: true
```
dashboard user and pass
```
Ahku+eRei_chu3ah
```
```
htpasswd -nb windy "Ahku+eRei_chu3ah"
```
```
windy:$apr1$wrhTVUaG$tcchNFj.yyA3OpK8f9XnA.
```
## 一步改成 MASTER
执行以下命令即可统一切换类型:
```
docker compose exec -T db psql -U pdns -d pdns -c "UPDATE domains SET type='MASTER';"
```
执行完,再确认:
```
docker compose exec -T db psql -U pdns -d pdns -c "SELECT id, name, type FROM domains ORDER BY name;"
```
应输出:
```
id | name | type ----+-----------+--------- 7 | chans.xyz | MASTER 9 | windy.me | MASTER 8 | wsvc.info | MASTER (3 rows)
```
---
## ✅ 一、明确两种元数据的作用
|kind|作用|主节点是否需要|
|---|---|---|
|`PRESIGNED`|表示该 zone 的 DNSSEC 已经签好,不需要 PowerDNS 重新签名|✅ 需要保留|
|`AXFR-MASTER-TSIG`|从节点用来验证上游 master(旧主)的 TSIG 密钥|❌ 主节点不需要|
---
## 🧹 二、删除无用的 `AXFR-MASTER-TSIG` 记录
执行:
```bash
docker compose exec -T db psql -U pdns -d pdns -c "DELETE FROM domainmetadata WHERE kind='AXFR-MASTER-TSIG';"
```
验证删除结果:
```bash
docker compose exec -T db psql -U pdns -d pdns -c "SELECT domain_id, kind, content FROM domainmetadata;"
```
应该只剩:
```
domain_id | kind | content
------------+------------+---------
7 | PRESIGNED | 1
8 | PRESIGNED | 1
9 | PRESIGNED | 1
(3 rows)
```
---
## 🧩 三、保留 `PRESIGNED`(不要删)
这三条 `PRESIGNED` 的存在说明你的 zone 已经是“预签名区”,
也就是在数据库里已经有 DNSKEY、RRSIG、NSEC/NSEC3 等签名记录,
PowerDNS 不会去重新签名,非常正确 ✅
---
## 🧰 四、确保 `supermasters` 已清空(如果还没执行)
```bash
docker compose exec -T db psql -U pdns -d pdns -c "TRUNCATE TABLE supermasters;"
```
---
## 🚀 五、重启 PDNS 并验证主节点状态
```bash
docker compose exec auth pdnsutil list-all-zones
docker compose exec auth pdnsutil check-all-zones
```
预期输出:
```
Zone "chans.xyz." (MASTER)
Zone "windy.me." (MASTER)
Zone "wsvc.info." (MASTER)
All zones OK
```
---
## ✅ 六、总结(当前应保留状态)
|表|应保留内容|已处理状态|
|---|---|---|
|domains|type=MASTER|✅|
|domainmetadata|仅 `PRESIGNED`|🧹 已清理|
|supermasters|空表|✅|
|records / cryptokeys / tsigkeys|原样保留|✅|
---
如果你愿意,我可以帮你生成一个命令脚本,一次性完成:
- 把所有域设为 MASTER
- 删除 AXFR/MASTER 相关 metadata
- 清空 supermasters
- 验证 zone 状态
你只需执行一条命令即可让任何副节点快照变主节点。是否要我生成?
```
docker compose exec auth pdnsutil metadata set wsvc.info ALLOW-AXFR-FROM "202.91.35.141"
docker compose exec auth pdnsutil metadata set windy.me ALLOW-AXFR-FROM "202.91.35.141"
docker compose exec auth pdnsutil metadata set chans.xyz ALLOW-AXFR-FROM "202.91.35.141"
docker compose exec auth pdnsutil metadata set wsvc.info TSIG-ALLOW-AXFR "mykey."
Set 'wsvc.info' meta TSIG-ALLOW-AXFR = mykey.
docker compose exec auth pdnsutil metadata set windy.me TSIG-ALLOW-AXFR "mykey."
Set 'windy.me' meta TSIG-ALLOW-AXFR = mykey.
docker compose exec auth pdnsutil metadata set chans.xyz TSIG-ALLOW-AXFR "mykey."
Set 'chans.xyz' meta TSIG-ALLOW-AXFR = mykey.
docker compose exec auth pdns_control notify windy.me
docker compose exec auth pdns_control notify chans.xyz
docker compose exec auth pdns_control notify wsvc.info
```
db-init/01-init.sh
```bash
#!/bin/bash
set -e
echo "🔧 Creating PowerDNS role and databases..."
psql -v ON_ERROR_STOP=1 --username "$PGUSER" <<-'EOSQL'
DO $$
BEGIN
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'pdns') THEN
CREATE USER pdns WITH PASSWORD 'windyboy';
END IF;
END
$$;
EOSQL
for dbname in pdns pdnsadmin; do
if ! psql -tAc "SELECT 1 FROM pg_database WHERE datname='${dbname}'" | grep -q 1; then
echo "🆕 Creating database ${dbname} owned by pdns"
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" -c "CREATE DATABASE ${dbname} OWNER pdns;"
else
echo "✅ Database ${dbname} already exists"
fi
done
echo "✅ Initialization finished."
```
```
for z in windy.me chans.xyz wsvc.info; do
docker compose exec auth pdnsutil zone unset-presigned $z
docker compose exec auth pdnsutil zone secure $z
docker compose exec auth pdnsutil zone rectify $z
done
```
```
docker compose exec auth pdnsutil zone list-all | while read z; do
docker compose exec auth pdnsutil zone list "$z" > "auth/export/$z.zone"
done
```
```
labels:
- "traefik.enable=true"
- "traefik.http.routers.pgweb.rule=Host(`pgweb.wsvc.info`)"
- "traefik.http.routers.pgweb.entrypoints=websecure"
- "traefik.http.routers.pgweb.tls.certresolver=letsencrypt"
- "traefik.http.services.pgweb.loadbalancer.server.port=8081"
```
@@ -0,0 +1,94 @@
```
version: "3.8"
services:
squid:
image: ubuntu/squid:latest
container_name: squid-proxy
restart: unless-stopped
volumes:
- ./config/squid.conf:/etc/squid/squid.conf:ro
- squid_cache:/var/spool/squid
- squid_logs:/var/log/squid
networks:
- proxy-net
- traefik
# 只在本地暴露端口(可选,用于调试)
ports:
# - "127.0.0.1:3128:3128"
healthcheck:
test: ["CMD", "squidclient", "-h", "localhost", "mgr:info", "||", "exit", "1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
# Squid 管理界面路由
#- "traefik.http.routers.squid-mgr.rule=Host(`squid.yourdomain.com`) && PathPrefix(`/squid-internal-mgr`)"
#- "traefik.http.routers.squid-mgr.entrypoints=websecure"
#- "traefik.http.routers.squid-mgr.tls.certresolver=letsencrypt"
#- "traefik.http.routers.squid-mgr.middlewares=squid-auth"
#- "traefik.http.services.squid-mgr.loadbalancer.server.port=3128"
# Basic Auth 中间件
#- "traefik.http.middlewares.squid-auth.basicauth.users=admin:$$apr1$$8EVjn/nj$$GiLUZqcbueTFeD23SuB6x0"
nghttpx:
image: jehrhart/nghttp2docker
container_name: nghttpx-proxy
restart: unless-stopped
volumes:
- ./config/nghttpx.conf:/nghttpx/nghttpx.conf:ro
command: nghttpx --conf /nghttpx/nghttpx.conf
depends_on:
squid:
condition: service_healthy
networks:
- proxy-net
- traefik
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/", "||", "exit", "1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
# HTTP/2 代理主路由
- "traefik.http.routers.nghttpx.rule=Host(`proxy.yourdomain.com`)"
- "traefik.http.routers.nghttpx.entrypoints=websecure"
- "traefik.http.routers.nghttpx.tls.certresolver=letsencrypt"
- "traefik.http.routers.nghttpx.tls.options=modern@file"
- "traefik.http.services.nghttpx.loadbalancer.server.port=8080"
# HTTP 到 HTTPS 重定向
- "traefik.http.routers.nghttpx-http.rule=Host(`proxy.yourdomain.com`)"
- "traefik.http.routers.nghttpx-http.entrypoints=web"
- "traefik.http.routers.nghttpx-http.middlewares=redirect-to-https@docker"
# 中间件
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true"
# 可选:添加速率限制
- "traefik.http.routers.nghttpx.middlewares=rate-limit@docker"
- "traefik.http.middlewares.rate-limit.ratelimit.average=100"
- "traefik.http.middlewares.rate-limit.ratelimit.burst=50"
networks:
traefik:
external: true
volumes:
squid_cache:
driver: local
squid_logs:
driver: local
```
@@ -0,0 +1,67 @@
```
services:
traefik:
image: traefik:v3.4
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- proxy
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./certs:/certs
- ./dynamic:/dynamic
command:
# Entrypoints
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.websecure.http.tls=true"
# Providers
- "--providers.file.filename=/dynamic/tls.yaml"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=proxy"
# API & Dashboard
- "--api.dashboard=true"
- "--api.insecure=false"
# Logs
- "--log.level=INFO"
- "--accesslog=true"
- "--metrics.prometheus=true"
# Let's Encrypt (ACME)
- "--certificatesresolvers.letsencrypt.acme.email=zhiqiang@windy.me"
- "--certificatesresolvers.letsencrypt.acme.storage=/certs/acme.json"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
labels:
- traefik.enable=true
- traefik.http.routers.dashboard.rule=Host(`us4.wsvc.info`)
- traefik.http.routers.dashboard.entrypoints=websecure,web
- traefik.http.routers.dashboard.service=api@internal
- traefik.http.routers.dashboard.tls.certresolver=letsencrypt
- "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$aBNLDToX$$nIKWMN41tGMBhtdSaA/Ih/"
- traefik.http.routers.dashboard.middlewares=dashboard-auth@docker,redirect-to-https@file
networks:
proxy:
name: proxy
external: true
```
```
S3cureP@ssw0rd!
```