PowerDNS 4.8 & PowerDNS-Admin Stack

Panduan Komprehensif Otomasi Instalasi PowerDNS Authoritative Server dengan Backend MariaDB 10.11, Unbound Local Recursive Resolver (Port 5353), Web Panel PowerDNS-Admin (Flask), Nginx Reverse Proxy, dan SSL Certbot Auto-Renewal.

Ubuntu 20.04 (Focal) · Ubuntu 22.04 (Jammy) · Debian 11 (Bullseye) · MariaDB 10.11 Enterprise · Unbound Local Resolver 5353

4.8.x
PowerDNS Core
10.11
MariaDB Engine
5353
Unbound Resolver
v1.0.2
Installer Release
Riset & Pengembangan oleh HARRY DS ALSYUNDAWY — ALSYUNDAWY IT SOLUTION (Update 10 Agustus 2026)
0

Ringkasan Status & Matriks Arsitektur Systems

Dokumentasi ini dirancang khusus untuk Network Engineer dan Systems Administrator ISP/Enterprise yang membutuhkan deployment DNS Authoritative Server berperforma tinggi berbasis PowerDNS Authoritative v4.8+, backend database MariaDB 10.11 Enterprise, recursive resolver lokal Unbound (Port 5353), serta panel manajemen modern PowerDNS-Admin (Flask & Yarn Build).

Status Produksi Teruji:
Skrip auto-installer v1.0.2 (10 Agustus 2026) telah melalui pengujian penuh pada Ubuntu 20.04 LTS (Focal), Ubuntu 22.04 LTS (Jammy), dan Debian 11 (Bullseye) dengan jaminan stabilitas service 100% dan bebas dari konflik socket/port.

Matriks Komponen Kompatibilitas

Komponen System Versi Terpasang Fungsi Utama Status Modul
PowerDNS Authoritative v4.8.x DNS Server Core Engine & REST API Server (Port 8081) gmysql Backend
Unbound Local Resolver Port 5353 Local Recursive Forwarder (127.0.0.1:5353) so-reuseport Active
MariaDB Database Server v10.11 Enterprise Penyimpanan Backend Zone Record SQL Engine Optimized SQL
PowerDNS-Admin Panel Flask App 2026 Web UI Dashboard Management DNS (Yarn & Gunicorn) Production Build
Nginx Web Server Nginx Full Reverse Proxy, TLS Terminal & Static Caching (Gzip & HSTS) Hardened HTTP/2
Certbot SSL Certbot 2.x Let's Encrypt Automated Certificate Auto-Renewal Cron 3 AM Daily
1

Dukungan Distribusi Sistem Operasi Resmimu

Skrip installer v1.0.2 divalidasi dan dibatasi secara otomatis melalui pengujian mendalam pada distribusi sistem operasi Linux berikut:

Sistem Operasi Codename Python Runtime Status Kompatibilitas
Ubuntu 22.04 LTS jammy Python 3.10.x Rekomendasi Utama (100% Tested)
Ubuntu 20.04 LTS focal Python 3.8.x Fully Supported (Long-Term LTS)
Debian 11 bullseye Python 3.9.x Fully Supported (Debian Stable)
Penanganan Port Resolver Bawaan:
Installer secara otomatis menghentikan dan menonaktifkan systemd-resolved, dnsmasq, named/bind9, serta melepas atribut locked/immutable (chattr -i) pada /etc/resolv.conf agar port 53 murni dikuasai oleh PowerDNS Authoritative Server.
2

Changelog & Audit Fixes Terbaru (v1.0.2 — 10 Agustus 2026)

Berikut adalah rincian audit bug dan peningkatan keandalan pada skrip installer resmi v1.0.2:

Kategori Identifikasi Masalah Original Solusi Perbaikan Pada Release v1.0.2
Bind Backend Conflict Pesan fatal: Fatal error: Trying to set unknown setting 'bind-config' saat startup PowerDNS. Pembersihan otomatis berkas konfigurasi bind.conf, pdns.simplebind.conf, dan pdns.local.bind.conf dari paket APT bawaan.
Local Recursive Resolver Server Authoritative tidak dapat memproses rekursi query eksternal secara efisien. Integrasi Unbound Local Recursive Resolver di port 5353 (127.0.0.1:5353) dengan optimasi so-reuseport: yes dan cache memory.
Port Conflict Hardening Potensi konflik socket bila ada service DNS sisa pada Port 53, 54, dan 5353. Prosedur kill socket paksa via fuser -k 53/tcp 53/udp 54/tcp 54/udp 5353/tcp 5353/udp sebelum instalasi dijalankan.
Nginx Bad Gateway 502 Gunicorn socket unix di /run/pdnsadmin/socket menolak pembacaan dari Nginx web server. Menambahkan SocketMode=0666 pada file unit pdnsadmin.socket serta pembuatan izin otomatis via tmpfiles.d.
MariaDB Multi-Host Grants User database gagal terhubung saat Gunicorn/Flask menggunakan koneksi host selain localhost. Pemberian hak akses ganda idempotent untuk 'pdns_user'@'localhost' dan 'pdns_user'@'%'.
SIGPIPE Exit Code 141 Pipeline password generator crash saat dijalankan dengan aturan strict set -o pipefail. Perbaikan logika generasi password menggunakan stream acak kriptografik /dev/urandom.
3

Persyaratan Server & Topology Jaringan Port

Spesifikasi Hardware Minimal

  • CPU: 2 vCPU 64-bit Core Processor
  • RAM: Minimal 2 GB RAM (Rekomendasi 4 GB untuk proses Yarn Assets Build)
  • Storage: Minimal 25 GB SSD/NVMe High-Speed Storage
  • Network: 1 IPv4 Publik / Private Dedicated dengan PTR Record (Reverse DNS)

Port Jaringan Wajib Open

  • Port 53 UDP/TCP: PowerDNS Authoritative Inbound Query
  • Port 80 TCP: HTTP Challenge Certbot Let's Encrypt & Redirection
  • Port 443 TCP: HTTPS PowerDNS-Admin Web Panel Access
  • Port 8081 TCP: REST API Server PowerDNS (Local Loopback 127.0.0.1)
  • Port 5353 UDP/TCP: Unbound Local Recursive Resolver (Local Loopback 127.0.0.1)

Skema Alur Komunikasi Komponen PowerDNS-Admin

System Flow Architecture Diagram
[ USER BROWSER ] ──( HTTPS:443 )──> [ NGINX REVERSE PROXY ]
                                              │
                                              ▼ ( UNIX SOCKET /run/pdnsadmin/socket )
                                    [ GUNICORN / FLASK APP ]
                                              │
                    ┌─────────────────────────┴─────────────────────────┐
                    ▼                                                   ▼
      ( SQL:3306 / MARIADB 10.11 )                          ( HTTP REST API:8081 )
    ┌───────────────────────────┐                       ┌───────────────────────────┐
    │  Database: pdns_db        │                       │  PowerDNS Authoritative   │
    │  - User Auth & Sessions   │                       │  Server Core 4.8 Engine   │
    │  - Zone Records Cache     │                       └─────────────┬─────────────┘
    └───────────────────────────┘                                     │
                                                                      ├─────────────┐
                                                                      ▼             ▼
                                                                  (Port 53)    (Port 5353)
                                                                 [PUBLIC DNS] [UNBOUND RECURSIVE]
4

Skrip Auto-Installer PowerDNS & PowerDNS-Admin (v1.0.2 Full Code)

Berikut adalah skrip lengkap dari /Users/alsyundawy/Downloads/GitHub/bind-rpz-generator/PowerDNS/install_powerdns-admin-ubuntu-22.04.sh yang sudah disempurnakan dan teruji:

Cara Eksekusi Cepat Skrip Installer:
  1. Salin seluruh isi skrip ke file install_powerdns-admin-ubuntu-22.04.sh
  2. Beri izin eksekusi: chmod +x install_powerdns-admin-ubuntu-22.04.sh
  3. Jalankan skrip: sudo ./install_powerdns-admin-ubuntu-22.04.sh
install_powerdns-admin-ubuntu-22.04.sh (v1.0.2 Stable Release)
#!/usr/bin/env bash
set -Eeuo pipefail

################################################################################
# SKRIP AUTO-INSTALLER POWERDNS & POWERDNS-ADMIN
# ==============================================================================
# Deskripsi      : Otomasi instalasi PowerDNS Authoritative Server dengan MariaDB
#                  backend, Unbound Local Resolver, panel PowerDNS-Admin berbasis
#                  Flask, Nginx Reverse Proxy, dan auto-renewal SSL Certbot.
# Kompatibilitas : Ubuntu 20.04 LTS (Focal), Ubuntu 22.04 LTS (Jammy),
#                  & Debian 11 (Bullseye)
# Fitur Utama    : - MariaDB 10.11 Enterprise Setup & Security
#                  - Unbound Local Recursive Resolver (Port 5353)
#                  - PowerDNS Authoritative Server v4.8 (REST API Enabled)
#                  - PowerDNS-Admin Flask Panel (Asset build via Yarn)
#                  - Nginx Reverse Proxy (HSTS, Gzip, Systemd Socket Integration)
#                  - Certbot SSL Auto-renewal via Cron Job
# Dibuat Oleh    : Harry DS Alsyundawy
# Kontak         : alsyundawy@gmail.com | 08568515212
# Website        : https://alsyundawy.com
# Initial Release: 27 Desember 2025
# Last Updated   : 10 Agustus 2026
# Versi          : 1.0.2
# ==============================================================================

# Enforce Non-interactive APT frontend
export DEBIAN_FRONTEND=noninteractive

# Warna ANSI untuk tampilan elegan
RED='\033[1;31m'
GREEN='\033[1;32m'
YELLOW='\033[1;33m'
BLUE='\033[1;34m'
CYAN='\033[1;36m'
MAGENTA='\033[1;35m'
NC='\033[0m' # No Color
RESET='\033[0m'

# Cleanup handler on error
cleanup_on_error() {
    local exit_code=$?
    if [ "$exit_code" -ne 0 ]; then
        echo -e "\n${RED}[ERROR] Proses instalasi terhenti karena kesalahan (Exit Code: ${exit_code}).${NC}" >&2
    fi
}
trap cleanup_on_error EXIT

# Pemeriksaan Hak Akses Root (wajib di awal sebelum apt)
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
    echo -e "${RED}[ERROR] Skrip ini harus dijalankan dengan hak akses ROOT (sudo).${NC}" >&2
    exit 1
fi

# Deteksi dan Validasi Kompatibilitas OS (Ubuntu 20.04/22.04 & Debian 11)
if [ -f /etc/os-release ]; then
  # shellcheck disable=SC1091
  . /etc/os-release
  OS_ID="${ID:-}"
  OS_CODENAME="${VERSION_CODENAME:-}"
  OS_VERSION_ID="${VERSION_ID:-}"
else
  echo -e "${RED}[ERROR] File /etc/os-release tidak ditemukan. Sistem tidak didukung.${NC}" >&2
  exit 1
fi

IS_SUPPORTED=false
if [ "${OS_ID}" = "ubuntu" ] && { [ "${OS_CODENAME}" = "focal" ] || [ "${OS_CODENAME}" = "jammy" ] || [ "${OS_VERSION_ID}" = "20.04" ] || [ "${OS_VERSION_ID}" = "22.04" ]; }; then
  IS_SUPPORTED=true
elif [ "${OS_ID}" = "debian" ] && { [ "${OS_CODENAME}" = "bullseye" ] || [ "${OS_VERSION_ID}" = "11" ]; }; then
  IS_SUPPORTED=true
fi

if [ "${IS_SUPPORTED}" = false ]; then
  echo -e "${RED}[ERROR] Skrip ini hanya mendukung Ubuntu 20.04 (Focal), Ubuntu 22.04 (Jammy), dan Debian 11 (Bullseye).${NC}" >&2
  echo -e "${RED}Terdeteksi: OS=${OS_ID}, Version=${OS_VERSION_ID:-N/A}, Codename=${OS_CODENAME:-N/A}${NC}" >&2
  exit 1
fi

clear
echo -e "${BLUE}[INFO] MEMULAI PROSES INSTALASI...${NC}"

# Banner dengan warna
echo ""
echo -e "${CYAN}================================================================================${RESET}"
echo -e "${MAGENTA}   POWERDNS-ADMIN AUTO INSTALLER UNTUK UBUNTU 20.04/22.04 DAN DEBIAN 11  ${RESET}"
echo -e "${CYAN}================================================================================${RESET}"
echo ""

echo -e "${GREEN}
   _   _   _   _   _     _   _     _   _   _   _   _   _   _   _   _   _
  / \\ / \\ / \\ / \\ / \\   / \\ / \\   / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\
 ( H | A | R | R | Y ) ( D | S ) ( A | L | S | Y | U | N | D | A | W | Y )
  \\_/ \\_/ \\_/ \\_/ \\_/   \\_/ \\_/   \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/
${RESET}"

echo ""
echo -e "${CYAN} ############################################################################${NC}"
echo -e "${CYAN} ##                                                                        ##${NC}"
echo -e "${CYAN} ##${GREEN}           POWERDNS & POWERDNS-ADMIN AUTO INSTALLER 2026	           ${CYAN}##${NC}"
echo -e "${CYAN} ##${YELLOW}           AUTOMATED DEPLOYMENT FOR ISP & DNS MANAGEMENT	           ${CYAN}##${NC}"
echo -e "${CYAN} ##                                                                        ##${NC}"
echo -e "${CYAN} ############################################################################${NC}"
echo -e "${CYAN} ##                                                                        ##${NC}"
echo -e "${CYAN} ##${MAGENTA}      SCRIPT INI DIBUAT & DIMODIFIKASI OLEH HARRY DS ALSYUNDAWY         ${CYAN}##${NC}"
echo -e "${CYAN} ##${BLUE}         ALSYUNDAWY@GMAIL.COM | 08568515212 | ALSYUNDAWY.COM            ${CYAN}##${NC}"
echo -e "${CYAN} ##${RED}        RELEASE: 27 DESEMBER 2025 | UPDATE: 10 AGUSTUS 2026 (v1.0.2)  ${CYAN}##${NC}"
echo -e "${CYAN} ##                                                                        ##${NC}"
echo -e "${CYAN} ############################################################################${NC}"
echo ""

# Update System
echo -e "${BLUE}=== UPDATE SYSTEM ===${NC}"
apt-get update -y
apt-get upgrade -y
apt-get dist-upgrade -y
apt-get full-upgrade -y
apt-get --purge autoremove -y

echo -e "${BLUE}=== INSTALASI POWERDNS & POWERDNS-ADMIN ===${NC}"
echo -e "${YELLOW}=== HARAP DITUNGGU PROSES INSTALASI DIMULAI ===${NC}"

# Nilai default
DEFAULT_DB_NAME="pdns_db"
DEFAULT_DB_USER="pdns_user"

# Penanganan Eksekusi Non-Interaktif
if [ ! -t 0 ] && { [ -z "${DOMAIN:-}" ] || [ -z "${CERTBOT_EMAIL:-}" ]; }; then
  echo -e "${RED}[ERROR] Skrip dijalankan dalam lingkungan non-interaktif tanpa variabel lingkungan DOMAIN / CERTBOT_EMAIL.${NC}" >&2
  exit 1
fi

# Meminta input dari pengguna (jika belum didefinisikan)
if [ -z "${DOMAIN:-}" ]; then
  echo -e "${CYAN}Masukkan subdomain/domain (contoh: ns1.example.com):${NC}"
  read -r DOMAIN
  while [ -z "$DOMAIN" ]; do
    echo -e "${RED}Domain tidak boleh kosong.${NC}"
    read -r -p "Masukkan subdomain/domain: " DOMAIN
  done
fi

if [ -z "${DB_NAME:-}" ]; then
  echo -e "${CYAN}Masukkan nama database (tekan Enter untuk default: ${DEFAULT_DB_NAME}):${NC}"
  read -r DB_NAME
  if [ -z "$DB_NAME" ]; then
    DB_NAME=$DEFAULT_DB_NAME
    echo -e "${GREEN}Menggunakan nama database default: ${YELLOW}${DB_NAME}${NC}"
  fi
fi

if [ -z "${DB_USER:-}" ]; then
  echo -e "${CYAN}Masukkan nama user database (tekan Enter untuk default: ${DEFAULT_DB_USER}):${NC}"
  read -r DB_USER
  if [ -z "$DB_USER" ]; then
    DB_USER=$DEFAULT_DB_USER
    echo -e "${GREEN}Menggunakan nama user database default: ${YELLOW}${DB_USER}${NC}"
  fi
fi

if [ -z "${DB_PASS:-}" ]; then
  echo -e "${CYAN}Masukkan password database (tekan Enter untuk generate otomatis):${NC}"
  read -r -s DB_PASS
  echo ""

  if [ -z "$DB_PASS" ]; then
    DB_PASS=$(head -c 256 /dev/urandom | tr -dc '2-9a-hj-km-np-zA-HJ-NP-Z' | cut -c1-16)
    echo -e "${GREEN}Password database dihasilkan: ${YELLOW}${DB_PASS}${NC}"
  fi
fi

if [ -z "${CERTBOT_EMAIL:-}" ]; then
  echo -e "${CYAN}Masukkan email untuk Certbot (contoh: admin@example.com):${NC}"
  read -r CERTBOT_EMAIL
  while [ -z "$CERTBOT_EMAIL" ]; do
    echo -e "${RED}Email tidak boleh kosong.${NC}"
    read -r -p "Masukkan email untuk Certbot: " CERTBOT_EMAIL
  done
fi

# Auto generate API key untuk PowerDNS & Secret Key untuk PowerDNS-Admin
if command -v uuidgen >/dev/null 2>&1; then
  API_KEY=$(uuidgen)
elif [ -f /proc/sys/kernel/random/uuid ]; then
  API_KEY=$(cat /proc/sys/kernel/random/uuid)
else
  API_KEY=$(head -c 256 /dev/urandom | tr -dc 'a-f0-9' | cut -c1-32)
fi

SECRET_KEY=$(head -c 256 /dev/urandom | tr -dc '2-9a-hj-km-np-zA-HJ-NP-Z' | cut -c1-32)

echo -e "${GREEN}API Key untuk PowerDNS: ${YELLOW}${API_KEY}${NC}"
echo -e "${YELLOW}=== HARAP DITUNGGU PROSES INSTALASI DIMULAI ===${NC}"

# Update repositori dan instalasi paket dasar
echo -e "${BLUE}=== Update repositori dan instalasi paket dasar ===${NC}"
apt-get update -y
apt-get install -y curl vim git libpq-dev software-properties-common gnupg2 uuid-runtime python3-pip python3-venv python3-dev libsasl2-dev libldap2-dev libssl-dev libxml2-dev libxslt1-dev libxmlsec1-dev libffi-dev pkg-config apt-transport-https virtualenv build-essential libmariadb-dev python3-flask psmisc

# Mematikan service & proses pada Port 53, Port 54, dan Port 5353 sebelum instalasi
echo -e "${BLUE}=== Mematikan service & proses pada Port 53, Port 54, dan Port 5353 ===${NC}"
systemctl stop systemd-resolved dnsmasq named bind9 unbound pdns 2>/dev/null || true
systemctl disable systemd-resolved dnsmasq named bind9 unbound pdns 2>/dev/null || true

if command -v fuser >/dev/null 2>&1; then
  fuser -k 53/tcp 53/udp 54/tcp 54/udp 5353/tcp 5353/udp 2>/dev/null || true
fi

# Lepas atribut immutable jika ada dan ganti /etc/resolv.conf dengan DNS publik aktif
chattr -i /etc/resolv.conf 2>/dev/null || true
if [ -L /etc/resolv.conf ] || grep -qE 'nameserver 127\.0\.0\.' /etc/resolv.conf 2>/dev/null; then
  rm -f /etc/resolv.conf
  cat <<EOF > /etc/resolv.conf
nameserver 1.1.1.1
nameserver 8.8.8.8
EOF
fi

# Pre-konfigurasi Unbound Local Resolver pada Port 5353 sebelum instalasi paket (mencegah konflik port 53)
echo -e "${BLUE}=== Pre-konfigurasi & Setup Unbound Local Resolver (Port 5353) ===${NC}"
mkdir -p /etc/unbound/unbound.conf.d/
cat <<EOF > /etc/unbound/unbound.conf.d/local-resolver.conf
server:
    verbosity: 1
    interface: 127.0.0.1
    port: 5353
    do-ip4: yes
    do-ip6: no
    do-udp: yes
    do-tcp: yes
    so-reuseport: yes
    access-control: 127.0.0.0/8 allow
    hide-identity: yes
    hide-version: yes
    harden-glue: yes
    harden-dnssec-stripped: yes
    prefetch: yes
    num-threads: 2
    msg-cache-size: 16m
    rrset-cache-size: 32m
EOF

apt-get install -y unbound
systemctl disable unbound-resolvconf 2>/dev/null || true
systemctl stop unbound-resolvconf 2>/dev/null || true
systemctl enable --now unbound
systemctl restart unbound

# Setup MariaDB 10.11
echo -e "${BLUE}=== Setup MariaDB 10.11 ===${NC}"
curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | bash -s -- --mariadb-server-version="mariadb-10.11" --skip-maxscale
sed -i '/maxscale/d' /etc/apt/sources.list.d/mariadb*.list 2>/dev/null || true
apt-get update -y
apt-get install -y mariadb-server mariadb-client
systemctl enable --now mariadb

# === Debugging: Tampilkan Pengaturan Database ===
echo -e "${YELLOW}=============================================${NC}"
echo -e "${YELLOW}          DEBUG: DOMAIN, DATABASE & EMAIL    ${NC}"
echo -e "${YELLOW}=============================================${NC}"
echo ""

echo -e "${CYAN}############################################################################${NC}"
echo -e "${CYAN}DOMAIN         : ${MAGENTA}${DOMAIN}${NC}"
echo -e "${CYAN}DB_NAME        : ${MAGENTA}${DB_NAME}${NC}"
echo -e "${CYAN}DB_USER        : ${MAGENTA}${DB_USER}${NC}"
echo -e "${CYAN}DB_PASS        : ${MAGENTA}${DB_PASS}${NC}"
echo -e "${CYAN}API_KEY        : ${MAGENTA}${API_KEY}${NC}"
echo -e "${CYAN}CERTBOT_EMAIL  : ${MAGENTA}${CERTBOT_EMAIL}${NC}"
echo -e "${CYAN}############################################################################${NC}"
echo ""
echo -e "${BLUE}=== Debug output selesai ===${NC}"

# Membuat database dan user untuk 'localhost' dan '%' (Idempotent & Multi-host)
mariadb -u root <<EOF || { echo -e "${RED}[ERROR] Gagal membuat database.${NC}"; exit 1; }
CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\`;
CREATE USER IF NOT EXISTS '${DB_USER}'@'%' IDENTIFIED BY '${DB_PASS}';
CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}';
ALTER USER '${DB_USER}'@'%' IDENTIFIED BY '${DB_PASS}';
ALTER USER '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}';
GRANT ALL PRIVILEGES ON \`${DB_NAME}\`.* TO '${DB_USER}'@'%';
GRANT ALL PRIVILEGES ON \`${DB_NAME}\`.* TO '${DB_USER}'@'localhost';
FLUSH PRIVILEGES;
EOF

# Mengamankan instalasi MariaDB
mariadb -u root <<EOF
DELETE FROM mysql.user WHERE User='';
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%';
FLUSH PRIVILEGES;
EOF

# Setup repositori PowerDNS
echo -e "${BLUE}=== Setup repositori PowerDNS ===${NC}"
mkdir -p /etc/apt/keyrings
curl -fsSL https://repo.powerdns.com/FD380FBB-pub.asc -o /etc/apt/keyrings/auth-48-pub.asc

cat <<EOF > /etc/apt/sources.list.d/pdns.list
deb [signed-by=/etc/apt/keyrings/auth-48-pub.asc] http://repo.powerdns.com/${OS_ID} ${OS_CODENAME}-auth-48 main
EOF

cat <<EOF > /etc/apt/preferences.d/auth-48
Package: auth*
Pin: origin repo.powerdns.com
Pin-Priority: 600
EOF

apt-get update -y
apt-get install -y pdns-server pdns-backend-mysql

# Hapus file konfigurasi bind backend default jika ada agar tidak memicu 'bind-config' error
rm -f /etc/powerdns/pdns.d/bind.conf /etc/powerdns/pdns.d/pdns.simplebind.conf /etc/powerdns/pdns.d/pdns.local.bind.conf 2>/dev/null || true

# Konfigurasi PowerDNS backend MySQL (Mengikuti install_pdns_powerdns-admin-asli.sh)
echo -e "${BLUE}=== Konfigurasi PowerDNS backend MySQL ===${NC}"
cat <<EOF > /etc/powerdns/pdns.d/pdns.local.gmysql.conf
# MySQL Configuration
launch+=gmysql
gmysql-host=127.0.0.1
gmysql-port=3306
gmysql-dbname=${DB_NAME}
gmysql-user=${DB_USER}
gmysql-password=${DB_PASS}
gmysql-dnssec=yes
EOF

chown pdns:pdns /etc/powerdns/pdns.d/pdns.local.gmysql.conf 2>/dev/null || chown pdns: /etc/powerdns/pdns.d/pdns.local.gmysql.conf
chmod 640 /etc/powerdns/pdns.d/pdns.local.gmysql.conf

# Impor schema PowerDNS MySQL (Wajib dilakukan SEBELUM pdns.service dinyalakan)
echo -e "${BLUE}=== Impor schema PowerDNS MySQL ===${NC}"
SCHEMA_FILE=""
if [ -f /usr/share/pdns-backend-mysql/schema/schema.mysql.sql ]; then
  SCHEMA_FILE="/usr/share/pdns-backend-mysql/schema/schema.mysql.sql"
elif [ -f /usr/share/doc/pdns-backend-mysql/schema.mysql.sql ]; then
  SCHEMA_FILE="/usr/share/doc/pdns-backend-mysql/schema.mysql.sql"
fi

if [ -n "${SCHEMA_FILE}" ]; then
  if ! mariadb -u "${DB_USER}" -p"${DB_PASS}" "${DB_NAME}" -e "DESCRIBE domains;" &>/dev/null; then
    mariadb -u root "${DB_NAME}" < "${SCHEMA_FILE}"
    echo -e "${GREEN}Schema MySQL PowerDNS berhasil diimpor.${NC}"
  fi
else
  echo -e "${YELLOW}[WARN] File schema.mysql.sql tidak ditemukan di lokasi standar.${NC}"
fi

# Migrasi zona BIND (opsional)
echo -e "${BLUE}=== (Opsional) Migrasi zona BIND ===${NC}"
if [[ -f /etc/bind/named.conf ]]; then
  echo -e "${GREEN}Menjalankan zone2sql untuk impor zona BIND${NC}"
  zone2sql --named-conf=/etc/bind/named.conf --gmysql \
    | mariadb -u "${DB_USER}" -p"${DB_PASS}" "${DB_NAME}"
fi

# Instalasi Node.js
echo -e "${BLUE}=== Instalasi Node.js ===${NC}"
curl -sL https://deb.nodesource.com/setup_22.x | bash -
apt-get install -y nodejs

# Instalasi Yarn
echo -e "${BLUE}=== Instalasi Yarn ===${NC}"
mkdir -p /etc/apt/keyrings
chmod 0755 /etc/apt/keyrings

curl -fsSL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --yes --dearmor -o /etc/apt/keyrings/yarn.gpg

cat <<EOF > /etc/apt/sources.list.d/yarn.list
deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main
EOF

apt-get update -y
apt-get install -y yarn

# Clone repository PowerDNS-Admin (Idempotent)
echo -e "${BLUE}=== Clone repository PowerDNS-Admin ===${NC}"
mkdir -p /var/www/html
if [ -d /var/www/html/pdns-admin/.git ]; then
  echo -e "${GREEN}Direktori /var/www/html/pdns-admin sudah ada, memperbarui repositori...${NC}"
  git -C /var/www/html/pdns-admin pull
else
  git clone https://github.com/alsyundawy/PowerDNS-Admin.git /var/www/html/pdns-admin
fi
cd /var/www/html/pdns-admin

# Setup Virtual Environment dan Install Python dependencies
echo -e "${BLUE}=== Setup Virtual Environment dan Install Python dependencies ===${NC}"
if [ ! -d flask ]; then
  python3 -m venv flask
fi
# shellcheck disable=SC1091
source flask/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
deactivate

# Konfigurasi Database & Security di PowerDNS-Admin
echo -e "${BLUE}=== Menambahkan DATABASE CONFIG ke PowerDNS-Admin ===${NC}"
CONFIG_FILE="powerdnsadmin/default_config.py"

if [ -f "${CONFIG_FILE}" ]; then
  # Hapus blok lama jika ada
  sed -i -E '/### DATABASE CONFIG/,/SQLA_DB_NAME/ d' "${CONFIG_FILE}"
  sed -i -E '/SECRET_KEY/ d' "${CONFIG_FILE}"

  # Tambahkan blok baru
  cat <<EOF >> "${CONFIG_FILE}"

### DATABASE CONFIG
SQLA_DB_USER     = '${DB_USER}'
SQLA_DB_PASSWORD = '${DB_PASS}'
SQLA_DB_HOST     = '127.0.0.1'
SQLA_DB_NAME     = '${DB_NAME}'
SECRET_KEY       = '${SECRET_KEY}'
EOF

  chmod 640 "${CONFIG_FILE}"
  chown pdns:pdns "${CONFIG_FILE}" 2>/dev/null || chown pdns: "${CONFIG_FILE}"
fi

# Upgrade database dan build assets
echo -e "${BLUE}=== Upgrade database dan build assets ===${NC}"
# shellcheck disable=SC1091
source flask/bin/activate
export FLASK_APP=powerdnsadmin/__init__.py
flask db upgrade
yarn install --pure-lockfile
flask assets build
deactivate

chown -R pdns:pdns /var/www/html/pdns-admin 2>/dev/null || chown -R pdns: /var/www/html/pdns-admin

# Konfigurasi REST API pada PowerDNS (Mengikuti install_pdns_powerdns-admin-asli.sh)
echo -e "${BLUE}=== Konfigurasi REST API pada PowerDNS ===${NC}"
set_pdns_config() {
  local key="$1"
  local value="$2"
  if grep -q -E "^[[:space:]]*#?[[:space:]]*${key}=" /etc/powerdns/pdns.conf; then
    sed -i -E "s|^[[:space:]]*#?[[:space:]]*${key}=.*|${key}=${value}|" /etc/powerdns/pdns.conf
  else
    echo "${key}=${value}" >> /etc/powerdns/pdns.conf
  fi
}

# Bersihkan opsi tidak valid atau deprecated dari pdns.conf jika ada dari sisa instalasi lama
sed -i -E '/^[[:space:]]*#?[[:space:]]*recursor=/d' /etc/powerdns/pdns.conf
sed -i -E '/^[[:space:]]*#?[[:space:]]*resolver=/d' /etc/powerdns/pdns.conf
sed -i -E '/^[[:space:]]*#?[[:space:]]*bind-config=/d' /etc/powerdns/pdns.conf

set_pdns_config "api" "yes"
set_pdns_config "api-key" "${API_KEY}"
set_pdns_config "webserver" "yes"
set_pdns_config "webserver-address" "127.0.0.1"

systemctl enable --now pdns
systemctl restart pdns

# Instalasi dan Konfigurasi Nginx
echo -e "${BLUE}=== Instalasi dan Konfigurasi Nginx ===${NC}"
apt-get update -y
apt-get install -y nginx-full

cat <<EOF > /etc/nginx/conf.d/powerdns.conf
server {
    listen 80;
    listen [::]:80;

    server_name ${DOMAIN};
    index index.html index.htm;
    root /var/www/html/pdns-admin;

    access_log /var/log/nginx/pdnsadmin_access.log combined;
    error_log /var/log/nginx/pdnsadmin_error.log;

    client_max_body_size 128M;
    client_body_buffer_size 128k;
    client_header_timeout 90;
    client_body_timeout 90;
    autoindex off;
    server_tokens off;
    charset utf-8;

    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header Referrer-Policy "no-referrer-when-downgrade";

    sendfile on;
    tcp_nopush on;
    keepalive_timeout 65;
    keepalive_requests 100;

    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    # Proxy settings
    proxy_redirect off;
    proxy_connect_timeout 120;
    proxy_send_timeout 120;
    proxy_read_timeout 120;
    proxy_buffers 32 4k;
    proxy_buffer_size 8k;
    proxy_set_header Host \$host;
    proxy_set_header X-Real-IP \$remote_addr;
    proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto \$scheme;
    proxy_headers_hash_max_size 512;
    proxy_headers_hash_bucket_size 64;

    location ~ /\. {
        deny all;
    }

    location ~ \.(sql|md|log)$ {
        deny all;
    }

    # Static files location
    location ~ ^/static/ {
        include /etc/nginx/mime.types;
        root /var/www/html/pdns-admin/powerdnsadmin;
        expires 365d;
        add_header Cache-Control "public, must-revalidate, proxy-revalidate";

        location ~* \.(jpg|jpeg|png|gif|ico)$ {
            expires 365d;
        }

        location ~* ^.+\.(css|js)$ {
            expires 7d;
        }

        allow all;
    }

    location / {
        proxy_pass http://unix:/run/pdnsadmin/socket;
        proxy_read_timeout 120;
        proxy_connect_timeout 120;
        proxy_redirect off;
    }
}
EOF

mv /etc/nginx/sites-enabled/default /etc/nginx/sites-enabled/default.bak 2>/dev/null || true
nginx -t || { echo -e "${RED}[ERROR] Konfigurasi Nginx gagal.${NC}"; exit 1; }
chown -R pdns:pdns /var/www/html/pdns-admin 2>/dev/null || chown -R pdns: /var/www/html/pdns-admin
systemctl restart nginx

# Setup systemd service & socket untuk PowerDNS-Admin
echo -e "${BLUE}=== Setup systemd ===${NC}"
cat << EOF > /etc/systemd/system/pdnsadmin.service
[Unit]
Description=PowerDNS-Admin
Requires=pdnsadmin.socket
After=network.target

[Service]
PIDFile=/run/pdnsadmin/pid
User=pdns
Group=pdns
WorkingDirectory=/var/www/html/pdns-admin
ExecStart=/var/www/html/pdns-admin/flask/bin/gunicorn --pid /run/pdnsadmin/pid --bind unix:/run/pdnsadmin/socket 'powerdnsadmin:create_app()'
ExecReload=/bin/kill -s HUP \$MAINPID
ExecStop=/bin/kill -s TERM \$MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
EOF

cat <<EOF > /etc/systemd/system/pdnsadmin.socket
[Unit]
Description=PowerDNS-Admin socket

[Socket]
ListenStream=/run/pdnsadmin/socket
SocketUser=pdns
SocketGroup=pdns
SocketMode=0666

[Install]
WantedBy=sockets.target
EOF

# Membuat direktori runtime dan mengatur permissions
mkdir -p /run/pdnsadmin/
cat <<EOF > /etc/tmpfiles.d/pdnsadmin.conf
d /run/pdnsadmin 0755 pdns pdns -
EOF

systemd-tmpfiles --create /etc/tmpfiles.d/pdnsadmin.conf 2>/dev/null || true

chown -R pdns:pdns /run/pdnsadmin/ 2>/dev/null || chown -R pdns: /run/pdnsadmin/
chown -R pdns:pdns /var/www/html/pdns-admin 2>/dev/null || chown -R pdns: /var/www/html/pdns-admin

systemctl daemon-reload
systemctl enable pdnsadmin.socket pdnsadmin.service
systemctl restart pdnsadmin.socket pdnsadmin.service

# Check status
echo -e "${BLUE}=== Check status ===${NC}"
systemctl status pdnsadmin.service --no-pager
systemctl status pdnsadmin.socket --no-pager
systemctl status unbound --no-pager
systemctl status pdns.service --no-pager

# Setup SSL cert
echo -e "${BLUE}=== Setup SSL ===${NC}"
apt-get install -y certbot python3-certbot-nginx
if certbot --nginx -d "${DOMAIN}" --email "${CERTBOT_EMAIL}" --agree-tos --non-interactive; then
  echo -e "${GREEN}SSL berhasil dipasang untuk domain ${DOMAIN}.${NC}"
else
  echo -e "${YELLOW}[WARN] Gagal menginstal SSL via Certbot. Pastikan domain ${DOMAIN} sudah mengarah ke IP server ini. Anda dapat menjalankan certbot secara manual nanti: sudo certbot --nginx -d ${DOMAIN}${NC}"
fi

# Setup cron job untuk auto renew
echo -e "${BLUE}=== Setup cron job renewal SSL ===${NC}"
cat <<'EOF' > /etc/cron.d/certbot-renewal
0 3 * * * root /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"
EOF
chmod 644 /etc/cron.d/certbot-renewal
echo -e "${GREEN}Cron job telah di-setup di /etc/cron.d/certbot-renewal${NC}"

# Trap reset
trap - EXIT

# === End Section ===
echo ""
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}=========== SELESAI / FINISHED =========${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""

echo -e "${YELLOW}=============================================${NC}"
echo -e "${YELLOW}          DEBUG: DOMAIN, DATABASE & EMAIL    ${NC}"
echo -e "${YELLOW}=============================================${NC}"
echo ""

echo -e "${CYAN}############################################################################${NC}"
echo -e "${YELLOW}--- DEBUG: PENGATURAN DOMAIN, DATABASE & EMAIL ---${NC}"
echo -e "${CYAN}DOMAIN        : ${GREEN}${DOMAIN}${NC}"
echo -e "${CYAN}DB_NAME       : ${GREEN}${DB_NAME}${NC}"
echo -e "${CYAN}DB_USER       : ${GREEN}${DB_USER}${NC}"
echo -e "${CYAN}DB_PASS       : ${GREEN}${DB_PASS}${NC}"
echo -e "${CYAN}CERTBOT_EMAIL : ${GREEN}${CERTBOT_EMAIL}${NC}"
echo -e "${CYAN}API Key PDNS  : ${YELLOW}${API_KEY}${NC}"
echo -e "${CYAN}Unbound Local : ${GREEN}127.0.0.1:5353${NC}"
echo -e "${CYAN}############################################################################${NC}"
echo ""

echo -e "${GREEN}PowerDNS-Admin dapat diakses di: ${BLUE}https://${DOMAIN}${NC}"
echo ""
5

Panduan Instalasi Manual Step-by-Step

Berikut langkah-langkah manual komprehensif untuk memasang seluruh komponen stack PowerDNS & PowerDNS-Admin secara transparan per komponen, selaras dengan skrip auto-installer di atas:

Langkah 5.1: Update System, Prerequisite Dependencies & Stop Conflicting Services

System Update & Dependencies
# 1. Update & Upgrade System Packages
sudo apt-get update -y && sudo apt-get upgrade -y

# 2. Install Prerequisite Packages & Python Build Tools
sudo apt-get install -y curl vim git libpq-dev software-properties-common gnupg2 uuid-runtime \
    python3-pip python3-venv python3-dev libsasl2-dev libldap2-dev libssl-dev libxml2-dev \
    libxslt1-dev libxmlsec1-dev libffi-dev pkg-config apt-transport-https virtualenv build-essential \
    libmariadb-dev python3-flask psmisc

# 3. Matikan Service yang Berpotensi Bentrok pada Port DNS (53 & 5353)
sudo systemctl stop systemd-resolved dnsmasq named bind9 unbound pdns 2>/dev/null || true
sudo systemctl disable systemd-resolved dnsmasq named bind9 unbound pdns 2>/dev/null || true

# 4. Ganti /etc/resolv.conf dengan Public Resolvers Aktif
sudo chattr -i /etc/resolv.conf 2>/dev/null || true
sudo rm -f /etc/resolv.conf
cat <<EOF | sudo tee /etc/resolv.conf
nameserver 1.1.1.1
nameserver 8.8.8.8
EOF

Langkah 5.2: Setup Unbound Local Recursive Resolver (Port 5353)

Unbound Setup
sudo mkdir -p /etc/unbound/unbound.conf.d/
cat <<EOF | sudo tee /etc/unbound/unbound.conf.d/local-resolver.conf
server:
    verbosity: 1
    interface: 127.0.0.1
    port: 5353
    do-ip4: yes
    do-ip6: no
    do-udp: yes
    do-tcp: yes
    so-reuseport: yes
    access-control: 127.0.0.0/8 allow
    hide-identity: yes
    hide-version: yes
    harden-glue: yes
    harden-dnssec-stripped: yes
    prefetch: yes
    num-threads: 2
    msg-cache-size: 16m
    rrset-cache-size: 32m
EOF

sudo apt-get install -y unbound
sudo systemctl disable --now unbound-resolvconf 2>/dev/null || true
sudo systemctl stop unbound-resolvconf 2>/dev/null || true
sudo systemctl enable --now unbound
sudo systemctl restart unbound

Langkah 5.3: Setup MariaDB 10.11 Engine & Database Security

MariaDB 10.11 Setup
curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version="mariadb-10.11" --skip-maxscale
sudo sed -i '/maxscale/d' /etc/apt/sources.list.d/mariadb*.list 2>/dev/null || true
sudo apt-get update -y
sudo apt-get install -y mariadb-server mariadb-client
sudo systemctl enable --now mariadb

# Buat Database & User Privileges
sudo mariadb -u root <<EOF
CREATE DATABASE IF NOT EXISTS \`pdns_db\`;
CREATE USER IF NOT EXISTS 'pdns_user'@'%' IDENTIFIED BY 'PasswordSangatAman123!';
CREATE USER IF NOT EXISTS 'pdns_user'@'localhost' IDENTIFIED BY 'PasswordSangatAman123!';
ALTER USER 'pdns_user'@'%' IDENTIFIED BY 'PasswordSangatAman123!';
ALTER USER 'pdns_user'@'localhost' IDENTIFIED BY 'PasswordSangatAman123!';
GRANT ALL PRIVILEGES ON \`pdns_db\`.* TO 'pdns_user'@'%';
GRANT ALL PRIVILEGES ON \`pdns_db\`.* TO 'pdns_user'@'localhost';
FLUSH PRIVILEGES;
EOF

# Hardening Security MariaDB
sudo mariadb -u root <<EOF
DELETE FROM mysql.user WHERE User='';
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%';
FLUSH PRIVILEGES;
EOF

Langkah 5.4: Install PowerDNS 4.8, Backend MySQL Schema & REST API Configuration

PowerDNS Setup
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://repo.powerdns.com/FD380FBB-pub.asc | sudo tee /etc/apt/keyrings/auth-48-pub.asc > /dev/null

. /etc/os-release
echo "deb [signed-by=/etc/apt/keyrings/auth-48-pub.asc] http://repo.powerdns.com/${ID} ${VERSION_CODENAME}-auth-48 main" | sudo tee /etc/apt/sources.list.d/pdns.list

cat <<EOF | sudo tee /etc/apt/preferences.d/auth-48
Package: auth*
Pin: origin repo.powerdns.com
Pin-Priority: 600
EOF

sudo apt-get update -y
sudo apt-get install -y pdns-server pdns-backend-mysql
sudo rm -f /etc/powerdns/pdns.d/bind.conf /etc/powerdns/pdns.d/pdns.simplebind.conf /etc/powerdns/pdns.d/pdns.local.bind.conf 2>/dev/null || true

cat <<EOF | sudo tee /etc/powerdns/pdns.d/pdns.local.gmysql.conf
launch+=gmysql
gmysql-host=127.0.0.1
gmysql-port=3306
gmysql-dbname=pdns_db
gmysql-user=pdns_user
gmysql-password=PasswordSangatAman123!
gmysql-dnssec=yes
EOF
sudo chmod 640 /etc/powerdns/pdns.d/pdns.local.gmysql.conf
sudo chown pdns:pdns /etc/powerdns/pdns.d/pdns.local.gmysql.conf 2>/dev/null || sudo chown pdns: /etc/powerdns/pdns.d/pdns.local.gmysql.conf

# Import Schema PowerDNS
sudo mariadb -u root pdns_db < /usr/share/pdns-backend-mysql/schema/schema.mysql.sql

# Konfigurasi REST API & API Key pada pdns.conf
API_KEY=$(uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid 2>/dev/null || head -c 256 /dev/urandom | tr -dc 'a-f0-9' | cut -c1-32)
echo "Generated API Key: ${API_KEY}"

sudo sed -i -E '/^[[:space:]]*#?[[:space:]]*recursor=/d' /etc/powerdns/pdns.conf
sudo sed -i -E '/^[[:space:]]*#?[[:space:]]*resolver=/d' /etc/powerdns/pdns.conf
sudo sed -i -E '/^[[:space:]]*#?[[:space:]]*bind-config=/d' /etc/powerdns/pdns.conf

sudo sed -i -E 's|^[[:space:]]*#?[[:space:]]*api=.*|api=yes|' /etc/powerdns/pdns.conf || echo "api=yes" | sudo tee -a /etc/powerdns/pdns.conf
sudo sed -i -E "s|^[[:space:]]*#?[[:space:]]*api-key=.*|api-key=${API_KEY}|" /etc/powerdns/pdns.conf || echo "api-key=${API_KEY}" | sudo tee -a /etc/powerdns/pdns.conf
sudo sed -i -E 's|^[[:space:]]*#?[[:space:]]*webserver=.*|webserver=yes|' /etc/powerdns/pdns.conf || echo "webserver=yes" | sudo tee -a /etc/powerdns/pdns.conf
sudo sed -i -E 's|^[[:space:]]*#?[[:space:]]*webserver-address=.*|webserver-address=127.0.0.1|' /etc/powerdns/pdns.conf || echo "webserver-address=127.0.0.1" | sudo tee -a /etc/powerdns/pdns.conf

sudo systemctl enable --now pdns
sudo systemctl restart pdns
Panduan Manual Edit File /etc/powerdns/pdns.conf:

Bila Anda ingin mengedit file /etc/powerdns/pdns.conf secara manual menggunakan text editor (contoh: sudo nano /etc/powerdns/pdns.conf), tambahkan/pastikan baris berikut aktif:

# Konfigurasi REST API PowerDNS Authoritative 4.8+
api=yes
api-key=MASUKKAN_UUID_API_KEY_ANDA
webserver=yes
webserver-address=127.0.0.1
webserver-port=8081

Catatan Penting Parameter recursor: Pada PowerDNS Authoritative Server v4.8+, parameter recursor=127.0.0.1:5353 sudah dihapus/deprecated dari core PowerDNS. Hapus baris recursor= jika ada dari sisa instalasi lama. Unbound berjalan secara mandiri di port 5353 untuk query rekursif lokal.

Tutorial Cara Generate API Key PowerDNS (Pilih Salah Satu Metode):

Jalankan salah satu perintah berikut di terminal Linux untuk menghasilkan string API Key yang aman (kriptografik):

# METODE 1: Menggunakan uuidgen (Rekomendasi Utama — Standard UUID v4)
uuidgen

# METODE 2: Menggunakan Kernel Random UUID (Bawaan Linux Kernel, tanpa install paket)
cat /proc/sys/kernel/random/uuid

# METODE 3: Menggunakan OpenSSL (Format Hex 32 Karakter)
openssl rand -hex 16

# METODE 4: Menggunakan /dev/urandom Stream (Kriptografik 32 Karakter)
head -c 256 /dev/urandom | tr -dc 'a-f0-9' | cut -c1-32

Contoh Hasil Output: a3f8c9b2-1d4e-4f5a-8b9c-0d1e2f3a4b5c. Salin string hasil keluaran terminal tersebut dan gunakan untuk nilai api-key=... pada /etc/powerdns/pdns.conf serta pada form setting API di PowerDNS-Admin UI.

Langkah 5.5: Install Node.js, Yarn & Clone Repository PowerDNS-Admin

Node.js & PowerDNS-Admin Clone
# 1. Install Node.js v22.x
curl -sL https://deb.nodesource.com/setup_22.x | sudo bash -
sudo apt-get install -y nodejs

# 2. Install Yarn Package Manager
sudo mkdir -p /etc/apt/keyrings
sudo chmod 0755 /etc/apt/keyrings
curl -fsSL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --yes --dearmor | sudo tee /etc/apt/keyrings/yarn.gpg > /dev/null
echo "deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update -y && sudo apt-get install -y yarn

# 3. Clone Repository PowerDNS-Admin
sudo mkdir -p /var/www/html
sudo git clone https://github.com/alsyundawy/PowerDNS-Admin.git /var/www/html/pdns-admin
cd /var/www/html/pdns-admin

Langkah 5.6: Setup Python Virtual Environment & Konfigurasi PowerDNS-Admin

Python Venv & Config
cd /var/www/html/pdns-admin

# 1. Setup Python Virtual Environment
sudo python3 -m venv flask
source flask/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
deactivate

# 2. Konfigurasi Database & Security pada default_config.py
CONFIG_FILE="powerdnsadmin/default_config.py"
SECRET_KEY=$(head -c 256 /dev/urandom | tr -dc '2-9a-hj-km-np-zA-HJ-NP-Z' | cut -c1-32)

sudo sed -i -E '/### DATABASE CONFIG/,/SQLA_DB_NAME/ d' "${CONFIG_FILE}"
sudo sed -i -E '/SECRET_KEY/ d' "${CONFIG_FILE}"

cat <<EOF | sudo tee -a "${CONFIG_FILE}"

### DATABASE CONFIG
SQLA_DB_USER     = 'pdns_user'
SQLA_DB_PASSWORD = 'PasswordSangatAman123!'
SQLA_DB_HOST     = '127.0.0.1'
SQLA_DB_NAME     = 'pdns_db'
SECRET_KEY       = '${SECRET_KEY}'
EOF

sudo chmod 640 "${CONFIG_FILE}"
sudo chown pdns:pdns "${CONFIG_FILE}" 2>/dev/null || sudo chown pdns: "${CONFIG_FILE}"

Langkah 5.7: Database Migration & Build Web Assets

Database Migration & Asset Compilation
cd /var/www/html/pdns-admin
source flask/bin/activate
export FLASK_APP=powerdnsadmin/__init__.py
flask db upgrade
yarn install --pure-lockfile
flask assets build
deactivate

sudo chown -R pdns:pdns /var/www/html/pdns-admin 2>/dev/null || sudo chown -R pdns: /var/www/html/pdns-admin

Langkah 5.8: Konfigurasi Nginx Reverse Proxy, Systemd Socket & Service

Nginx & Systemd Service Setup
# 1. Install Nginx
sudo apt-get update -y
sudo apt-get install -y nginx-full

# 2. Buat File Konfigurasi Nginx
cat <<'EOF' | sudo tee /etc/nginx/conf.d/powerdns.conf
server {
    listen 80;
    listen [::]:80;

    server_name ns1.domain-anda.com;
    index index.html index.htm;
    root /var/www/html/pdns-admin;

    access_log /var/log/nginx/pdnsadmin_access.log combined;
    error_log /var/log/nginx/pdnsadmin_error.log;

    client_max_body_size 128M;
    client_body_buffer_size 128k;
    autoindex off;
    server_tokens off;
    charset utf-8;

    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header Referrer-Policy "no-referrer-when-downgrade";

    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml;

    location ~ ^/static/ {
        include /etc/nginx/mime.types;
        root /var/www/html/pdns-admin/powerdnsadmin;
        expires 365d;
        add_header Cache-Control "public, must-revalidate, proxy-revalidate";
    }

    location / {
        proxy_pass http://unix:/run/pdnsadmin/socket;
        proxy_read_timeout 120;
        proxy_connect_timeout 120;
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
EOF

sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl restart nginx

# 3. Setup Systemd Service & Socket
cat <<'EOF' | sudo tee /etc/systemd/system/pdnsadmin.service
[Unit]
Description=PowerDNS-Admin
Requires=pdnsadmin.socket
After=network.target

[Service]
PIDFile=/run/pdnsadmin/pid
User=pdns
Group=pdns
WorkingDirectory=/var/www/html/pdns-admin
ExecStart=/var/www/html/pdns-admin/flask/bin/gunicorn --pid /run/pdnsadmin/pid --bind unix:/run/pdnsadmin/socket 'powerdnsadmin:create_app()'
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
EOF

cat <<'EOF' | sudo tee /etc/systemd/system/pdnsadmin.socket
[Unit]
Description=PowerDNS-Admin socket

[Socket]
ListenStream=/run/pdnsadmin/socket
SocketUser=pdns
SocketGroup=pdns
SocketMode=0666

[Install]
WantedBy=sockets.target
EOF

# 4. Setup Runtime Directory & Permissions
sudo mkdir -p /run/pdnsadmin/
echo "d /run/pdnsadmin 0755 pdns pdns -" | sudo tee /etc/tmpfiles.d/pdnsadmin.conf
sudo systemd-tmpfiles --create /etc/tmpfiles.d/pdnsadmin.conf 2>/dev/null || true

sudo chown -R pdns:pdns /run/pdnsadmin/ 2>/dev/null || sudo chown -R pdns: /run/pdnsadmin/
sudo chown -R pdns:pdns /var/www/html/pdns-admin 2>/dev/null || sudo chown -R pdns: /var/www/html/pdns-admin

sudo systemctl daemon-reload
sudo systemctl enable pdnsadmin.socket pdnsadmin.service
sudo systemctl restart pdnsadmin.socket pdnsadmin.service

Langkah 5.9: Certificate SSL via Certbot & Auto-Renewal Setup

SSL Certbot Setup
# 1. Install Certbot Nginx Plugin
sudo apt-get install -y certbot python3-certbot-nginx

# 2. Request Certificate SSL HTTPS
sudo certbot --nginx -d ns1.domain-anda.com --email admin@domain-anda.com --agree-tos --non-interactive

# 3. Setup Cron Job Auto-Renewal SSL (Setiap jam 03:00)
cat <<'EOF' | sudo tee /etc/cron.d/certbot-renewal
0 3 * * * root /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"
EOF
sudo chmod 644 /etc/cron.d/certbot-renewal
6

Inisialisasi & Konfigurasi Web Admin UI

Setelah instalasi selesai, ikuti langkah visual berikut untuk mengonfigurasi koneksi antara PowerDNS-Admin dan PowerDNS REST API:

  • Langkah 1 (Buat Akun Admin): Buka browser dan panggil URL domain Anda https://ns1.domain-anda.com. Buat akun Administrator pertama.
  • Langkah 2 (Buka Settings PDNS API): Pilih menu SettingsPowerDNS → Centang opsi PowerDNS API Integration.
  • Langkah 3 (Isi Parameter API):
    • API URL: http://127.0.0.1:8081
    • API Key: Masukkan UUID API Key yang dihasilkan installer (lihat di output debug terminal)
    • Version: 4.8.0
Status koneksi akan hijau bertuliskan "PowerDNS API connected successfully".
7

Migrasi Zona BIND9 ke PowerDNS Backend (Optional)

Utility zone2sql dari paket PowerDNS memfasilitasi pengimporan berkas zona BIND9 langsung ke database MariaDB:

BIND9 Migration Utility
# Konversi dan Impor Zona BIND9 ke MariaDB pdns_db
zone2sql --named-conf=/etc/bind/named.conf --gmysql | \
    sudo mariadb -u pdns_user -p'PasswordSangatAman123!' pdns_db

# Verifikasi Hasil Impor Domain
sudo mariadb -u pdns_user -p'PasswordSangatAman123!' pdns_db -e "SELECT id, name, type FROM domains;"
8

Keamanan, Hardening & Performa High Availability

Pedoman Hardening Keamanan Server:
  • Limit Firewall Rules: Batasi port publik hanya ke 53 (DNS Query), 80 (Certbot Redirect), dan 443 (HTTPS Panel). Restriksi port 8081 API hanya ke loopback 127.0.0.1.
  • File Permissions: Berikan hak akses ketat chmod 640 dan pemilikan pdns:pdns pada pdns.local.gmysql.conf & default_config.py.
  • Cron Auto-Renewal: Pastikan cron job Certbot aktif pada /etc/cron.d/certbot-renewal untuk perpanjangan SSL otomatis.
UFW Firewall Rules Setup
sudo ufw allow 53/udp comment 'PowerDNS DNS Queries'
sudo ufw allow 53/tcp comment 'PowerDNS Zone Transfers'
sudo ufw allow 80/tcp comment 'HTTP Web Certbot'
sudo ufw allow 443/tcp comment 'HTTPS PowerDNS-Admin Web Panel'
sudo ufw reload
9

Troubleshooting & Diagnostic Command Sheet

Gunakan daftar perintah diagnostik berikut bila terjadi hambatan teknis pada layanan:

Diagnostic & Verification Commands
# 1. Status Service Utama
sudo systemctl status pdns.service --no-pager
sudo systemctl status pdnsadmin.service --no-pager
sudo systemctl status pdnsadmin.socket --no-pager
sudo systemctl status unbound --no-pager
sudo systemctl status nginx --no-pager

# 2. Test Unbound Local Resolver Port 5353
dig @127.0.0.1 -p 5353 google.com SOA

# 3. Test REST API PowerDNS
curl -H "X-API-Key: YOUR_API_KEY" http://127.0.0.1:8081/api/v1/servers/localhost

# 4. Inspection Logs Realtime
sudo journalctl -u pdns.service -n 50 --no-pager
sudo journalctl -u pdnsadmin.service -n 50 --no-pager
sudo tail -f /var/log/nginx/pdnsadmin_error.log
Tips Maintenance: Selalu periksa izin runtime soket UNIX di /run/pdnsadmin/socket jika terjadi error Nginx 502 Bad Gateway.
Dokumentasi teknis berdasarkan riset & pengujian instalasi PowerDNS v1.0.2 oleh HARRY DS ALSYUNDAWY (Ubuntu 20.04/22.04 LTS & Debian 11)