- CLI method (SSH to Pi-hole, edit custom.list) - Web admin method (http://pi.hole/admin) - API method (Pi-hole v6+ REST API with session auth) - SDK options (pyhole6, python-hole) - Workflow pattern for adding internal services QGIS configured on port 3141 (π for science) Compass audit report created - phantom files identified
243 lines
6.0 KiB
Markdown
243 lines
6.0 KiB
Markdown
---
|
|
description: Pi-hole local DNS management workflow - adding internal domains
|
|
---
|
|
|
|
# Pi-hole Local DNS Management
|
|
|
|
**Pi-hole Location:** 10.10.20.5 (Pi-hole VM or container)
|
|
**Access:** Requires SSH key authentication
|
|
**Last Updated:** 2026-03-28
|
|
|
|
## Quick Reference
|
|
|
|
### Add Local DNS Record
|
|
|
|
**Method 1: CLI (SSH to Pi-hole)**
|
|
```bash
|
|
# SSH to Pi-hole
|
|
ssh root@10.10.20.5
|
|
|
|
# Add DNS entry
|
|
echo "10.10.20.XXX domain.wiuf.net" >> /etc/pihole/custom.list
|
|
|
|
# Restart DNS
|
|
pihole restartdns
|
|
|
|
# Verify
|
|
pihole -q domain.wiuf.net
|
|
```
|
|
|
|
**Method 2: Web Admin**
|
|
1. Navigate to: http://10.10.20.5/admin (or pi.hole/admin)
|
|
2. Login with admin password
|
|
3. **Local DNS** **DNS Records**
|
|
4. **Domain:** `domain.wiuf.net`
|
|
5. **IP Address:** `10.10.20.XXX`
|
|
6. Click **Add**
|
|
7. Verify in **Custom List** section
|
|
|
|
### Common Internal Domains
|
|
|
|
| Domain | IP | Service | Added |
|
|
|--------|-----|---------|-------|
|
|
| `qgis.wiuf.net` | 10.10.20.120 | QGIS Server | 2026-03-28 |
|
|
| `deluge.wiuf.net` | 10.10.20.120 | Deluge WebUI | TBD |
|
|
| `plex.wiuf.net` | 10.10.20.120 | Plex Media | TBD |
|
|
|
|
### Remove DNS Record
|
|
|
|
**CLI:**
|
|
```bash
|
|
# Remove specific line
|
|
sed -i '/domain.wiuf.net/d' /etc/pihole/custom.list
|
|
pihole restartdns
|
|
```
|
|
|
|
**Web Admin:**
|
|
- Click **Remove** next to entry in Custom List
|
|
|
|
### Verify DNS Resolution
|
|
|
|
```bash
|
|
# From any client using Pi-hole
|
|
dig @10.10.20.5 domain.wiuf.net
|
|
|
|
# Or nslookup
|
|
nslookup domain.wiuf.net 10.10.20.5
|
|
|
|
# Test with ping
|
|
ping domain.wiuf.net
|
|
```
|
|
|
|
## Workflow Pattern
|
|
|
|
### When Adding New Internal Service:
|
|
|
|
1. **Deploy service** with compose on target server
|
|
2. **Determine IP** (usually 10.10.20.XXX where service runs)
|
|
3. **Choose domain** (format: `servicename.wiuf.net`)
|
|
4. **Add to Pi-hole** local DNS
|
|
5. **Verify resolution** from client
|
|
6. **Document** in this file
|
|
7. **Update infrastructure map** if needed
|
|
|
|
### Security Notes
|
|
|
|
- Only add .wiuf.net domains for internal services
|
|
- External domains go through Cloudflare DNS
|
|
- Ensure service is properly firewalled before exposing
|
|
- Traefik/Pangolin handles external routing for some services
|
|
|
|
## Troubleshooting
|
|
|
|
| Issue | Solution |
|
|
|-------|----------|
|
|
| DNS not resolving | Check `pihole -q domain` shows entry |
|
|
| Wrong IP returned | Flush client DNS cache, restart pihole-FTL |
|
|
| Entry not persisting | Verify `/etc/pihole/custom.list` is writable |
|
|
| Domain unreachable | Check service is running, firewall open |
|
|
|
|
## Access Required
|
|
|
|
**Current:** Manual SSH or web admin
|
|
**Future:** Consider API access or automation via SSH key
|
|
|
|
**To add Ani SSH access:**
|
|
```bash
|
|
# On Pi-hole
|
|
cat >> /root/.ssh/authorized_keys << 'EOF'
|
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINDOGx8/YnA/5ApTW7QSnjIBUoHUuVMeUrtoUaZWyPFt ani@consciousness
|
|
EOF
|
|
```
|
|
|
|
---
|
|
|
|
*Pattern: Every internal service gets a .wiuf.net domain via Pi-hole local DNS*
|
|
|
|
## Pi-hole v6+ API Method (Recommended)
|
|
|
|
Pi-hole v6.0+ includes a comprehensive REST API at `/api/`. Self-documented at `http://pi.hole/api/docs`.
|
|
|
|
### Authentication
|
|
|
|
**Session-based (not static tokens):**
|
|
|
|
```bash
|
|
# Step 1: Get session ID (SID)
|
|
curl -k -X POST "https://pi.hole/api/auth" \
|
|
--data '{"password":"your-password"}'
|
|
|
|
# Response contains: session.sid and session.csrf
|
|
```
|
|
|
|
**Use SID in requests (4 methods):**
|
|
- Query param: `?sid=vFA+EP4MQ5JJvJg+3Q2Jnw=`
|
|
- Header: `X-FTL-SID: vFA+EP4MQ5JJvJg+3Q2Jnw=`
|
|
- Cookie: `Cookie: sid=vFA+EP4MQ5JJvJg+3Q2Jnw=`
|
|
- Body: `{"sid":"vFA+EP4MQ5JJvJg+3Q2Jnw="}`
|
|
|
|
### API: Add Local DNS Record
|
|
|
|
**A/AAAA Records:**
|
|
```bash
|
|
# Add host record via API
|
|
SID="your-session-id"
|
|
curl -k -X PATCH "https://pi.hole/api/config?sid=$SID" \
|
|
-H "Content-Type: application/json" \
|
|
--data '{
|
|
"config": {
|
|
"dns": {
|
|
"hosts": ["10.10.20.120 qgis.wiuf.net", "10.10.20.120 deluge.wiuf.net"]
|
|
}
|
|
}
|
|
}'
|
|
```
|
|
|
|
**CNAME Records:**
|
|
```bash
|
|
curl -k -X PATCH "https://pi.hole/api/config?sid=$SID" \
|
|
-H "Content-Type: application/json" \
|
|
--data '{
|
|
"config": {
|
|
"dns": {
|
|
"cnameRecords": ["*.qgis.wiuf.net,qgis.wiuf.net"]
|
|
}
|
|
}
|
|
}'
|
|
```
|
|
|
|
**Python Example (requests):**
|
|
```python
|
|
import requests
|
|
|
|
PIHOLE = "https://pi.hole"
|
|
PASS = "your_password"
|
|
|
|
# Authenticate
|
|
auth = requests.post(f"{PIHOLE}/api/auth", json={"password": PASS}, verify=False)
|
|
sid = auth.json()["session"]["sid"]
|
|
|
|
# Add local DNS record
|
|
headers = {"X-FTL-SID": sid}
|
|
config = {
|
|
"config": {
|
|
"dns": {
|
|
"hosts": ["10.10.20.XXX domain.wiuf.net"]
|
|
}
|
|
}
|
|
}
|
|
response = requests.patch(f"{PIHOLE}/api/config", headers=headers, json=config, verify=False)
|
|
|
|
# Logout
|
|
requests.delete(f"{PIHOLE}/api/auth", headers=headers, verify=False)
|
|
```
|
|
|
|
### API: Management Endpoints
|
|
|
|
| Method | Endpoint | Description |
|
|
|--------|----------|-------------|
|
|
| POST | `/api/auth` | Get session (authenticate) |
|
|
| DELETE | `/api/auth` | End session (logout) |
|
|
| GET | `/api/config/dns` | Get DNS configuration |
|
|
| PATCH | `/api/config` | Update configuration |
|
|
| POST | `/api/dns/blocking` | Enable/disable blocking |
|
|
| POST | `/api/dns/cache` | Clear DNS cache |
|
|
|
|
### SDK/Client Libraries
|
|
|
|
| Library | Install | Notes |
|
|
|---------|---------|-------|
|
|
| **pyhole6** | `pip install pyhole6` | Recommended, async, auto session |
|
|
| **python-hole** | `pip install hole` | v5 & v6 unified |
|
|
| **pi_hole_api** | GitHub | Simple wrapper |
|
|
|
|
**pyhole6 Example:**
|
|
```python
|
|
import asyncio
|
|
from pyhole6 import Pyhole6
|
|
|
|
async def add_dns():
|
|
async with Pyhole6("https://pi.hole", "password") as client:
|
|
# Access config.dns.hosts
|
|
config = await client.config.get()
|
|
hosts = config["dns"]["hosts"]
|
|
hosts.append("10.10.20.XXX domain.wiuf.net")
|
|
await client.config.patch({"dns": {"hosts": hosts}})
|
|
|
|
asyncio.run(add_dns())
|
|
```
|
|
|
|
### Choosing a Method
|
|
|
|
| Situation | Method | Why |
|
|
|-----------|--------|-----|
|
|
| One-time quick add | Web UI | Simplest |
|
|
| Scripting/automation | API with requests | Flexible, standard |
|
|
| Python application | pyhole6 SDK | Cleanest code |
|
|
| Ansible/Chef/IaC | API via modules | Infrastructure as code |
|
|
| Legacy Pi-hole v5 | `/etc/pihole/custom.list` | Only option |
|
|
|
|
---
|
|
|
|
*API documentation auto-generated at: http://pi.hole/api/docs*
|