- Deploy PowerDNS on k3s with SQLite backend - Add DNS ports 53 UDP/TCP to security group - Configure zones for superviber.com, muffinlabs.ai, letemcook.com, appbasecamp.com, thanksforborrowing.com - Add deploy-powerdns.sh standalone deployment script - Document in RFC 0003 Glue records updated at GoDaddy to point ns1/ns2 to 3.218.167.115. DNS verified working via Google DNS (8.8.8.8). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
352 lines
9 KiB
Bash
Executable file
352 lines
9 KiB
Bash
Executable file
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
# Deploy PowerDNS on k3s
|
|
# Usage: sudo ./deploy-powerdns.sh <PUBLIC_IP>
|
|
# Example: sudo ./deploy-powerdns.sh 3.218.167.115
|
|
|
|
if [ $# -lt 1 ]; then
|
|
echo "Usage: $0 <PUBLIC_IP>"
|
|
echo "Example: $0 3.218.167.115"
|
|
exit 1
|
|
fi
|
|
|
|
PUBLIC_IP="$1"
|
|
|
|
echo "=========================================="
|
|
echo "Deploying PowerDNS with IP: $PUBLIC_IP"
|
|
echo "=========================================="
|
|
|
|
# Create data directory
|
|
echo "Creating data directory..."
|
|
mkdir -p /data/powerdns
|
|
chown 953:953 /data/powerdns
|
|
|
|
# Generate API key
|
|
PDNS_API_KEY=$(openssl rand -hex 32)
|
|
echo "$PDNS_API_KEY" > /root/.pdns-api-key
|
|
chmod 600 /root/.pdns-api-key
|
|
echo "API key saved to /root/.pdns-api-key"
|
|
|
|
# Deploy PowerDNS
|
|
echo "Deploying PowerDNS to k3s..."
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Namespace
|
|
metadata:
|
|
name: dns
|
|
---
|
|
apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: powerdns-api-key
|
|
namespace: dns
|
|
type: Opaque
|
|
stringData:
|
|
api-key: "$PDNS_API_KEY"
|
|
---
|
|
apiVersion: v1
|
|
kind: PersistentVolume
|
|
metadata:
|
|
name: powerdns-data
|
|
spec:
|
|
capacity:
|
|
storage: 1Gi
|
|
accessModes:
|
|
- ReadWriteOnce
|
|
hostPath:
|
|
path: /data/powerdns
|
|
storageClassName: local
|
|
persistentVolumeReclaimPolicy: Retain
|
|
---
|
|
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata:
|
|
name: powerdns-data
|
|
namespace: dns
|
|
spec:
|
|
accessModes:
|
|
- ReadWriteOnce
|
|
resources:
|
|
requests:
|
|
storage: 1Gi
|
|
storageClassName: local
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: powerdns
|
|
namespace: dns
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels:
|
|
app: powerdns
|
|
strategy:
|
|
type: Recreate
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: powerdns
|
|
spec:
|
|
securityContext:
|
|
fsGroup: 953
|
|
initContainers:
|
|
- name: init-db
|
|
image: powerdns/pdns-auth-49:4.9.2
|
|
command:
|
|
- /bin/sh
|
|
- -c
|
|
- |
|
|
if [ ! -f /var/lib/powerdns/pdns.sqlite3 ]; then
|
|
sqlite3 /var/lib/powerdns/pdns.sqlite3 < /usr/local/share/doc/pdns/schema.sqlite3.sql
|
|
chown 953:953 /var/lib/powerdns/pdns.sqlite3
|
|
fi
|
|
volumeMounts:
|
|
- name: data
|
|
mountPath: /var/lib/powerdns
|
|
containers:
|
|
- name: powerdns
|
|
image: powerdns/pdns-auth-49:4.9.2
|
|
ports:
|
|
- name: dns-udp
|
|
containerPort: 53
|
|
protocol: UDP
|
|
- name: dns-tcp
|
|
containerPort: 53
|
|
protocol: TCP
|
|
- name: api
|
|
containerPort: 8081
|
|
protocol: TCP
|
|
env:
|
|
- name: PDNS_AUTH_API_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: powerdns-api-key
|
|
key: api-key
|
|
command:
|
|
- pdns_server
|
|
args:
|
|
- --api=yes
|
|
- --api-key=\$(PDNS_AUTH_API_KEY)
|
|
- --webserver=yes
|
|
- --webserver-address=0.0.0.0
|
|
- --webserver-port=8081
|
|
- --webserver-allow-from=0.0.0.0/0
|
|
- --launch=gsqlite3
|
|
- --gsqlite3-database=/var/lib/powerdns/pdns.sqlite3
|
|
- --local-address=0.0.0.0
|
|
- --local-port=53
|
|
volumeMounts:
|
|
- name: data
|
|
mountPath: /var/lib/powerdns
|
|
resources:
|
|
requests:
|
|
cpu: 50m
|
|
memory: 64Mi
|
|
limits:
|
|
cpu: 500m
|
|
memory: 256Mi
|
|
volumes:
|
|
- name: data
|
|
persistentVolumeClaim:
|
|
claimName: powerdns-data
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: powerdns
|
|
namespace: dns
|
|
spec:
|
|
type: LoadBalancer
|
|
externalTrafficPolicy: Local
|
|
selector:
|
|
app: powerdns
|
|
ports:
|
|
- name: dns-udp
|
|
port: 53
|
|
targetPort: 53
|
|
protocol: UDP
|
|
- name: dns-tcp
|
|
port: 53
|
|
targetPort: 53
|
|
protocol: TCP
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: powerdns-api
|
|
namespace: dns
|
|
spec:
|
|
type: ClusterIP
|
|
selector:
|
|
app: powerdns
|
|
ports:
|
|
- name: api
|
|
port: 8081
|
|
targetPort: 8081
|
|
protocol: TCP
|
|
EOF
|
|
|
|
echo "Waiting for PowerDNS pod to be ready..."
|
|
kubectl wait --for=condition=ready pod -l app=powerdns -n dns --timeout=120s
|
|
|
|
# Get PowerDNS API service IP
|
|
PDNS_HOST="http://$(kubectl get svc powerdns-api -n dns -o jsonpath='{.spec.clusterIP}'):8081"
|
|
|
|
echo "PowerDNS API: $PDNS_HOST"
|
|
|
|
# Wait for API to be ready
|
|
echo "Waiting for PowerDNS API..."
|
|
until curl -sf -H "X-API-Key: $PDNS_API_KEY" "$PDNS_HOST/api/v1/servers/localhost" > /dev/null 2>&1; do
|
|
sleep 2
|
|
done
|
|
echo "PowerDNS API is ready!"
|
|
|
|
# Function to create zone
|
|
create_zone() {
|
|
local DOMAIN=$1
|
|
echo "Creating zone: $DOMAIN"
|
|
|
|
curl -sf -X POST "$PDNS_HOST/api/v1/servers/localhost/zones" \
|
|
-H "X-API-Key: $PDNS_API_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{
|
|
\"name\": \"$DOMAIN.\",
|
|
\"kind\": \"Native\",
|
|
\"nameservers\": [\"ns1.$DOMAIN.\", \"ns2.$DOMAIN.\"]
|
|
}" 2>/dev/null || echo " (zone may already exist)"
|
|
}
|
|
|
|
# Function to setup records
|
|
setup_records() {
|
|
local DOMAIN=$1
|
|
echo "Setting up records for: $DOMAIN"
|
|
|
|
curl -sf -X PATCH "$PDNS_HOST/api/v1/servers/localhost/zones/$DOMAIN." \
|
|
-H "X-API-Key: $PDNS_API_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{
|
|
\"rrsets\": [
|
|
{
|
|
\"name\": \"$DOMAIN.\",
|
|
\"type\": \"NS\",
|
|
\"ttl\": 86400,
|
|
\"changetype\": \"REPLACE\",
|
|
\"records\": [
|
|
{\"content\": \"ns1.$DOMAIN.\", \"disabled\": false},
|
|
{\"content\": \"ns2.$DOMAIN.\", \"disabled\": false}
|
|
]
|
|
},
|
|
{
|
|
\"name\": \"ns1.$DOMAIN.\",
|
|
\"type\": \"A\",
|
|
\"ttl\": 3600,
|
|
\"changetype\": \"REPLACE\",
|
|
\"records\": [{\"content\": \"$PUBLIC_IP\", \"disabled\": false}]
|
|
},
|
|
{
|
|
\"name\": \"ns2.$DOMAIN.\",
|
|
\"type\": \"A\",
|
|
\"ttl\": 3600,
|
|
\"changetype\": \"REPLACE\",
|
|
\"records\": [{\"content\": \"$PUBLIC_IP\", \"disabled\": false}]
|
|
},
|
|
{
|
|
\"name\": \"$DOMAIN.\",
|
|
\"type\": \"A\",
|
|
\"ttl\": 300,
|
|
\"changetype\": \"REPLACE\",
|
|
\"records\": [{\"content\": \"$PUBLIC_IP\", \"disabled\": false}]
|
|
},
|
|
{
|
|
\"name\": \"www.$DOMAIN.\",
|
|
\"type\": \"CNAME\",
|
|
\"ttl\": 300,
|
|
\"changetype\": \"REPLACE\",
|
|
\"records\": [{\"content\": \"$DOMAIN.\", \"disabled\": false}]
|
|
}
|
|
]
|
|
}"
|
|
}
|
|
|
|
echo ""
|
|
echo "Creating DNS zones..."
|
|
for domain in superviber.com muffinlabs.ai letemcook.com appbasecamp.com thanksforborrowing.com alignment.coop; do
|
|
create_zone $domain
|
|
setup_records $domain
|
|
done
|
|
|
|
# Setup superviber.com beyondtheuniverse services
|
|
echo ""
|
|
echo "Setting up beyondtheuniverse.superviber.com services..."
|
|
curl -sf -X PATCH "$PDNS_HOST/api/v1/servers/localhost/zones/superviber.com." \
|
|
-H "X-API-Key: $PDNS_API_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{
|
|
\"rrsets\": [
|
|
{
|
|
\"name\": \"beyondtheuniverse.superviber.com.\",
|
|
\"type\": \"A\",
|
|
\"ttl\": 300,
|
|
\"changetype\": \"REPLACE\",
|
|
\"records\": [{\"content\": \"$PUBLIC_IP\", \"disabled\": false}]
|
|
},
|
|
{
|
|
\"name\": \"git.beyondtheuniverse.superviber.com.\",
|
|
\"type\": \"A\",
|
|
\"ttl\": 300,
|
|
\"changetype\": \"REPLACE\",
|
|
\"records\": [{\"content\": \"$PUBLIC_IP\", \"disabled\": false}]
|
|
},
|
|
{
|
|
\"name\": \"mail.beyondtheuniverse.superviber.com.\",
|
|
\"type\": \"A\",
|
|
\"ttl\": 300,
|
|
\"changetype\": \"REPLACE\",
|
|
\"records\": [{\"content\": \"$PUBLIC_IP\", \"disabled\": false}]
|
|
},
|
|
{
|
|
\"name\": \"auth.beyondtheuniverse.superviber.com.\",
|
|
\"type\": \"A\",
|
|
\"ttl\": 300,
|
|
\"changetype\": \"REPLACE\",
|
|
\"records\": [{\"content\": \"$PUBLIC_IP\", \"disabled\": false}]
|
|
},
|
|
{
|
|
\"name\": \"vault.beyondtheuniverse.superviber.com.\",
|
|
\"type\": \"A\",
|
|
\"ttl\": 300,
|
|
\"changetype\": \"REPLACE\",
|
|
\"records\": [{\"content\": \"$PUBLIC_IP\", \"disabled\": false}]
|
|
},
|
|
{
|
|
\"name\": \"grafana.beyondtheuniverse.superviber.com.\",
|
|
\"type\": \"A\",
|
|
\"ttl\": 300,
|
|
\"changetype\": \"REPLACE\",
|
|
\"records\": [{\"content\": \"$PUBLIC_IP\", \"disabled\": false}]
|
|
}
|
|
]
|
|
}"
|
|
|
|
echo ""
|
|
echo "=========================================="
|
|
echo "PowerDNS deployment complete!"
|
|
echo "=========================================="
|
|
echo ""
|
|
echo "Verification:"
|
|
echo " dig @$PUBLIC_IP superviber.com NS"
|
|
echo " dig @$PUBLIC_IP git.beyondtheuniverse.superviber.com A"
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo "1. Update GoDaddy glue records for each domain:"
|
|
echo " - ns1.<domain> -> $PUBLIC_IP"
|
|
echo " - ns2.<domain> -> $PUBLIC_IP"
|
|
echo ""
|
|
echo "2. Update nameservers at GoDaddy:"
|
|
echo " - ns1.<domain>"
|
|
echo " - ns2.<domain>"
|
|
echo ""
|
|
echo "3. Wait for DNS propagation (up to 48 hours)"
|
|
echo "=========================================="
|