diff --git a/zet.home.arpa/README.md b/zet.home.arpa/README.md new file mode 100644 index 0000000..7624da4 --- /dev/null +++ b/zet.home.arpa/README.md @@ -0,0 +1,83 @@ +# zet.home.arpa + +Home lab server providing file sharing, DNS filtering, web proxy, and git hosting. + +## Server Overview + +| Field | Value | +|-------|-------| +| **Hostname** | zet / zet.home.arpa / zet.kenjim.com | +| **Primary IP** | 172.27.0.35 | +| **OS** | Ubuntu 24.04.4 LTS (Noble) | +| **Kernel** | 6.8.0-124-generic | +| **Network** | 172.27.0.0/24 | + +## Services + +| Service | Port(s) | How It Runs | Docs | +|---------|---------|-------------|------| +| nginx (SSL reverse proxy) | 80, 443/tcp | systemd (`nginx`) | [nginx/](nginx/) | +| Pi-hole (DNS + ad blocking) | 53/tcp+udp, 8081/tcp, 67/udp | Docker container | [pihole/](pihole/) | +| Samba (SMB file sharing) | 445, 139 | systemd (`smbd`, `nmbd`) | [samba/](samba/) | +| NFS | 2049/tcp+udp | systemd | [nfs/](nfs/) | +| Squid (web proxy) | 3128/tcp | systemd (`squid`) | [squid/](squid/) | +| Gitea (git server) | 3000/tcp | systemd (`gitea`) | [git-server/](git-server/) | +| SSH | 22/tcp | systemd (`ssh`) | — | +| Docker | — | systemd (`docker`) | — | + +## Public Hostnames (HTTPS via nginx) + +| Hostname | Target | Notes | +|----------|--------|-------| +| `git.kenjim.com` | Gitea (:3000) | Public | +| `www.kenjim.com` | Docker container | Public | +| `kenji.kenjim.com` | Docker container | Public | +| `gt.kenjim.com` | CNAME elsewhere | Cert covers it, nginx drops it | +| `zet.kenjim.com` | SSH only | No web — dynamic DNS entry | + +All HTTPS subdomains share one Let's Encrypt cert. See [ssl/](ssl/) and [nginx/](nginx/). + +## Storage + +See [storage.md](storage.md) for full disk layout. Summary: + +| Mount | Device | Size | Use | +|-------|--------|------|-----| +| `/` | nvme0n1p3 (LVM) | 950 GB | OS + apps | +| `/data/ssd-photos` | sda (LVM, Crucial MX500) | 916 GB | Photos SSD | +| `/data/hsgt10a` | sdb1 (HGST 10 TB) | 9.1 TB | Primary bulk storage | +| `/data/hsgt10b` | sdc1 (HGST 10 TB) | 9.1 TB | Secondary bulk storage | + +## Network + +The server holds four IPs on a single USB NIC (`enx00242788c03a`): + +- `172.27.0.35` — primary (DHCP, used for most services) +- `172.27.0.36`, `172.27.0.37`, `172.27.0.38` — secondary aliases + +The built-in NIC (`enp1s0`) and WiFi (`wlp2s0`) are both **DOWN**. + +## Quick Service Commands + +```bash +# nginx (SSL reverse proxy) +sudo systemctl restart nginx +sudo nginx -t # test config before reload + +# Pi-hole (admin UI now on :8081) +cd ~/docker-pi-hole && docker compose up -d # start +cd ~/docker-pi-hole && docker compose down # stop +docker exec pihole pihole version # version check + +# Samba +sudo systemctl restart smbd nmbd + +# NFS +sudo systemctl restart nfs-server + +# Squid +sudo systemctl restart squid + +# Gitea +sudo systemctl restart gitea +``` diff --git a/zet.home.arpa/nfs/README.md b/zet.home.arpa/nfs/README.md new file mode 100644 index 0000000..c0eb863 --- /dev/null +++ b/zet.home.arpa/nfs/README.md @@ -0,0 +1,61 @@ +# NFS — zet.home.arpa + +NFSv4 file server exporting bulk storage to LAN clients. + +## Overview + +| Field | Value | +|-------|-------| +| **Package** | `nfs-kernel-server` (Ubuntu) | +| **Config** | `/etc/exports` | +| **Services** | `nfs-server`, `nfs-mountd`, `nfs-idmapd`, `rpcbind`, `rpc-statd` | +| **Port** | 2049/tcp+udp | + +## Exports + +| Path | Clients | Options | +|------|---------|---------| +| `/data/hsgt10a` | `172.27.0.0/24` | `rw,sync,no_subtree_check` | + +### `/etc/exports` + +``` +/data/hsgt10a 172.27.0.0/24(rw,sync,no_subtree_check) +``` + +> **Warning**: The current `/etc/exports` on the server has a space between `172.27.0.0/24` and `(rw,sync,...)`. +> A space causes the options to apply to `*` (world) rather than the specified subnet — this is a security misconfiguration. +> The correct syntax has **no space** before the parenthesis: +> ``` +> /data/hsgt10a 172.27.0.0/24(rw,sync,no_subtree_check) +> ``` +> Fix and reload: `sudo exportfs -ra` + +## Service Management + +```bash +sudo systemctl status nfs-server +sudo systemctl restart nfs-server +sudo exportfs -v # show active exports +sudo exportfs -ra # reload /etc/exports without restarting +showmount -e 172.27.0.35 # list exports (run from client) +``` + +## Mounting from a Client + +```bash +# Temporary mount +sudo mount -t nfs 172.27.0.35:/data/hsgt10a /mnt/hsgt10a + +# Permanent — add to client's /etc/fstab: +172.27.0.35:/data/hsgt10a /mnt/hsgt10a nfs defaults,_netdev 0 0 +``` + +## Migration Notes + +To move NFS to a new server: +1. Install: `sudo apt install nfs-kernel-server` +2. Copy `/etc/exports` (fix the space issue above before copying) +3. Ensure `/data/hsgt10a` is mounted on the new host +4. Enable and start: `sudo systemctl enable --now nfs-server` +5. Update any client `/etc/fstab` entries to point to the new server IP diff --git a/zet.home.arpa/nginx/README.md b/zet.home.arpa/nginx/README.md new file mode 100644 index 0000000..0e612b4 --- /dev/null +++ b/zet.home.arpa/nginx/README.md @@ -0,0 +1,114 @@ +# nginx — zet.home.arpa + +SSL-terminating reverse proxy. Handles all inbound HTTPS traffic and routes to backend services by hostname. + +## Overview + +| Field | Value | +|-------|-------| +| **Package** | `nginx` (Ubuntu apt) | +| **Config** | `/etc/nginx/sites-available/kenjim.conf` | +| **SSL cert** | `/etc/nginx/ssl/kenjim.com/` (managed by acme.sh) | +| **Ports** | 80/tcp (HTTP→HTTPS redirect), 443/tcp (HTTPS) | +| **Service** | `nginx.service` (systemd, enabled) | + +## Architecture + +``` +Internet → pfSense NAT (80,443) → nginx on 172.27.0.35 + │ + ┌────────┴─────────┐ + git.kenjim.com (future) + │ + Gitea :3000 +``` + +LAN clients resolve `*.kenjim.com` subdomains directly to `172.27.0.35` via split DNS (Pi-hole + pfSense Unbound host overrides), avoiding hairpin NAT through pfSense's WAN interface. + +## Virtual Hosts + +| Hostname | Backend | Notes | +|----------|---------|-------| +| `git.kenjim.com` | `http://127.0.0.1:3000` | Gitea (systemd service) | +| `www.kenjim.com` | `http://127.0.0.1:8080` | Update port when container is running | +| `kenji.kenjim.com` | `http://127.0.0.1:8082` | Update port when container is running | +| `gt.kenjim.com` | — | Returns 444 (CNAME points elsewhere) | +| default (unknown host) | — | Returns 444 (drops connection) | + +## Config File + +**Location**: `/etc/nginx/sites-available/kenjim.conf` +**Repo copy**: [`kenjim.conf`](kenjim.conf) + +To add a new Docker container backend, add a new `server {}` block following the existing pattern and update the `proxy_pass` port to match the container's host port mapping. + +## SSL Certificate + +Certificate is managed by acme.sh — see [../ssl/](../ssl/). + +| File | Path | +|------|------| +| Full chain | `/etc/nginx/ssl/kenjim.com/fullchain.pem` | +| Private key | `/etc/nginx/ssl/kenjim.com/key.pem` | + +Directory: owned by `kenjim:www-data`, mode `750`. +Sudoers rule at `/etc/sudoers.d/acme-nginx-reload` allows acme.sh to reload nginx without a password on cert renewal. + +## Service Management + +```bash +sudo systemctl status nginx +sudo systemctl reload nginx # reload config (no downtime) +sudo systemctl restart nginx # full restart +sudo nginx -t # test config syntax before applying +``` + +## pfSense NAT Rules + +| WAN Port | Redirect to | Port | Description | +|----------|-------------|------|-------------| +| 80/tcp | 172.27.0.35 | 80 | HTTP → nginx (redirects to HTTPS) | +| 443/tcp | 172.27.0.35 | 443 | HTTPS → nginx | + +## Adding a New Docker Container + +1. Start the container with a host port mapping, e.g. `-p 8083:80` +2. Add a server block to `/etc/nginx/sites-available/kenjim.conf`: + +```nginx +server { + listen 443 ssl; + listen [::]:443 ssl; + server_name newservice.kenjim.com; + + ssl_certificate /etc/nginx/ssl/kenjim.com/fullchain.pem; + ssl_certificate_key /etc/nginx/ssl/kenjim.com/key.pem; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + location / { + proxy_pass http://127.0.0.1:8083; + 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; + } +} +``` + +3. Add the domain to the cert's SAN list if not already covered (see [../ssl/](../ssl/)) +4. Add a CNAME in GoDaddy: `newservice` → `lair.kenjim.com` +5. Add split DNS overrides in Pi-hole and pfSense Unbound +6. Test and reload: `sudo nginx -t && sudo systemctl reload nginx` + +## Migration Notes + +To move nginx to a new server: +1. `sudo apt install nginx` +2. Copy `/etc/nginx/sites-available/kenjim.conf` +3. Copy `/etc/nginx/ssl/kenjim.com/` (cert files) +4. Copy `/etc/sudoers.d/acme-nginx-reload` +5. Re-run `acme.sh --install-cert` to wire up the renewal hook to the new host +6. Update pfSense NAT rules to point to the new host IP +7. Update split DNS overrides to the new host IP diff --git a/zet.home.arpa/nginx/kenjim.conf b/zet.home.arpa/nginx/kenjim.conf new file mode 100644 index 0000000..f96a819 --- /dev/null +++ b/zet.home.arpa/nginx/kenjim.conf @@ -0,0 +1,99 @@ +# /etc/nginx/sites-available/kenjim.conf +# +# SSL reverse proxy for kenjim.com subdomains. +# Certificate managed by acme.sh (DNS-01 via GoDaddy). +# Cert path: /etc/nginx/ssl/kenjim.com/ + +# Redirect all HTTP to HTTPS +server { + listen 80 default_server; + listen [::]:80 default_server; + return 301 https://$host$request_uri; +} + +# Drop requests for unknown hostnames at SSL level (no response) +server { + listen 443 ssl default_server; + listen [::]:443 ssl default_server; + + ssl_certificate /etc/nginx/ssl/kenjim.com/fullchain.pem; + ssl_certificate_key /etc/nginx/ssl/kenjim.com/key.pem; + + return 444; +} + +# Gitea — git.kenjim.com +server { + listen 443 ssl; + listen [::]:443 ssl; + server_name git.kenjim.com; + + ssl_certificate /etc/nginx/ssl/kenjim.com/fullchain.pem; + ssl_certificate_key /etc/nginx/ssl/kenjim.com/key.pem; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + # Gitea runs directly on the host (systemd), not in Docker + location / { + proxy_pass http://127.0.0.1:3000; + 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; + } +} + +# www.kenjim.com — update proxy_pass when container is running +server { + listen 443 ssl; + listen [::]:443 ssl; + server_name www.kenjim.com; + + ssl_certificate /etc/nginx/ssl/kenjim.com/fullchain.pem; + ssl_certificate_key /etc/nginx/ssl/kenjim.com/key.pem; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + location / { + proxy_pass http://127.0.0.1:8080; # update port to match container + 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; + } +} + +# kenji.kenjim.com — update proxy_pass when container is running +server { + listen 443 ssl; + listen [::]:443 ssl; + server_name kenji.kenjim.com; + + ssl_certificate /etc/nginx/ssl/kenjim.com/fullchain.pem; + ssl_certificate_key /etc/nginx/ssl/kenjim.com/key.pem; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + location / { + proxy_pass http://127.0.0.1:8082; # update port to match container + 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; + } +} + +# gt.kenjim.com — CNAME pointing elsewhere; reject cleanly if it lands here +server { + listen 443 ssl; + listen [::]:443 ssl; + server_name gt.kenjim.com; + + ssl_certificate /etc/nginx/ssl/kenjim.com/fullchain.pem; + ssl_certificate_key /etc/nginx/ssl/kenjim.com/key.pem; + + return 444; +} diff --git a/zet.home.arpa/pihole/README.md b/zet.home.arpa/pihole/README.md new file mode 100644 index 0000000..5ba1f60 --- /dev/null +++ b/zet.home.arpa/pihole/README.md @@ -0,0 +1,156 @@ +# Pi-hole — zet.home.arpa + +Network-wide DNS ad blocker and DHCP server. Runs as a Docker container. + +## Overview + +| Field | Value | +|-------|-------| +| **Image** | `pihole/pihole:latest` | +| **Container name** | `pihole` | +| **Compose file** | `/home/kenjim/docker-pi-hole/docker-compose.yml` | +| **Config directory** | `/home/kenjim/docker-pi-hole/` | +| **Restart policy** | `always` | +| **Timezone** | `America/Chicago` | + +## Versions (as of 2026-06-19) + +| Component | Running | Latest | +|-----------|---------|--------| +| Core | v6.1.4 | v6.4.2 | +| Web | v6.2.1 | v6.5.1 | +| FTL | v6.2.3 | v6.6.2 | + +**Updates are available** — see [Upgrading](#upgrading) below. + +## Ports + +| Port | Protocol | Purpose | Accessible from | +|------|----------|---------|-----------------| +| 53 | TCP + UDP | DNS | LAN (pfSense points clients here) | +| 67 | UDP | DHCP | LAN only (broadcast, not routable to WAN) | +| 8081 | TCP | Web admin UI | LAN only (pfSense does not forward this port) | + +**Admin UI**: `http://172.27.0.35:8081/admin` + +## Role: DHCP Server for Untrusted Devices + +Pi-hole acts as the DHCP server for untrusted LAN devices — Alexa/Echo devices, IoT, and children's machines. By serving DHCP, Pi-hole automatically assigns itself as the DNS server for those clients, ensuring DNS-level ad/content blocking applies without any per-device configuration. + +pfSense should have its own DHCP server **disabled** (or scoped to a different subnet/VLAN) for any segment where Pi-hole is authoritative for DHCP, to avoid conflicts. + +### DHCP + Docker caveat + +Pi-hole's own docs recommend `network_mode: host` for reliable DHCP from Docker (broadcast packets don't traverse Docker NAT cleanly). The current setup uses port mappings + `NET_ADMIN` capability instead, which works in practice. If DHCP stops responding after a container restart or image upgrade, switching to host networking is the fix: + +```yaml +# Alternative compose config if DHCP becomes unreliable: +services: + pihole: + network_mode: host # replaces all port: mappings + # remove the ports: section entirely + # admin UI returns to port 80, but WAN can't reach it + # since pfSense won't forward port 80 to this host +``` + +## Volume Mounts + +| Host path | Container path | Contents | +|-----------|----------------|----------| +| `./etc-pihole` | `/etc/pihole` | Pi-hole config, gravity DB, DHCP leases | +| `./etc-dnsmasq.d` | `/etc/dnsmasq.d` | Custom DNS/DHCP overrides | + +Both paths are relative to `/home/kenjim/docker-pi-hole/`. + +## docker-compose.yml + +```yaml +version: "3" + +services: + pihole: + container_name: pihole + image: pihole/pihole:latest + ports: + - "53:53/tcp" + - "53:53/udp" + - "67:67/udp" + - "8081:80/tcp" + environment: + TZ: 'America/Chicago' + FTLCONF_MAXCONCURRENTQUERIES: 300 + volumes: + - './etc-pihole:/etc/pihole' + - './etc-dnsmasq.d:/etc/dnsmasq.d' + cap_add: + - NET_ADMIN + restart: always +``` + +## Service Management + +```bash +cd ~/docker-pi-hole + +docker compose up -d # start +docker compose down # stop +docker compose pull && docker compose up -d # update image +docker compose logs -f pihole # follow logs + +# Run pihole commands inside container +docker exec pihole pihole status +docker exec pihole pihole -g # update gravity (blocklists) +docker exec pihole pihole version +``` + +## Upgrading + +Pi-hole updates are released as new Docker image versions: + +```bash +cd ~/docker-pi-hole +docker compose pull # pull latest image +docker compose up -d # recreate container +docker exec pihole pihole version # verify new version +``` + +The volumes (`etc-pihole/`, `etc-dnsmasq.d/`) persist config across upgrades. + +## Migration Notes + +To move Pi-hole to a new server: + +1. Stop the container: `docker compose down` +2. Copy the entire `/home/kenjim/docker-pi-hole/` directory to the new host +3. Install Docker on the new host +4. Run `docker compose up -d` from the copied directory +5. Update pfSense DNS server setting to point to the new host IP + +Key data to preserve: +- `etc-pihole/gravity.db` — blocklists +- `etc-pihole/dhcp.leases` — DHCP lease table +- `etc-pihole/pihole.toml` — main config (v6+ format) +- `etc-pihole/local.list` — local DNS overrides +- `etc-dnsmasq.d/` — custom dnsmasq config + +## Network Integration + +Pi-hole is one of two DNS resolvers on the LAN — some devices use Pi-hole (`172.27.0.35:53`), others use pfSense's built-in Unbound resolver directly. + +## Split DNS — Local Overrides + +To avoid hairpin NAT issues (LAN clients connecting to the public WAN IP for local services), DNS overrides are configured in **both** resolvers so that `kenjim.com` subdomains resolve directly to `172.27.0.35` from inside the LAN. + +### Pi-hole (Local DNS → DNS Records) + +| Domain | IP | +|--------|----| +| `git.kenjim.com` | `172.27.0.35` | +| `www.kenjim.com` | `172.27.0.35` | +| `kenji.kenjim.com` | `172.27.0.35` | + +### pfSense DNS Resolver (Services → DNS Resolver → Host Overrides) + +Same three entries as above — covers clients that use pfSense Unbound instead of Pi-hole. + +Without these overrides, LAN clients resolving `git.kenjim.com` get the public WAN IP and connect to pfSense's own HTTPS interface rather than nginx on zet. diff --git a/zet.home.arpa/pihole/docker-compose.yml b/zet.home.arpa/pihole/docker-compose.yml new file mode 100644 index 0000000..a1d1480 --- /dev/null +++ b/zet.home.arpa/pihole/docker-compose.yml @@ -0,0 +1,27 @@ +version: "3" + +# https://github.com/pi-hole/docker-pi-hole/blob/master/README.md +# Source: /home/kenjim/docker-pi-hole/docker-compose.yml on zet.home.arpa + +services: + pihole: + container_name: pihole + image: pihole/pihole:latest + # For DHCP it is recommended to remove these ports and instead add: network_mode: "host" + ports: + - "53:53/tcp" + - "53:53/udp" + - "67:67/udp" + - "8081:80/tcp" + environment: + TZ: 'America/Chicago' + FTLCONF_MAXCONCURRENTQUERIES: 300 + # WEBPASSWORD: 'set a secure password here or it will be random' + # Volumes store your data between container upgrades + volumes: + - './etc-pihole:/etc/pihole' + - './etc-dnsmasq.d:/etc/dnsmasq.d' + # https://github.com/pi-hole/docker-pi-hole#note-on-capabilities + cap_add: + - NET_ADMIN + restart: always diff --git a/zet.home.arpa/samba/README.md b/zet.home.arpa/samba/README.md new file mode 100644 index 0000000..c7a1d32 --- /dev/null +++ b/zet.home.arpa/samba/README.md @@ -0,0 +1,86 @@ +# Samba — zet.home.arpa + +SMB file sharing for the home network. + +## Overview + +| Field | Value | +|-------|-------| +| **Package** | `samba` (Ubuntu package) | +| **Version** | Samba 4.19.5-Ubuntu | +| **Config** | `/etc/samba/smb.conf` | +| **Logs** | `/var/log/samba/log.%m` | +| **Services** | `smbd.service`, `nmbd.service` | + +## Shares + +| Share name | Path | Read-only | Notes | +|------------|------|-----------|-------| +| `photos` | `/data/ssd-photos` | No | Crucial MX500 SSD — photos library | +| `hsgt10a` | `/data/hsgt10a` | No | HGST 10 TB primary bulk drive | +| `printers` | `/var/tmp` | Yes | Default printer share (unused) | +| `print$` | `/var/lib/samba/printers` | Yes | Printer drivers (unused) | + +### Access paths (from Windows/macOS) + +``` +\\172.27.0.35\photos +\\172.27.0.35\hsgt10a +``` + +## Configuration (`/etc/samba/smb.conf` — relevant sections) + +```ini +[global] + workgroup = WORKGROUP + server role = standalone server + map to guest = bad user + usershare allow guests = yes + log file = /var/log/samba/log.%m + max log size = 1000 + +[photos] + comment = Photos SSD on Zet + path = /data/ssd-photos + read only = no + browsable = yes + +[hsgt10a] + comment = HSGT10 Data Drive + path = /data/hsgt10a + read only = no + browsable = yes +``` + +## Service Management + +```bash +sudo systemctl status smbd nmbd +sudo systemctl restart smbd nmbd +sudo systemctl enable smbd nmbd # already enabled +``` + +## User Management + +```bash +sudo smbpasswd -a # add Samba user +sudo smbpasswd -e # enable user +sudo pdbedit -L # list Samba users +``` + +## Testing + +```bash +testparm # validate smb.conf syntax +smbclient -L 172.27.0.35 -N # list shares anonymously +sudo tail -f /var/log/samba/log.smbd # watch connections +``` + +## Migration Notes + +To migrate to a new server: +1. Install: `sudo apt install samba` +2. Copy `/etc/samba/smb.conf` +3. Recreate Samba users: `sudo smbpasswd -a ` +4. Ensure `/data/ssd-photos` and `/data/hsgt10a` are mounted at the same paths +5. Restart: `sudo systemctl enable --now smbd nmbd` diff --git a/zet.home.arpa/squid/README.md b/zet.home.arpa/squid/README.md new file mode 100644 index 0000000..1241d54 --- /dev/null +++ b/zet.home.arpa/squid/README.md @@ -0,0 +1,116 @@ +# Squid Web Proxy — zet.home.arpa + +Caching web proxy with content filtering for children's devices on the LAN. + +## Overview + +| Field | Value | +|-------|-------| +| **Package** | `squid` (Ubuntu) | +| **Version** | Squid 6.14 | +| **Config** | `/etc/squid/squid.conf` | +| **Port** | 3128/tcp | +| **Cache directory** | `/var/spool/squid` (2 GB, UFS) | +| **Access log** | `/var/log/squid/access.log` | + +## How It Works + +Squid runs as a standard forward proxy. pfSense intercepts HTTP traffic from children's device IPs and redirects port 80 to `172.27.0.35:3128` via NAT port-forward (see [../PROXY-SETUP.md](../PROXY-SETUP.md) for pfSense setup). + +``` +Child device → pfSense NAT (port 80 → 172.27.0.35:3128) → Squid → Internet +``` + +## Configuration (`/etc/squid/squid.conf`) + +``` +http_port 3128 + +# ACLs +acl children src "/etc/squid/children.txt" +acl blocked_sites dstdomain "/etc/squid/blocked_sites.txt" +acl SSL_ports port 443 +acl Safe_ports port 80 +acl Safe_ports port 443 +acl CONNECT method CONNECT +acl allowed_hosts src 172.27.0.0/24 + +# Access rules +http_access deny blocked_sites children +http_access deny !Safe_ports +http_access allow allowed_hosts + +# Cache +cache_dir ufs /var/spool/squid 2048 16 256 +cache_mem 512 MB +maximum_object_size_in_memory 1 MB +maximum_object_size 128 MB +minimum_object_size 0 KB + +# Refresh patterns +refresh_pattern ^ftp: 1440 20% 10080 +refresh_pattern ^gopher: 1440 0% 1440 +refresh_pattern -i \.(gif|png|jpg|jpeg|ico)$ 10080 90% 43200 +refresh_pattern -i \.(css|js)$ 1440 90% 10080 +refresh_pattern . 0 20% 4320 + +pipeline_prefetch 1 +collapsed_forwarding on +``` + +## ACL Files + +### `/etc/squid/children.txt` — devices subject to content filtering + +Contains one IP per line for children's devices on the `172.27.0.0/24` network. + +### `/etc/squid/blocked_sites.txt` — blocked domains + +Domains blocked for children (partial list): + +``` +.facebook.com / .fb.com / .instagram.com / .snapchat.com / .tiktok.com +.x.com / .twitter.com / .reddit.com / .redditmedia.com / .redditstatic.com +.pinterest.com / .youtube.com / .youtu.be / .googlevideo.com +.twitch.tv / .vimeo.com / .dailymotion.com +.netflix.com / .hulu.com / .disneyplus.com +``` + +## Service Management + +```bash +sudo systemctl status squid +sudo systemctl restart squid +sudo systemctl reload squid # reload config without dropping connections +sudo squid -k parse # validate squid.conf syntax + +# Watch live traffic +sudo tail -f /var/log/squid/access.log + +# Check cache stats +sudo squidclient -h 127.0.0.1 mgr:info +``` + +## Adding/Removing Children's IPs + +Edit `/etc/squid/children.txt` — one IP per line, then reload: + +```bash +sudo nano /etc/squid/children.txt +sudo systemctl reload squid +``` + +## Limitations + +- Only intercepts plain **HTTP (port 80)**. HTTPS (port 443) is not filtered. +- To filter HTTPS would require SSL bump (TLS interception) with a custom CA installed on every client device — significantly more complex. +- See [../PROXY-SETUP.md](../PROXY-SETUP.md) for pfSense NAT configuration. + +## Migration Notes + +To move Squid to a new server: +1. Install: `sudo apt install squid` +2. Copy `/etc/squid/squid.conf`, `children.txt`, `blocked_sites.txt` +3. Initialize cache: `sudo squid -z` +4. Start: `sudo systemctl enable --now squid` +5. Update pfSense NAT rule redirect target to the new server IP diff --git a/zet.home.arpa/ssl/README.md b/zet.home.arpa/ssl/README.md new file mode 100644 index 0000000..45bbf3c --- /dev/null +++ b/zet.home.arpa/ssl/README.md @@ -0,0 +1,124 @@ +# SSL Certificate — zet.home.arpa + +Let's Encrypt certificate for `kenjim.com` subdomains, issued and renewed via acme.sh using GoDaddy DNS-01 challenge. + +## Overview + +| Field | Value | +|-------|-------| +| **CA** | Let's Encrypt | +| **Tool** | acme.sh v3.1.4 | +| **Challenge** | DNS-01 via GoDaddy API | +| **Key type** | ECC (default in acme.sh v3) | +| **Validity** | 90 days (auto-renewed at ~60 days) | +| **Installed to** | `/etc/nginx/ssl/kenjim.com/` | +| **acme.sh data** | `~/.acme.sh/zet.kenjim.com_ecc/` | + +## Covered Domains (SANs) + +- `zet.kenjim.com` (primary) +- `git.kenjim.com` +- `www.kenjim.com` +- `kenji.kenjim.com` +- `gt.kenjim.com` + +## GoDaddy API Credentials + +Stored by acme.sh in `~/.acme.sh/account.conf` after first use. Only needs to be re-exported if rotating the API key. + +The API key used is named **"Claude"** in GoDaddy's developer portal (developer.godaddy.com → API Keys). + +To re-export credentials for a manual reissue: +```bash +export GD_Key='your-key' +export GD_Secret='your-secret' +``` + +**Never paste credentials into chat or commit them to this repo.** + +## Installed Certificate Files + +| File | Purpose | +|------|---------| +| `/etc/nginx/ssl/kenjim.com/fullchain.pem` | Certificate + intermediates (used by nginx) | +| `/etc/nginx/ssl/kenjim.com/key.pem` | Private key (used by nginx) | + +Directory ownership: `kenjim:www-data`, mode `750`. Files readable by nginx (`www-data` group). + +## Auto-Renewal + +acme.sh installs a cron job at install time: + +```bash +crontab -l | grep acme # verify cron entry exists +``` + +On renewal acme.sh: +1. Adds `_acme-challenge` TXT records to GoDaddy DNS via API +2. Requests new cert from Let's Encrypt +3. Copies cert files to `/etc/nginx/ssl/kenjim.com/` +4. Runs `sudo systemctl reload nginx` +5. Cleans up TXT records + +The sudoers rule that allows passwordless nginx reload: +``` +/etc/sudoers.d/acme-nginx-reload: +kenjim ALL=(ALL) NOPASSWD: /bin/systemctl reload nginx +``` + +## Manual Reissue + +Only needed if the cert is lost or domains change: + +```bash +export GD_Key='your-key' +export GD_Secret='your-secret' + +~/.acme.sh/acme.sh --issue \ + --server letsencrypt \ + --dns dns_gd \ + -d zet.kenjim.com \ + -d git.kenjim.com \ + -d www.kenjim.com \ + -d kenji.kenjim.com \ + -d gt.kenjim.com + +~/.acme.sh/acme.sh --install-cert \ + -d zet.kenjim.com \ + --fullchain-file /etc/nginx/ssl/kenjim.com/fullchain.pem \ + --key-file /etc/nginx/ssl/kenjim.com/key.pem \ + --reloadcmd "sudo systemctl reload nginx" +``` + +Note: always pass `--server letsencrypt` — acme.sh v3 defaults to ZeroSSL which has a 24-hour retry delay incompatible with acme.sh's timeout. + +## Verify Certificate + +```bash +# Check cert served by nginx locally +curl -sv --resolve git.kenjim.com:443:172.27.0.35 https://git.kenjim.com 2>&1 \ + | grep -E "subject|issuer|subjectAltName|SSL connection" + +# Check expiry +~/.acme.sh/acme.sh --list + +# Check from outside the LAN +curl -sv https://git.kenjim.com 2>&1 | grep -E "subject|issuer|< HTTP" +``` + +## DNS Setup + +Public DNS uses CNAMEs to a DDNS-managed A record: + +| Record | Type | Value | +|--------|------|-------| +| `lair.kenjim.com` | A | `` — updated by pfSense DDNS | +| `zet.kenjim.com` | CNAME | `lair.kenjim.com` | +| `git.kenjim.com` | CNAME | `lair.kenjim.com` | +| `www.kenjim.com` | CNAME | `lair.kenjim.com` | +| `kenji.kenjim.com` | CNAME | `lair.kenjim.com` | +| `gt.kenjim.com` | CNAME | `lair.kenjim.com` | + +pfSense Dynamic DNS keeps `lair.kenjim.com` updated whenever the ISP WAN IP changes. All subdomains follow automatically via their CNAMEs. + +See [../nginx/](../nginx/) for how the cert is used. diff --git a/zet.home.arpa/storage.md b/zet.home.arpa/storage.md new file mode 100644 index 0000000..435bfca --- /dev/null +++ b/zet.home.arpa/storage.md @@ -0,0 +1,82 @@ +# Storage — zet.home.arpa + +## Disk Layout + +``` +NAME SIZE FSTYPE MOUNTPOINT LABEL +nvme0n1 953.9G +├─nvme0n1p1 1G vfat /boot/efi +├─nvme0n1p2 2G ext4 /boot +└─nvme0n1p3 950.8G LVM2_member + └─ubuntu--vg-ubuntu--lv 950.8G ext4 / (OS disk) + +sda 931.5G +└─sda1 931.5G LVM2_member + └─crucial--mx500-backup 931.5G ext4 /data/ssd-photos (Crucial MX500 SSD) + +sdb 9.1T +└─sdb1 9.1T ext4 /data/hsgt10a (HGST 10TB #1) + +sdc 9.1T +└─sdc1 9.1T ext4 /data/hsgt10b (HGST 10TB #2) + +sdd 3.6T (unmounted — ws4000a/ws4000b) +├─sdd1 1.8T ext4 ws4000a +└─sdd2 1.8T ext4 ws4000b +``` + +## Mount Points + +| Mount | Device | Size | Used | Free | Use% | +|-------|--------|------|------|------|------| +| `/` | ubuntu--vg-ubuntu--lv | 935 GB | 302 GB | 586 GB | 34% | +| `/boot` | nvme0n1p2 | 2.0 GB | 201 MB | 1.6 GB | 11% | +| `/boot/efi` | nvme0n1p1 | 1.1 GB | 6.2 MB | 1.1 GB | 1% | +| `/data/ssd-photos` | crucial--mx500-backup | 916 GB | 426 GB | 445 GB | 49% | +| `/data/hsgt10a` | sdb1 | 9.1 TB | 7.4 TB | 1.3 TB | 86% | +| `/data/hsgt10b` | sdc1 | 9.1 TB | 7.4 TB | 1.3 TB | 86% | + +## fstab (`/etc/fstab`) + +``` +# OS / boot / swap — installer-generated +/dev/disk/by-id/dm-uuid-LVM-pZkwCzArj53TehlUJeYjQyR5pMsex7qNIPGqddmwnOHy5Rw7eBJvFHdd5fPzVMtw / ext4 defaults 0 1 +/dev/disk/by-uuid/a186b998-5a71-4ca5-a7f0-dc36055e5d49 /boot ext4 defaults 0 1 +/dev/disk/by-uuid/8D7F-166E /boot/efi vfat defaults 0 1 +/swap.img none swap sw 0 0 + +# Added by Kenji +/dev/disk/by-id/dm-name-crucial--mx500-backup /data/ssd-photos ext4 defaults 0 1 +/swap.extra none swap sw 0 0 +/dev/sdb1 /data/hsgt10a ext4 defaults 0 1 +/dev/sdc1 /data/hsgt10b ext4 defaults 0 1 +``` + +## Samba Shares + +| Share | Path | Notes | +|-------|------|-------| +| `photos` | `/data/ssd-photos` | Crucial MX500 SSD, photos library | +| `hsgt10a` | `/data/hsgt10a` | HGST 10 TB primary bulk drive | + +## NFS Exports + +| Export | Clients | Options | +|--------|---------|---------| +| `/data/hsgt10a` | `172.27.0.0/24` | `rw,sync,no_subtree_check` | + +## Notes + +- **`sdd` (3.6 TB)** is connected but unmounted. Partitions labeled `ws4000a` and `ws4000b`, previously referenced in fstab as CIFS mounts from `172.27.0.2`. These appear to be historical drive copies — verify before mounting. +- **`/data/hsgt10a` and `/data/hsgt10b`** are at 86% capacity. Monitor closely. +- **SMART monitoring** (`smartmontools.service`) is active — check `smartctl -a /dev/sdX` for drive health. + +## Checking Drive Health + +```bash +sudo smartctl -a /dev/sda # Crucial MX500 +sudo smartctl -a /dev/sdb # HGST 10TB #1 +sudo smartctl -a /dev/sdc # HGST 10TB #2 +sudo smartctl -a /dev/sdd # unmounted 3.6 TB +sudo smartctl -a /dev/nvme0n1 # OS NVMe +```