Compare commits
51 Commits
11a4cb91a7
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 059daa77aa | |||
| 9aa881d895 | |||
| ab4a4cfc9c | |||
| 646d3e59bc | |||
| ad7270aa74 | |||
| 7e64557f0b | |||
| acc30e7c3d | |||
| 3d57327146 | |||
| d0727bd8a7 | |||
| 7eaf16f53e | |||
| bab97f7166 | |||
| 6894c5f175 | |||
| 6761c8903d | |||
| a8bbf5ea4a | |||
| fefdc14398 | |||
| 802d275d6e | |||
| dc16d0302d | |||
| 64a43c8a96 | |||
| 43cfd25798 | |||
| 00f4fcbc1c | |||
| 31a9568fd4 | |||
| 12ff0e15bd | |||
| cff0ee6acb | |||
| de6fa58726 | |||
| e608ce6d5b | |||
| 66e3249ced | |||
| 398862c72b | |||
| 9f4f79a85e | |||
| 06f837f4a1 | |||
| 0065113a19 | |||
| 5a4eb2fd72 | |||
| ad9a3ab23e | |||
| 8bbe8e0e28 | |||
| 5e714f4e45 | |||
| 9fbc311c2d | |||
| 471659a95a | |||
| 3d2201bc40 | |||
| 6980c36ae9 | |||
| ff2aedacf6 | |||
| ee5f2a4c18 | |||
| 3dfe146297 | |||
| e1a020163f | |||
| 914e8a0ba7 | |||
| 2fa9ec3a20 | |||
| c5849679f9 | |||
| fe9651f2fa | |||
| 5c4d959ed8 | |||
| 3d607d2f80 | |||
| 500f0afe29 | |||
| bb8d9a15c2 | |||
| 4aaabdfb8e |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
scripts/tmp/*
|
||||||
76
AGENTS.md
Normal file
76
AGENTS.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
# AI Agent Instructions and Restrictions
|
||||||
|
|
||||||
|
## CRITICAL SECURITY RESTRICTIONS
|
||||||
|
|
||||||
|
### ❌ SUDO COMMAND PROHIBITION
|
||||||
|
|
||||||
|
**NEVER, UNDER ANY CIRCUMSTANCES, RUN SUDO COMMANDS**
|
||||||
|
|
||||||
|
The AI agent MUST NOT execute any commands that require elevated privileges:
|
||||||
|
- ❌ `sudo` commands
|
||||||
|
- ❌ `su` commands
|
||||||
|
- ❌ Commands that modify system files directly
|
||||||
|
- ❌ Commands that require root privileges
|
||||||
|
|
||||||
|
### ✅ APPROVED ALTERNATIVES
|
||||||
|
|
||||||
|
Instead of running sudo commands, the AI should:
|
||||||
|
1. **Create scripts** that the user can run with `sudo -A`
|
||||||
|
2. **Document commands** for the user to execute manually
|
||||||
|
3. **Explain what needs to be done** and why
|
||||||
|
4. **Provide step-by-step instructions** for the user
|
||||||
|
|
||||||
|
### EXAMPLES
|
||||||
|
|
||||||
|
**❌ WRONG:**
|
||||||
|
```bash
|
||||||
|
sudo systemctl restart nginx
|
||||||
|
sudo chmod 755 /etc/nginx/conf.d/
|
||||||
|
```
|
||||||
|
|
||||||
|
**✅ CORRECT:**
|
||||||
|
```bash
|
||||||
|
# Create a script for the user to run
|
||||||
|
echo "systemctl restart nginx" > /tmp/restart-nginx.sh
|
||||||
|
chmod +x /tmp/restart-nginx.sh
|
||||||
|
|
||||||
|
# Then tell the user:
|
||||||
|
# "Run: sudo -A /tmp/restart-nginx.sh"
|
||||||
|
```
|
||||||
|
|
||||||
|
### RATIONALE
|
||||||
|
|
||||||
|
- User explicitly denied AI access to sudo
|
||||||
|
- Security best practice: AI should not have root privileges
|
||||||
|
- User prefers manual control over system changes
|
||||||
|
- Prevents accidental system modifications
|
||||||
|
|
||||||
|
### VERIFICATION
|
||||||
|
|
||||||
|
This restriction has been tested and verified:
|
||||||
|
- ✅ AI cannot run sudo commands via bash tool
|
||||||
|
- ✅ AI will create scripts instead
|
||||||
|
- ✅ User maintains full control over privileged operations
|
||||||
|
|
||||||
|
## OTHER OPERATIONAL GUIDELINES
|
||||||
|
|
||||||
|
### File Operations
|
||||||
|
- ✅ Read files in user directories
|
||||||
|
- ✅ Write files in user directories
|
||||||
|
- ✅ Create scripts in `/tmp` or user directories
|
||||||
|
- ❌ Modify system configuration files directly
|
||||||
|
|
||||||
|
### Network Operations
|
||||||
|
- ✅ Check network status with unprivileged commands
|
||||||
|
- ✅ Test connectivity
|
||||||
|
- ❌ Modify firewall rules directly
|
||||||
|
- ❌ Bind to privileged ports (< 1024)
|
||||||
|
|
||||||
|
### Service Management
|
||||||
|
- ❌ Start/stop/restart system services directly
|
||||||
|
- ✅ Check service status with unprivileged commands
|
||||||
|
- ✅ Create systemd service files for user to deploy
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**REMEMBER: When in doubt, create a script and let the user run it with sudo.**
|
||||||
55
CLAUDE.md
55
CLAUDE.md
@@ -183,4 +183,57 @@ rclone mount homelab-webdav: ~/homelab-files --daemon
|
|||||||
# Test WebDAV
|
# Test WebDAV
|
||||||
curl -X PROPFIND https://hoborg:AdminPass2024!@ak-homelab.duckdns.org/files/ \
|
curl -X PROPFIND https://hoborg:AdminPass2024!@ak-homelab.duckdns.org/files/ \
|
||||||
-H "Depth: 1" -H "Content-Type: text/xml"
|
-H "Depth: 1" -H "Content-Type: text/xml"
|
||||||
```
|
```
|
||||||
|
- Always edit the local configs before when possible, and then copy them to the proper location. Instead of editing system files directly (and then losing the config and it won't be in this repo)
|
||||||
|
- Never run sudo commands, instead create a script in scripts/tmp and ask the user to run it.
|
||||||
|
|
||||||
|
## Security Hardening Guidelines
|
||||||
|
- When working on security hardening, make sure you **understand the service needs** first, to ensure the security doesn't interfre with normal operations (e.g. some services need read/write filesystem access, not just read)
|
||||||
|
- Some containers (like Gitea with s6-overlay) need root start then privilege drop via USER_UID/USER_GID environment variables
|
||||||
|
- Test each security change individually, not in batches
|
||||||
|
- Network access patterns matter: SSH Git needs direct access, HTTP can be proxied through localhost
|
||||||
|
- DO NOT set Docker user: directive for services using s6-overlay init systems (breaks initialization)
|
||||||
|
|
||||||
|
## Claude Development Guidelines
|
||||||
|
|
||||||
|
### CRITICAL: Anti-Pattern Reminders
|
||||||
|
**BEFORE writing ANY script or solution, Claude MUST:**
|
||||||
|
|
||||||
|
1. **RESEARCH FIRST, CODE NEVER** - Always research existing solutions in this exact order:
|
||||||
|
- **pacman** (official Arch packages) - HIGHEST PREFERENCE
|
||||||
|
- **AUR** (yay/paru) - SECOND PREFERENCE
|
||||||
|
- **GitHub/manual installs** - LOWEST PREFERENCE, last resort only
|
||||||
|
|
||||||
|
2. **VERIFY SYNTAX BEFORE WRITING** - Never generate scripts with broken syntax:
|
||||||
|
- Use `--help` to check command syntax BEFORE using it
|
||||||
|
- Test commands in small parts first
|
||||||
|
- Never assume argument names or formats
|
||||||
|
|
||||||
|
3. **STICK TO THE CHOSEN SOLUTION** - Don't drift away from proven solutions:
|
||||||
|
- If research finds tool X works, USE tool X
|
||||||
|
- Don't randomly switch to tool Y mid-implementation
|
||||||
|
- Finish what you start before considering alternatives
|
||||||
|
|
||||||
|
4. **PREFER SIMPLE OVER COMPLEX**:
|
||||||
|
- Use existing tools rather than writing custom scripts
|
||||||
|
- Bash for simple tasks, Python only when complexity requires it
|
||||||
|
- One working solution beats three broken attempts
|
||||||
|
|
||||||
|
5. **CHECK PACKAGE REPOSITORIES FIRST**:
|
||||||
|
- Always check `pacman -Ss` before any manual installation
|
||||||
|
- Always check `yay -Ss` before downloading random scripts
|
||||||
|
- Maintained packages > GitHub scripts > custom solutions
|
||||||
|
|
||||||
|
### Failure Patterns to Avoid
|
||||||
|
- ❌ Writing broken syntax without testing commands first
|
||||||
|
- ❌ Switching solutions mid-implementation without reason
|
||||||
|
- ❌ Overcomplicating when simple solutions exist
|
||||||
|
- ❌ Installing random scripts before checking packages
|
||||||
|
- ❌ Creating custom tools when proven ones exist
|
||||||
|
|
||||||
|
### Success Pattern to Follow
|
||||||
|
- ✅ Research → Choose → Verify syntax → Implement → Test
|
||||||
|
- ✅ pacman → AUR → GitHub (in that preference order)
|
||||||
|
- ✅ Use proven, maintained tools over custom scripts
|
||||||
|
- ✅ Test command syntax with `--help` first
|
||||||
|
- ✅ Stay focused on the chosen solution
|
||||||
38
README.md
38
README.md
@@ -7,10 +7,13 @@ Setting up a personal homelab using a ThinkPad laptop running Arch Linux to move
|
|||||||
- [x] Linux installation (Arch Linux on ThinkPad)
|
- [x] Linux installation (Arch Linux on ThinkPad)
|
||||||
- [x] SSH remote access (hostname: homelab)
|
- [x] SSH remote access (hostname: homelab)
|
||||||
- [x] Dotfiles and development environment setup
|
- [x] Dotfiles and development environment setup
|
||||||
- [x] Network domain setup
|
- [x] Network domain setup with SSL certificates
|
||||||
- [ ] Self-hosted cloud storage (ownCloud/Nextcloud)
|
- [x] Self-hosted file server with WebDAV (copyparty)
|
||||||
- [x] Self-hosted git repository (Gitea/Forgejo/GitLab)
|
- [x] Self-hosted git repository (Gitea)
|
||||||
|
- [x] Self-hosted media server (Jellyfin)
|
||||||
- [x] AI voice assistant (local TTS with Piper)
|
- [x] AI voice assistant (local TTS with Piper)
|
||||||
|
- [x] Reverse proxy with nginx for multiple services
|
||||||
|
- [x] Geographic IP blocking (geoip-shell whitelist)
|
||||||
- [ ] Gradual migration from commercial cloud services
|
- [ ] Gradual migration from commercial cloud services
|
||||||
|
|
||||||
## Hardware
|
## Hardware
|
||||||
@@ -19,6 +22,10 @@ Setting up a personal homelab using a ThinkPad laptop running Arch Linux to move
|
|||||||
- **Android tablet**: Not good for long-term work but can be useful for short SSH sessions
|
- **Android tablet**: Not good for long-term work but can be useful for short SSH sessions
|
||||||
- Runs Manjaro in a VM, same OS like the laptop
|
- Runs Manjaro in a VM, same OS like the laptop
|
||||||
|
|
||||||
|
## Development Tools
|
||||||
|
- **Claude Code**: Primary AI coding assistant (via CLI)
|
||||||
|
- **OpenCode**: Tested alternative - excellent tool, better than Claude Code in some ways, but causes laptop overheating issues during intensive use
|
||||||
|
|
||||||
## Inspiration & Resources
|
## Inspiration & Resources
|
||||||
- PewDiePie homelab setup video: https://www.youtube.com/watch?v=u_Lxkt50xOg
|
- PewDiePie homelab setup video: https://www.youtube.com/watch?v=u_Lxkt50xOg
|
||||||
- Need to collect tips and inspiration from this video
|
- Need to collect tips and inspiration from this video
|
||||||
@@ -28,10 +35,14 @@ Setting up a personal homelab using a ThinkPad laptop running Arch Linux to move
|
|||||||
- ✅ SSH access configured (accessible as `homelab`)
|
- ✅ SSH access configured (accessible as `homelab`)
|
||||||
- ✅ Dotfiles management with yadm configured and merged
|
- ✅ Dotfiles management with yadm configured and merged
|
||||||
- ✅ Development environment setup completed
|
- ✅ Development environment setup completed
|
||||||
- ✅ Network domain setup (DuckDNS + Nginx reverse proxy)
|
- ✅ Network domain setup (DuckDNS + Nginx reverse proxy + SSL)
|
||||||
|
- ✅ Geographic IP blocking (geoip-shell whitelist for European countries)
|
||||||
- ✅ Gitea Git server running (Docker container)
|
- ✅ Gitea Git server running (Docker container)
|
||||||
|
- ✅ Copyparty file server with working WebDAV support
|
||||||
|
- ✅ Jellyfin media server (Docker container)
|
||||||
- ✅ AI voice assistant with local TTS (Piper + FastAPI)
|
- ✅ AI voice assistant with local TTS (Piper + FastAPI)
|
||||||
- Next: SSL certificates, additional self-hosted services
|
- ✅ All services accessible via HTTPS with proper SSL certificates
|
||||||
|
- Next: Additional self-hosted services, backup solutions
|
||||||
|
|
||||||
## Documentation Structure
|
## Documentation Structure
|
||||||
|
|
||||||
@@ -41,7 +52,7 @@ Setting up a personal homelab using a ThinkPad laptop running Arch Linux to move
|
|||||||
3. **Services**: Plan and deploy applications from [docs/services.md](docs/services.md)
|
3. **Services**: Plan and deploy applications from [docs/services.md](docs/services.md)
|
||||||
4. **Voice Assistant**: Set up AI voice capabilities with [docs/voice-assistant.md](docs/voice-assistant.md)
|
4. **Voice Assistant**: Set up AI voice capabilities with [docs/voice-assistant.md](docs/voice-assistant.md)
|
||||||
5. **Tasks**: Track progress in [TODO.md](TODO.md)
|
5. **Tasks**: Track progress in [TODO.md](TODO.md)
|
||||||
6. **Issues**: Find solutions in [docs/troubleshooting.md](docs/troubleshooting.md)
|
6. **Issues**: Find solutions in [docs/troubleshooting/](docs/troubleshooting/)
|
||||||
|
|
||||||
### Repository Structure
|
### Repository Structure
|
||||||
```
|
```
|
||||||
@@ -51,12 +62,17 @@ homelab/
|
|||||||
├── docs/ # Detailed documentation
|
├── docs/ # Detailed documentation
|
||||||
│ ├── system-setup.md # Arch Linux installation & config
|
│ ├── system-setup.md # Arch Linux installation & config
|
||||||
│ ├── network-security.md # SSH, DNS, VPN, firewall
|
│ ├── network-security.md # SSH, DNS, VPN, firewall
|
||||||
|
│ ├── geoip-blocking.md # Geographic IP blocking setup
|
||||||
│ ├── services.md # Self-hosted services
|
│ ├── services.md # Self-hosted services
|
||||||
│ ├── voice-assistant.md # AI voice setup with Piper TTS
|
│ ├── voice-assistant.md # AI voice setup with Piper TTS
|
||||||
│ └── troubleshooting.md # Solutions & fixes
|
│ └── troubleshooting/ # Solutions & troubleshooting guides
|
||||||
├── config/ # Configurations & scripts
|
├── config/ # Configurations & scripts
|
||||||
│ ├── docker/gitea/ # Gitea container setup
|
│ ├── docker/gitea/ # Gitea container setup
|
||||||
|
│ ├── docker/jellyfin/ # Jellyfin media server setup
|
||||||
|
│ ├── docker/nextcloud/ # Nextcloud config (tested but disabled)
|
||||||
|
│ ├── copyparty/ # Copyparty file server configuration
|
||||||
│ ├── nginx/ # Reverse proxy configs
|
│ ├── nginx/ # Reverse proxy configs
|
||||||
|
│ ├── www/ # Landing page HTML
|
||||||
│ └── scripts/ # Utility scripts
|
│ └── scripts/ # Utility scripts
|
||||||
├── voice-server/ # AI voice assistant server
|
├── voice-server/ # AI voice assistant server
|
||||||
│ ├── src/voice_server/ # FastAPI application
|
│ ├── src/voice_server/ # FastAPI application
|
||||||
@@ -70,14 +86,16 @@ homelab/
|
|||||||
### Documentation Files
|
### Documentation Files
|
||||||
- **[docs/system-setup.md](docs/system-setup.md)** - Complete Arch Linux installation, TTY config, desktop setup
|
- **[docs/system-setup.md](docs/system-setup.md)** - Complete Arch Linux installation, TTY config, desktop setup
|
||||||
- **[docs/network-security.md](docs/network-security.md)** - SSH hardening, DuckDNS, WireGuard VPN, firewall setup
|
- **[docs/network-security.md](docs/network-security.md)** - SSH hardening, DuckDNS, WireGuard VPN, firewall setup
|
||||||
|
- **[docs/geoip-blocking.md](docs/geoip-blocking.md)** - Geographic IP blocking with geoip-shell whitelist
|
||||||
- **[docs/services.md](docs/services.md)** - Self-hosted services: Git hosting, cloud storage, media server
|
- **[docs/services.md](docs/services.md)** - Self-hosted services: Git hosting, cloud storage, media server
|
||||||
- **[docs/voice-assistant.md](docs/voice-assistant.md)** - AI voice assistant setup with Piper TTS and FastAPI
|
- **[docs/voice-assistant.md](docs/voice-assistant.md)** - AI voice assistant setup with Piper TTS and FastAPI
|
||||||
- **[TODO.md](TODO.md)** - Centralized task list with progress tracking by category
|
- **[TODO.md](TODO.md)** - Centralized task list with progress tracking by category
|
||||||
- **[docs/troubleshooting.md](docs/troubleshooting.md)** - Hardware issues, software problems, and solutions
|
- **[docs/troubleshooting/](docs/troubleshooting/)** - Hardware issues, software problems, and solutions
|
||||||
|
- **[docs/troubleshooting/webdav-copyparty.md](docs/troubleshooting/webdav-copyparty.md)** - WebDAV troubleshooting guide
|
||||||
|
|
||||||
### Current Configuration
|
### Current Configuration
|
||||||
- **System**: Arch Linux with XFCE desktop, ter-124b TTY font, Colemak layout
|
- **System**: Arch Linux with XFCE desktop, ter-124b TTY font, Colemak layout
|
||||||
- **Network**: Static IP (192.168.0.100), SSH port 2222, DuckDNS (ak-homelab.duckdns.org)
|
- **Network**: Static IP (192.168.0.100), SSH port 2222, DuckDNS (ak-homelab.duckdns.org)
|
||||||
- **Services**: Nginx reverse proxy, Gitea Git server (Docker), AI voice assistant (Piper TTS)
|
- **Services**: Nginx reverse proxy, Gitea Git server, Copyparty file server with WebDAV, Jellyfin media server, AI voice assistant
|
||||||
- **Security**: SSH hardened, firewall planned, SSL certificates pending
|
- **Security**: SSH hardened, SSL certificates active, WebDAV authentication enabled, geographic IP blocking (geoip-shell whitelist)
|
||||||
- **Development**: yadm dotfiles, tmux with temperature monitoring, zsh with proper history
|
- **Development**: yadm dotfiles, tmux with temperature monitoring, zsh with proper history
|
||||||
|
|||||||
41
TODO.md
41
TODO.md
@@ -5,9 +5,25 @@
|
|||||||
- [x] SSH security hardening *(documented in network-security.md)*
|
- [x] SSH security hardening *(documented in network-security.md)*
|
||||||
- [x] Figure out why laptop IP changes: Different eth ports have different MAC?
|
- [x] Figure out why laptop IP changes: Different eth ports have different MAC?
|
||||||
- [x] Router port forwarding configuration
|
- [x] Router port forwarding configuration
|
||||||
|
- [x] !!! Set up geoblocking for SSH. Rest of SSH hardening already done.
|
||||||
|
- [ ] !!! Modify syncthing to sync the NAS folders where appropriate (e.g. Logseq)
|
||||||
|
- [ ] Dockerize everything and use symlinks for dockerfiles (tired of constantly copying stuff over)
|
||||||
|
- [ ] !!! IMPORTANT: Run setup scripts made by security reviewer agent
|
||||||
|
- [ ] Ran out of AI quota mid-security review so continue where we left off. Some scripts created but it's not
|
||||||
|
complete yet
|
||||||
|
- [ ] Some logs saved to ~/audit
|
||||||
|
- [ ] ENCRYPTED FOLDER idea:
|
||||||
|
- Use tomb to create an encrypted vault e.g. /mnt/nas/nas_encrypted
|
||||||
|
- Have a local folder that's empty e.g. <something>/nas_plain
|
||||||
|
- Use tomb to unlock and mount /mnt/nas/nas_encrypted to <something>/nas_plain
|
||||||
|
- Jellyfin is set up to look at nas_plain
|
||||||
|
- When locked: Jellyfin sees empty folder
|
||||||
|
- When unlocked: Jellyfin has access
|
||||||
|
- TO TEST: What about preview pictures etc. within Jellyfin? Adult content may still be visible
|
||||||
- [ ] WireGuard VPN server configuration
|
- [ ] WireGuard VPN server configuration
|
||||||
- [ ] UFW firewall setup and rules
|
- [ ] UFW firewall setup and rules
|
||||||
- [ ] fail2ban for intrusion prevention
|
- [ ] fail2ban for intrusion prevention
|
||||||
|
- [x] VNC setup for remote desktop access *(TigerVNC installed, enabled temporarily via SSH when needed)*
|
||||||
|
|
||||||
## Git & Development
|
## Git & Development
|
||||||
- [x] Gitea Docker container setup *(completed - running on port 3000)*
|
- [x] Gitea Docker container setup *(completed - running on port 3000)*
|
||||||
@@ -52,7 +68,7 @@ Lower priority - mostly using SSH or TTY anyways
|
|||||||
- [ ] Copy any media files from other devices
|
- [ ] Copy any media files from other devices
|
||||||
|
|
||||||
## Music Collection Management
|
## Music Collection Management
|
||||||
- [ ] Extract playlists from YouTube Music and SoundCloud
|
- [ ] Extract playlists from YouTube Music and SoundCloud
|
||||||
- Store metadata (author, song title) in plaintext format
|
- Store metadata (author, song title) in plaintext format
|
||||||
- Tools to consider: ytmusicapi (YouTube Music), scdl (SoundCloud), Google Takeout
|
- Tools to consider: ytmusicapi (YouTube Music), scdl (SoundCloud), Google Takeout
|
||||||
- Output formats: CSV, JSON, M3U with metadata, plain text lists
|
- Output formats: CSV, JSON, M3U with metadata, plain text lists
|
||||||
@@ -73,16 +89,27 @@ Lower priority - mostly using SSH or TTY anyways
|
|||||||
- [x] Docker container setup with hardware acceleration
|
- [x] Docker container setup with hardware acceleration
|
||||||
- [x] Nginx reverse proxy integration at /media/ path
|
- [x] Nginx reverse proxy integration at /media/ path
|
||||||
- [x] Shared media folders with Copyparty (Music, Videos, shared)
|
- [x] Shared media folders with Copyparty (Music, Videos, shared)
|
||||||
|
- [ ] Set up self-hosted chat server (Matrix or Mattermost)
|
||||||
|
- [ ] Install monitoring and management tools *(in progress)*
|
||||||
|
- [x] Portainer (Docker management with built-in auth)
|
||||||
|
- [x] Glances (system monitoring with nginx basic auth)
|
||||||
|
- [ ] lazydocker (terminal Docker management)
|
||||||
|
- [ ] Configure nginx basic auth for Glances endpoint
|
||||||
|
- [ ] Update nginx reverse proxy config for new admin services
|
||||||
|
- [ ] Update homelab landing page with new admin service links
|
||||||
- [ ] Set up Nextcloud for advanced file synchronization features
|
- [ ] Set up Nextcloud for advanced file synchronization features
|
||||||
- Copyparty covers basic file sharing needs
|
- Copyparty covers basic file sharing needs
|
||||||
- [ ] Implement monitoring stack (Prometheus/Grafana)
|
|
||||||
- Also consider alternatives, make setup simple and FOSS only
|
|
||||||
- [x] Set up reverse proxy with SSL certificates *(completed - HTTPS working with auto-renewal)*
|
- [x] Set up reverse proxy with SSL certificates *(completed - HTTPS working with auto-renewal)*
|
||||||
- [ ] Make sure all services are dockerized unless we have a good reason not to
|
- [x] Make sure all services are dockerized unless we have a good reason not to
|
||||||
- Gitea: ✅ Docker
|
- Gitea: ✅ Docker
|
||||||
- Jellyfin: ✅ Docker
|
- Jellyfin: ✅ Docker
|
||||||
- Copyparty: ❌ systemd service (consider dockerizing)
|
- qBittorrent: ✅ Docker
|
||||||
- Nginx: ❌ system package (fine as-is for reverse proxy)
|
- Portainer: ✅ Docker
|
||||||
|
- Copyparty: ✅ Docker
|
||||||
|
- Glances: ❌ systemd (needs full system access for accurate monitoring)
|
||||||
|
- Netdata: ❌ systemd (needs full system access for accurate monitoring)
|
||||||
|
- Syncthing: ❌ systemd (Docker permission issues with config directory)
|
||||||
|
- Nginx: ❌ system package (reverse proxy, system integration required)
|
||||||
|
|
||||||
## Hardware & Troubleshooting
|
## Hardware & Troubleshooting
|
||||||
- [ ] Fix bluetooth audio connectivity issues
|
- [ ] Fix bluetooth audio connectivity issues
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
# Copyparty Configuration for Homelab
|
|
||||||
# DEPLOYMENT LOCATION: /home/hoborg/.config/copyparty/copyparty.conf
|
|
||||||
|
|
||||||
[global]
|
|
||||||
# Network settings
|
|
||||||
i: 127.0.0.1
|
|
||||||
p: 8082
|
|
||||||
rp-loc: /files
|
|
||||||
|
|
||||||
# Reverse proxy configuration
|
|
||||||
rproxy: -1
|
|
||||||
|
|
||||||
# Security and features
|
|
||||||
usernames
|
|
||||||
chpw
|
|
||||||
chpw-db: /home/hoborg/.config/copyparty/passwords.json
|
|
||||||
e2dsa
|
|
||||||
dedup
|
|
||||||
dotpart
|
|
||||||
|
|
||||||
# Upload settings
|
|
||||||
u2ts: c
|
|
||||||
chmod-f: 644
|
|
||||||
chmod-d: 755
|
|
||||||
|
|
||||||
# Server name
|
|
||||||
name: homelab-files
|
|
||||||
|
|
||||||
[accounts]
|
|
||||||
guest: SecurePass2024!
|
|
||||||
hoborg: AdminPass2024!
|
|
||||||
|
|
||||||
[/shared]
|
|
||||||
/home/hoborg/shared
|
|
||||||
accs:
|
|
||||||
rw: guest
|
|
||||||
rwmd: hoborg
|
|
||||||
|
|
||||||
[/documents]
|
|
||||||
/home/hoborg/Documents
|
|
||||||
accs:
|
|
||||||
rwmd: hoborg
|
|
||||||
|
|
||||||
[/music]
|
|
||||||
/home/hoborg/Music
|
|
||||||
accs:
|
|
||||||
rw: guest
|
|
||||||
rwmd: hoborg
|
|
||||||
|
|
||||||
[/videos]
|
|
||||||
/home/hoborg/Videos
|
|
||||||
accs:
|
|
||||||
rw: guest
|
|
||||||
rwmd: hoborg
|
|
||||||
|
|
||||||
[/private]
|
|
||||||
/home/hoborg/private
|
|
||||||
accs:
|
|
||||||
rwmd: hoborg
|
|
||||||
|
|
||||||
[/pictures]
|
|
||||||
/home/hoborg/Pictures
|
|
||||||
accs:
|
|
||||||
rw: guest
|
|
||||||
rwmd: hoborg
|
|
||||||
90
config/docker/copyparty/docker-compose.yml
Normal file
90
config/docker/copyparty/docker-compose.yml
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
# Copyparty Docker Compose Configuration
|
||||||
|
# Deploy with: sudo mkdir -p /opt/docker/copyparty && sudo cp config/docker/copyparty/docker-compose.yml /opt/docker/copyparty/
|
||||||
|
# Start with: cd /opt/docker/copyparty && sudo docker-compose up -d
|
||||||
|
|
||||||
|
# COPYPARTY CONFIGURATION
|
||||||
|
# - File server with upload/download capabilities
|
||||||
|
# - WebDAV support for mobile and desktop clients
|
||||||
|
# - Bound to localhost only (reverse proxy required)
|
||||||
|
# - Configuration stored in repo at config/copyparty/copyparty.conf
|
||||||
|
|
||||||
|
services:
|
||||||
|
copyparty:
|
||||||
|
image: copyparty/ac:latest
|
||||||
|
container_name: copyparty
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Europe/Budapest
|
||||||
|
|
||||||
|
# Network - bind to localhost for security
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:8082:3923" # Web UI (reverse proxy only)
|
||||||
|
|
||||||
|
# Volume mounts
|
||||||
|
volumes:
|
||||||
|
# Password database
|
||||||
|
- /home/hoborg/.config/copyparty/passwords.json:/home/hoborg/.config/copyparty/passwords.json
|
||||||
|
# NAS storage volumes (mapped to container paths)
|
||||||
|
- /mnt/nas/shared:/w/shared:rw
|
||||||
|
- /mnt/nas/documents:/w/documents:rw
|
||||||
|
- /mnt/nas/music:/w/music:rw
|
||||||
|
- /mnt/nas/videos:/w/videos:rw
|
||||||
|
- /mnt/nas/private:/w/private:rw
|
||||||
|
- /mnt/nas/pictures:/w/pictures:rw
|
||||||
|
- /mnt/nas/installers:/w/installers:rw
|
||||||
|
- /mnt/nas/torrent:/w/torrent:rw
|
||||||
|
|
||||||
|
# Command with inline volume configuration using -v flag
|
||||||
|
command:
|
||||||
|
- --rp-loc=/files
|
||||||
|
- --name=homelab-files
|
||||||
|
- --usernames
|
||||||
|
- --chpw
|
||||||
|
- --chpw-db=/home/hoborg/.config/copyparty/passwords.json
|
||||||
|
- --xff-src=172.0.0.0/8
|
||||||
|
- --rproxy=-1
|
||||||
|
- -e2dsa
|
||||||
|
- --dedup
|
||||||
|
- --dotpart
|
||||||
|
- -a
|
||||||
|
- hoborg:AdminPass2024!
|
||||||
|
- -a
|
||||||
|
- guest:SecurePass2024!
|
||||||
|
- -v
|
||||||
|
- /w/shared:shared:r:rw,guest:rwmd,hoborg
|
||||||
|
- -v
|
||||||
|
- /w/documents:documents:rwmd,hoborg
|
||||||
|
- -v
|
||||||
|
- /w/music:music:r:rw,guest:rwmd,hoborg
|
||||||
|
- -v
|
||||||
|
- /w/videos:videos:r:rw,guest:rwmd,hoborg
|
||||||
|
- -v
|
||||||
|
- /w/private:private:rwmd,hoborg
|
||||||
|
- -v
|
||||||
|
- /w/pictures:pictures:r:rw,guest:rwmd,hoborg
|
||||||
|
- -v
|
||||||
|
- /w/installers:installers:r:rw,guest:rwmd,hoborg
|
||||||
|
- -v
|
||||||
|
- /w/torrent:torrent:r:rw,guest:rwmd,hoborg
|
||||||
|
|
||||||
|
# Resource limits
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '1.0'
|
||||||
|
memory: 1G
|
||||||
|
reservations:
|
||||||
|
cpus: '0.25'
|
||||||
|
memory: 256M
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3923/files/"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
7
config/docker/daemon.json
Normal file
7
config/docker/daemon.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"log-driver": "json-file",
|
||||||
|
"log-opts": {
|
||||||
|
"max-size": "100m",
|
||||||
|
"max-file": "3"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,11 @@
|
|||||||
# Create data directory: sudo mkdir -p /opt/docker/gitea/data
|
# Create data directory: sudo mkdir -p /opt/docker/gitea/data
|
||||||
# Set permissions: sudo chown -R hoborg:hoborg /opt/docker/gitea
|
# Set permissions: sudo chown -R hoborg:hoborg /opt/docker/gitea
|
||||||
|
|
||||||
|
# WORKING CONFIGURATION - Balanced security and functionality
|
||||||
|
# - Ports bound to localhost only
|
||||||
|
# - Non-root user (1000:1000)
|
||||||
|
# - Removed read-only filesystem (causes s6-svscan issues)
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
gitea:
|
gitea:
|
||||||
external: false
|
external: false
|
||||||
@@ -14,13 +19,35 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- USER_UID=1000
|
- USER_UID=1000
|
||||||
- USER_GID=1000
|
- USER_GID=1000
|
||||||
restart: always
|
- GITEA__server__ROOT_URL=https://ak-homelab.duckdns.org/gitea/
|
||||||
|
- GITEA__server__SSH_PORT=2223
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# Let Gitea container handle user switching internally (USER_UID/USER_GID)
|
||||||
|
# DO NOT set user: directive - breaks s6-overlay init system
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
- gitea
|
- gitea
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/data
|
- ./data:/data
|
||||||
- /etc/timezone:/etc/timezone:ro
|
- /etc/timezone:/etc/timezone:ro
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
|
||||||
|
# Bind ports to localhost only for reverse proxy
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "127.0.0.1:3000:3000"
|
||||||
- "2223:22"
|
- "2223:22"
|
||||||
|
|
||||||
|
# Reasonable resource limits
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 1G
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:3000/api/healthz"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|||||||
@@ -2,6 +2,13 @@
|
|||||||
# Deploy with: sudo mkdir -p /opt/docker/jellyfin && sudo cp config/docker/jellyfin/docker-compose.yml /opt/docker/jellyfin/
|
# Deploy with: sudo mkdir -p /opt/docker/jellyfin && sudo cp config/docker/jellyfin/docker-compose.yml /opt/docker/jellyfin/
|
||||||
# Start with: cd /opt/docker/jellyfin && sudo docker-compose up -d
|
# Start with: cd /opt/docker/jellyfin && sudo docker-compose up -d
|
||||||
|
|
||||||
|
# HARDENED CONFIGURATION - Updated for security
|
||||||
|
# - Non-root user maintained (1000:1000)
|
||||||
|
# - Security options added
|
||||||
|
# - Resource limits enhanced
|
||||||
|
# - Health check added
|
||||||
|
# - Network mode kept for hardware acceleration (acceptable risk)
|
||||||
|
|
||||||
services:
|
services:
|
||||||
jellyfin:
|
jellyfin:
|
||||||
image: jellyfin/jellyfin:latest
|
image: jellyfin/jellyfin:latest
|
||||||
@@ -16,8 +23,24 @@ services:
|
|||||||
- JELLYFIN_PublishedServerUrl=https://ak-homelab.duckdns.org/media
|
- JELLYFIN_PublishedServerUrl=https://ak-homelab.duckdns.org/media
|
||||||
|
|
||||||
# Network mode for better performance and hardware acceleration
|
# Network mode for better performance and hardware acceleration
|
||||||
|
# NOTE: Host networking is required for GPU hardware acceleration
|
||||||
|
# This is an acceptable security trade-off for media performance
|
||||||
network_mode: host
|
network_mode: host
|
||||||
|
|
||||||
|
# Security hardening
|
||||||
|
read_only: false # Jellyfin needs write access for transcoding
|
||||||
|
tmpfs:
|
||||||
|
- /tmp:noexec,nosuid,size=2G
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
cap_add:
|
||||||
|
- CHOWN
|
||||||
|
- SETUID
|
||||||
|
- SETGID
|
||||||
|
- DAC_OVERRIDE
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
|
||||||
# Volume mounts - using same folders as Copyparty
|
# Volume mounts - using same folders as Copyparty
|
||||||
volumes:
|
volumes:
|
||||||
# Jellyfin configuration and data
|
# Jellyfin configuration and data
|
||||||
@@ -25,11 +48,11 @@ services:
|
|||||||
- /opt/docker/jellyfin/cache:/cache
|
- /opt/docker/jellyfin/cache:/cache
|
||||||
|
|
||||||
# Media folders (shared with Copyparty)
|
# Media folders (shared with Copyparty)
|
||||||
- /home/hoborg/Music:/media/music:ro
|
- /mnt/nas/music:/media/music:ro
|
||||||
- /home/hoborg/Videos:/media/videos:ro
|
- /mnt/nas/videos:/media/videos:ro
|
||||||
- /home/hoborg/Pictures:/media/pictures:ro
|
- /mnt/nas/pictures:/media/pictures:ro
|
||||||
- /home/hoborg/shared:/media/shared:ro
|
- /mnt/nas/shared:/media/shared:ro
|
||||||
- /home/hoborg/private:/media/private:ro
|
- /mnt/nas/private:/media/private:ro
|
||||||
|
|
||||||
# Additional media folders if they exist
|
# Additional media folders if they exist
|
||||||
# - /home/hoborg/Movies:/media/movies:ro
|
# - /home/hoborg/Movies:/media/movies:ro
|
||||||
@@ -39,15 +62,20 @@ services:
|
|||||||
devices:
|
devices:
|
||||||
- /dev/dri:/dev/dri
|
- /dev/dri:/dev/dri
|
||||||
|
|
||||||
# Memory limits for container stability
|
# Enhanced resource limits
|
||||||
deploy:
|
deploy:
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
|
cpus: '2.0'
|
||||||
memory: 2G
|
memory: 2G
|
||||||
reservations:
|
reservations:
|
||||||
|
cpus: '0.5'
|
||||||
memory: 512M
|
memory: 512M
|
||||||
|
|
||||||
# Optional: Create a custom network if not using host networking
|
# Health check
|
||||||
# networks:
|
healthcheck:
|
||||||
# jellyfin:
|
test: ["CMD", "curl", "-f", "http://localhost:8096/health"]
|
||||||
# driver: bridge
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 60s
|
||||||
|
|||||||
50
config/docker/portainer/docker-compose.yml
Normal file
50
config/docker/portainer/docker-compose.yml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# Portainer Docker Compose Configuration
|
||||||
|
# Deploy with: sudo mkdir -p /opt/docker/portainer && sudo cp config/docker/portainer/docker-compose.yml /opt/docker/portainer/
|
||||||
|
# Start with: cd /opt/docker/portainer && sudo docker-compose up -d
|
||||||
|
|
||||||
|
# PORTAINER CONFIGURATION
|
||||||
|
# - Web UI for Docker container management
|
||||||
|
# - Bound to localhost only (reverse proxy recommended)
|
||||||
|
# - Persistent data storage in named volume
|
||||||
|
|
||||||
|
services:
|
||||||
|
portainer:
|
||||||
|
image: portainer/portainer-ce:latest
|
||||||
|
container_name: portainer
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
environment:
|
||||||
|
- TZ=Europe/Budapest
|
||||||
|
|
||||||
|
# Network - bind to localhost for security
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:9000:9000" # Web UI (reverse proxy only)
|
||||||
|
|
||||||
|
# Volume mounts
|
||||||
|
volumes:
|
||||||
|
# Docker socket for container management
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
# Portainer data
|
||||||
|
- portainer_data:/data
|
||||||
|
|
||||||
|
# Resource limits
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '0.5'
|
||||||
|
memory: 512M
|
||||||
|
reservations:
|
||||||
|
cpus: '0.1'
|
||||||
|
memory: 128M
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9000/api/status"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
portainer_data:
|
||||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
|||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
(N) 2025-09-10T12:25:50 - qBittorrent v5.1.2 started. Process ID: 153
|
||||||
|
(N) 2025-09-10T12:25:50 - Using config directory: /config/qBittorrent
|
||||||
|
(N) 2025-09-10T12:25:50 - Trying to listen on the following list of IP addresses: "0.0.0.0:6881,[::]:6881"
|
||||||
|
(I) 2025-09-10T12:25:50 - Peer ID: "-qB5120-"
|
||||||
|
(I) 2025-09-10T12:25:50 - HTTP User-Agent: "qBittorrent/5.1.2"
|
||||||
|
(I) 2025-09-10T12:25:50 - Distributed Hash Table (DHT) support: ON
|
||||||
|
(I) 2025-09-10T12:25:50 - Local Peer Discovery support: ON
|
||||||
|
(I) 2025-09-10T12:25:50 - Peer Exchange (PeX) support: ON
|
||||||
|
(I) 2025-09-10T12:25:50 - Anonymous mode: OFF
|
||||||
|
(I) 2025-09-10T12:25:50 - Encryption support: ON
|
||||||
|
(I) 2025-09-10T12:25:50 - Successfully listening on IP. IP: "127.0.0.1". Port: "TCP/6881"
|
||||||
|
(I) 2025-09-10T12:25:50 - Successfully listening on IP. IP: "127.0.0.1". Port: "UTP/6881"
|
||||||
|
(I) 2025-09-10T12:25:50 - Successfully listening on IP. IP: "172.19.0.2". Port: "TCP/6881"
|
||||||
|
(I) 2025-09-10T12:25:50 - Successfully listening on IP. IP: "172.19.0.2". Port: "UTP/6881"
|
||||||
|
(I) 2025-09-10T12:25:50 - Successfully listening on IP. IP: "::1". Port: "TCP/6881"
|
||||||
|
(I) 2025-09-10T12:25:50 - Successfully listening on IP. IP: "::1". Port: "UTP/6881"
|
||||||
|
(W) 2025-09-10T12:25:50 - Couldn't load IP geolocation database. Reason: No such file or directory
|
||||||
|
(N) 2025-09-10T12:25:50 - Using built-in WebUI.
|
||||||
|
(W) 2025-09-10T12:25:50 - Couldn't load WebUI translation for selected locale (C).
|
||||||
|
(N) 2025-09-10T12:25:50 - WebUI: Now listening on IP: *, port: 8080
|
||||||
|
(I) 2025-09-10T12:25:50 - Detected external IP. IP: "31.46.51.110"
|
||||||
|
(I) 2025-09-10T12:25:50 - IP geolocation database loaded. Type: DBIP-Country-Lite. Build time: Mon Sep 1 03:19:30 2025.
|
||||||
|
(I) 2025-09-10T12:25:50 - Successfully updated IP geolocation database.
|
||||||
|
(W) 2025-09-10T12:27:31 - WebAPI login failure. Reason: invalid credentials, attempt count: 1, IP: ::ffff:172.19.0.1, username: admin
|
||||||
|
(W) 2025-09-10T12:27:37 - WebAPI login failure. Reason: invalid credentials, attempt count: 2, IP: ::ffff:172.19.0.1, username: admin
|
||||||
|
(W) 2025-09-10T12:28:34 - WebAPI login failure. Reason: invalid credentials, attempt count: 3, IP: ::ffff:172.19.0.1, username: admin
|
||||||
|
(N) 2025-09-10T12:29:02 - WebAPI login success. IP: ::ffff:172.19.0.1
|
||||||
|
(N) 2025-09-10T12:30:39 - WebUI translation for selected locale (en) has been successfully loaded.
|
||||||
|
(N) 2025-09-10T12:31:08 - WebAPI login success. IP: ::ffff:172.19.0.1
|
||||||
|
(N) 2025-09-10T23:41:28 - qBittorrent termination initiated
|
||||||
|
(N) 2025-09-10T23:41:28 - Saving resume data completed.
|
||||||
|
(N) 2025-09-10T23:41:28 - BitTorrent session successfully finished.
|
||||||
|
(N) 2025-09-10T23:41:28 - qBittorrent is now ready to exit
|
||||||
|
(N) 2025-09-10T23:43:04 - qBittorrent v5.1.2 started. Process ID: 148
|
||||||
|
(N) 2025-09-10T23:43:04 - Using config directory: /config/qBittorrent
|
||||||
|
(N) 2025-09-10T23:43:04 - Trying to listen on the following list of IP addresses: "0.0.0.0:6881,[::]:6881"
|
||||||
|
(I) 2025-09-10T23:43:04 - Peer ID: "-qB5120-"
|
||||||
|
(I) 2025-09-10T23:43:04 - HTTP User-Agent: "qBittorrent/5.1.2"
|
||||||
|
(I) 2025-09-10T23:43:04 - Distributed Hash Table (DHT) support: ON
|
||||||
|
(I) 2025-09-10T23:43:04 - Local Peer Discovery support: ON
|
||||||
|
(I) 2025-09-10T23:43:04 - Peer Exchange (PeX) support: ON
|
||||||
|
(I) 2025-09-10T23:43:04 - Anonymous mode: OFF
|
||||||
|
(I) 2025-09-10T23:43:04 - Encryption support: ON
|
||||||
|
(I) 2025-09-10T23:43:04 - IP geolocation database loaded. Type: DBIP-Country-Lite. Build time: Mon Sep 1 03:19:30 2025.
|
||||||
|
(N) 2025-09-10T23:43:04 - Using built-in WebUI.
|
||||||
|
(N) 2025-09-10T23:43:04 - WebUI translation for selected locale (en) has been successfully loaded.
|
||||||
|
(N) 2025-09-10T23:43:04 - WebUI: Now listening on IP: *, port: 8080
|
||||||
|
(I) 2025-09-10T23:43:04 - Successfully listening on IP. IP: "127.0.0.1". Port: "TCP/6881"
|
||||||
|
(I) 2025-09-10T23:43:04 - Successfully listening on IP. IP: "127.0.0.1". Port: "UTP/6881"
|
||||||
|
(I) 2025-09-10T23:43:04 - Successfully listening on IP. IP: "172.19.0.2". Port: "TCP/6881"
|
||||||
|
(I) 2025-09-10T23:43:04 - Successfully listening on IP. IP: "172.19.0.2". Port: "UTP/6881"
|
||||||
|
(I) 2025-09-10T23:43:04 - Successfully listening on IP. IP: "::1". Port: "TCP/6881"
|
||||||
|
(I) 2025-09-10T23:43:04 - Successfully listening on IP. IP: "::1". Port: "UTP/6881"
|
||||||
|
(I) 2025-09-10T23:43:07 - Detected external IP. IP: "31.46.51.110"
|
||||||
|
(N) 2025-09-12T09:27:37 - qBittorrent termination initiated
|
||||||
|
(N) 2025-09-12T09:27:37 - Saving resume data completed.
|
||||||
|
(N) 2025-09-12T09:27:37 - BitTorrent session successfully finished.
|
||||||
|
(N) 2025-09-12T09:27:37 - qBittorrent is now ready to exit
|
||||||
|
(N) 2025-09-12T09:29:21 - qBittorrent v5.1.2 started. Process ID: 146
|
||||||
|
(N) 2025-09-12T09:29:21 - Using config directory: /config/qBittorrent
|
||||||
|
(N) 2025-09-12T09:29:21 - Trying to listen on the following list of IP addresses: "0.0.0.0:6881,[::]:6881"
|
||||||
|
(I) 2025-09-12T09:29:21 - Peer ID: "-qB5120-"
|
||||||
|
(I) 2025-09-12T09:29:21 - HTTP User-Agent: "qBittorrent/5.1.2"
|
||||||
|
(I) 2025-09-12T09:29:21 - Distributed Hash Table (DHT) support: ON
|
||||||
|
(I) 2025-09-12T09:29:21 - Local Peer Discovery support: ON
|
||||||
|
(I) 2025-09-12T09:29:21 - Peer Exchange (PeX) support: ON
|
||||||
|
(I) 2025-09-12T09:29:21 - Anonymous mode: OFF
|
||||||
|
(I) 2025-09-12T09:29:21 - Encryption support: ON
|
||||||
|
(I) 2025-09-12T09:29:21 - Successfully listening on IP. IP: "127.0.0.1". Port: "TCP/6881"
|
||||||
|
(I) 2025-09-12T09:29:21 - Successfully listening on IP. IP: "127.0.0.1". Port: "UTP/6881"
|
||||||
|
(I) 2025-09-12T09:29:21 - Successfully listening on IP. IP: "172.19.0.2". Port: "TCP/6881"
|
||||||
|
(I) 2025-09-12T09:29:21 - Successfully listening on IP. IP: "172.19.0.2". Port: "UTP/6881"
|
||||||
|
(I) 2025-09-12T09:29:21 - Successfully listening on IP. IP: "::1". Port: "TCP/6881"
|
||||||
|
(I) 2025-09-12T09:29:21 - Successfully listening on IP. IP: "::1". Port: "UTP/6881"
|
||||||
|
(I) 2025-09-12T09:29:21 - IP geolocation database loaded. Type: DBIP-Country-Lite. Build time: Mon Sep 1 03:19:30 2025.
|
||||||
|
(N) 2025-09-12T09:29:21 - Using built-in WebUI.
|
||||||
|
(N) 2025-09-12T09:29:21 - WebUI translation for selected locale (en) has been successfully loaded.
|
||||||
|
(N) 2025-09-12T09:29:21 - WebUI: Now listening on IP: *, port: 8080
|
||||||
|
(I) 2025-09-12T09:29:23 - Detected external IP. IP: "31.46.51.110"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[Stats]
|
||||||
|
AllStats=@Variant(\0\0\0\x1c\0\0\0\x2\0\0\0\x12\0\x41\0l\0l\0t\0i\0m\0\x65\0U\0L\0\0\0\x4\0\0\0\0\0\x12+H\0\0\0\x12\0\x41\0l\0l\0t\0i\0m\0\x65\0\x44\0L\0\0\0\x4\0\0\0\0\0\xf#()
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
[Application]
|
||||||
|
FileLogger\Age=1
|
||||||
|
FileLogger\AgeType=1
|
||||||
|
FileLogger\Backup=true
|
||||||
|
FileLogger\DeleteOld=true
|
||||||
|
FileLogger\Enabled=true
|
||||||
|
FileLogger\MaxSizeBytes=66560
|
||||||
|
FileLogger\Path=/config/qBittorrent/logs
|
||||||
|
|
||||||
|
[AutoRun]
|
||||||
|
enabled=false
|
||||||
|
program=
|
||||||
|
|
||||||
|
[BitTorrent]
|
||||||
|
Session\AddTorrentStopped=false
|
||||||
|
Session\DefaultSavePath=/downloads/
|
||||||
|
Session\ExcludedFileNames=
|
||||||
|
Session\Port=6881
|
||||||
|
Session\QueueingSystemEnabled=true
|
||||||
|
Session\SSL\Port=6913
|
||||||
|
Session\ShareLimitAction=Stop
|
||||||
|
Session\TempPath=/downloads/incomplete/
|
||||||
|
|
||||||
|
[Core]
|
||||||
|
AutoDeleteAddedTorrentFile=Never
|
||||||
|
|
||||||
|
[LegalNotice]
|
||||||
|
Accepted=true
|
||||||
|
|
||||||
|
[Meta]
|
||||||
|
MigrationVersion=8
|
||||||
|
|
||||||
|
[Network]
|
||||||
|
Cookies=@Invalid()
|
||||||
|
PortForwardingEnabled=false
|
||||||
|
Proxy\HostnameLookupEnabled=false
|
||||||
|
Proxy\Profiles\BitTorrent=true
|
||||||
|
Proxy\Profiles\Misc=true
|
||||||
|
Proxy\Profiles\RSS=true
|
||||||
|
|
||||||
|
[Preferences]
|
||||||
|
Connection\PortRangeMin=6881
|
||||||
|
Connection\UPnP=false
|
||||||
|
Downloads\SavePath=/downloads/
|
||||||
|
Downloads\TempPath=/downloads/incomplete/
|
||||||
|
General\Locale=en
|
||||||
|
MailNotification\req_auth=true
|
||||||
|
WebUI\Address=*
|
||||||
|
WebUI\Password_PBKDF2="@ByteArray(ZKvbJkSupyOioYkDDVrMYA==:d65EaPafxao/dcmEXRsFaMgRcGBoCSxTcGZiY/LCa48TUErDQC03rNemvsCClKqDbi4ec7idnMkHd6Ila+4vxg==)"
|
||||||
|
WebUI\ServerDomains=*
|
||||||
|
WebUI\Username=hoborg
|
||||||
|
|
||||||
|
[RSS]
|
||||||
|
AutoDownloader\DownloadRepacks=true
|
||||||
|
AutoDownloader\SmartEpisodeFilter=s(\\d+)e(\\d+), (\\d+)x(\\d+), "(\\d{4}[.\\-]\\d{1,2}[.\\-]\\d{1,2})", "(\\d{1,2}[.\\-]\\d{1,2}[.\\-]\\d{4})"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
{
|
||||||
|
}
|
||||||
71
config/docker/qbittorrent/docker-compose.yml
Normal file
71
config/docker/qbittorrent/docker-compose.yml
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# DEPLOYMENT LOCATION: /opt/docker/qbittorrent/docker-compose.yml
|
||||||
|
# Deploy with: sudo mkdir -p /opt/docker/qbittorrent && sudo cp config/docker/qbittorrent/docker-compose.yml /opt/docker/qbittorrent/
|
||||||
|
# Start with: cd /opt/docker/qbittorrent && sudo docker-compose up -d
|
||||||
|
|
||||||
|
# HARDENED CONFIGURATION - Updated for security
|
||||||
|
# - Torrent ports bound to localhost (VPN/reverse tunnel recommended for external access)
|
||||||
|
# - Non-root user (1000:1000)
|
||||||
|
# - Security options added
|
||||||
|
# - Resource limits maintained
|
||||||
|
# - Read-only config volume where possible
|
||||||
|
|
||||||
|
services:
|
||||||
|
qbittorrent:
|
||||||
|
image: linuxserver/qbittorrent:latest
|
||||||
|
container_name: qbittorrent
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# User and group IDs to match host user (hoborg)
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Europe/Prague
|
||||||
|
- WEBUI_PORT=8080
|
||||||
|
|
||||||
|
# Security hardening
|
||||||
|
user: "1000:1000"
|
||||||
|
read_only: false # qBittorrent needs write access for downloads
|
||||||
|
tmpfs:
|
||||||
|
- /tmp:noexec,nosuid,size=100m
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
cap_add:
|
||||||
|
- CHOWN
|
||||||
|
- SETUID
|
||||||
|
- SETGID
|
||||||
|
- NET_BIND_SERVICE # For port binding
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
|
||||||
|
# Network - bind to localhost for security
|
||||||
|
# NOTE: For external torrent access, use VPN or port forwarding
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:8080:8080" # Web UI (reverse proxy only)
|
||||||
|
- "127.0.0.1:6881:6881" # BitTorrent TCP (localhost only)
|
||||||
|
- "127.0.0.1:6881:6881/udp" # BitTorrent UDP (localhost only)
|
||||||
|
|
||||||
|
# Volume mounts
|
||||||
|
volumes:
|
||||||
|
# qBittorrent configuration
|
||||||
|
- /opt/docker/qbittorrent/config:/config
|
||||||
|
|
||||||
|
# Torrent storage on NAS
|
||||||
|
- /mnt/nas/torrent:/downloads
|
||||||
|
|
||||||
|
# Enhanced resource limits
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '1.0'
|
||||||
|
memory: 1G
|
||||||
|
reservations:
|
||||||
|
cpus: '0.25'
|
||||||
|
memory: 256M
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:8080"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
9
config/fail2ban/filter.d/gitea-auth.conf
Normal file
9
config/fail2ban/filter.d/gitea-auth.conf
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Fail2ban filter for Gitea authentication failures
|
||||||
|
# Deploy to: /etc/fail2ban/filter.d/gitea-auth.conf
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
failregex = .*Failed authentication attempt for .* from <HOST>
|
||||||
|
.*Invalid credentials from <HOST>
|
||||||
|
.*Login attempt failed .* from <HOST>
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
17
config/fail2ban/filter.d/nginx-badbots.conf
Normal file
17
config/fail2ban/filter.d/nginx-badbots.conf
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Fail2ban filter for nginx bad bots and scanners
|
||||||
|
# Deploy to: /etc/fail2ban/filter.d/nginx-badbots.conf
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
failregex = ^<HOST> -.*"(GET|POST|HEAD).*HTTP.*" (400|401|403|404|405|406|407|408|409|410|411|412|413|414|415|416|417|500|501|502|503|504|505)
|
||||||
|
^<HOST> -.*".*sqlmap.*"
|
||||||
|
^<HOST> -.*".*nikto.*"
|
||||||
|
^<HOST> -.*".*nmap.*"
|
||||||
|
^<HOST> -.*".*masscan.*"
|
||||||
|
^<HOST> -.*".*nessus.*"
|
||||||
|
^<HOST> -.*".*openvas.*"
|
||||||
|
^<HOST> -.*".*vega.*"
|
||||||
|
^<HOST> -.*".*morfeus.*"
|
||||||
|
^<HOST> -.*".*ZmEu.*"
|
||||||
|
^<HOST> -.*".*Havij.*"
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
10
config/fail2ban/filter.d/sshd-ddos.conf
Normal file
10
config/fail2ban/filter.d/sshd-ddos.conf
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Fail2ban filter for SSH DDoS attacks
|
||||||
|
# Deploy to: /etc/fail2ban/filter.d/sshd-ddos.conf
|
||||||
|
# Catches rapid connection attempts that may overwhelm SSH
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
failregex = ^%(__prefix_line)sConnection from <HOST> port \d+$
|
||||||
|
^%(__prefix_line)sConnection closed by <HOST> port \d+ \[preauth\]$
|
||||||
|
^%(__prefix_line)sDisconnected from <HOST> port \d+ \[preauth\]$
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
98
config/fail2ban/jail.local
Normal file
98
config/fail2ban/jail.local
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# Fail2ban Jail Configuration for Homelab
|
||||||
|
# Deploy to: /etc/fail2ban/jail.local
|
||||||
|
#
|
||||||
|
# Setup commands:
|
||||||
|
# sudo cp config/fail2ban/jail.local /etc/fail2ban/
|
||||||
|
# sudo systemctl restart fail2ban
|
||||||
|
# sudo systemctl enable fail2ban
|
||||||
|
|
||||||
|
[DEFAULT]
|
||||||
|
# Ban settings
|
||||||
|
bantime = 3600
|
||||||
|
findtime = 600
|
||||||
|
maxretry = 5
|
||||||
|
banaction = ufw
|
||||||
|
backend = systemd
|
||||||
|
|
||||||
|
# Ignore local networks and your management IPs
|
||||||
|
ignoreip = 127.0.0.1/8 ::1 192.168.0.0/16 10.0.0.0/8
|
||||||
|
|
||||||
|
# Email notifications (configure if needed)
|
||||||
|
# destemail = your-email@domain.com
|
||||||
|
# sender = fail2ban@homelab
|
||||||
|
# action = %(action_mwl)s
|
||||||
|
|
||||||
|
#
|
||||||
|
# SSH Protection (Critical - Primary attack vector)
|
||||||
|
#
|
||||||
|
[sshd]
|
||||||
|
enabled = true
|
||||||
|
port = 2222
|
||||||
|
filter = sshd
|
||||||
|
logpath = /var/log/auth.log
|
||||||
|
maxretry = 3
|
||||||
|
bantime = 7200
|
||||||
|
findtime = 300
|
||||||
|
|
||||||
|
[sshd-ddos]
|
||||||
|
enabled = true
|
||||||
|
port = 2222
|
||||||
|
filter = sshd-ddos
|
||||||
|
logpath = /var/log/auth.log
|
||||||
|
maxretry = 6
|
||||||
|
bantime = 3600
|
||||||
|
findtime = 60
|
||||||
|
|
||||||
|
#
|
||||||
|
# Web Service Protection
|
||||||
|
#
|
||||||
|
[nginx-http-auth]
|
||||||
|
enabled = true
|
||||||
|
filter = nginx-http-auth
|
||||||
|
logpath = /var/log/nginx/error.log
|
||||||
|
maxretry = 3
|
||||||
|
bantime = 3600
|
||||||
|
|
||||||
|
[nginx-badbots]
|
||||||
|
enabled = true
|
||||||
|
filter = nginx-badbots
|
||||||
|
logpath = /var/log/nginx/access.log
|
||||||
|
maxretry = 2
|
||||||
|
bantime = 86400
|
||||||
|
findtime = 600
|
||||||
|
|
||||||
|
[nginx-noscript]
|
||||||
|
enabled = true
|
||||||
|
filter = nginx-noscript
|
||||||
|
logpath = /var/log/nginx/access.log
|
||||||
|
maxretry = 6
|
||||||
|
bantime = 86400
|
||||||
|
|
||||||
|
[nginx-noproxy]
|
||||||
|
enabled = true
|
||||||
|
filter = nginx-noproxy
|
||||||
|
logpath = /var/log/nginx/access.log
|
||||||
|
maxretry = 2
|
||||||
|
bantime = 86400
|
||||||
|
|
||||||
|
#
|
||||||
|
# Docker/Container Protection
|
||||||
|
#
|
||||||
|
[gitea-auth]
|
||||||
|
enabled = true
|
||||||
|
filter = gitea-auth
|
||||||
|
logpath = /opt/docker/gitea/data/log/gitea.log
|
||||||
|
maxretry = 5
|
||||||
|
bantime = 1800
|
||||||
|
findtime = 300
|
||||||
|
|
||||||
|
#
|
||||||
|
# System Protection
|
||||||
|
#
|
||||||
|
[systemd-auth]
|
||||||
|
enabled = true
|
||||||
|
filter = systemd-auth
|
||||||
|
logpath = /var/log/auth.log
|
||||||
|
maxretry = 5
|
||||||
|
bantime = 3600
|
||||||
|
findtime = 300
|
||||||
20
config/honeypot/response.sh
Normal file
20
config/honeypot/response.sh
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SSH Honeypot Response Script
|
||||||
|
# Deploy to: /opt/honeypot/response.sh
|
||||||
|
#
|
||||||
|
# Setup commands:
|
||||||
|
# sudo mkdir -p /opt/honeypot
|
||||||
|
# sudo cp config/honeypot/response.sh /opt/honeypot/
|
||||||
|
# sudo chmod +x /opt/honeypot/response.sh
|
||||||
|
# sudo touch /var/log/honeypot.log
|
||||||
|
# sudo chmod 644 /var/log/honeypot.log
|
||||||
|
|
||||||
|
# Log connection with timestamp and client IP
|
||||||
|
CLIENT_IP=${NCAT_REMOTE_ADDR:-unknown}
|
||||||
|
echo "$(date): SSH honeypot connection from $CLIENT_IP" >> /var/log/honeypot.log
|
||||||
|
|
||||||
|
# Send fake SSH banner to make it look like OpenSSH
|
||||||
|
echo "SSH-2.0-OpenSSH_8.9"
|
||||||
|
|
||||||
|
# Brief delay before closing connection
|
||||||
|
sleep 2
|
||||||
31
config/netdata/netdata.conf
Normal file
31
config/netdata/netdata.conf
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# DEPLOYMENT LOCATION: /etc/netdata/netdata.conf
|
||||||
|
# Deploy with: sudo cp config/netdata/netdata.conf /etc/netdata/netdata.conf
|
||||||
|
|
||||||
|
[global]
|
||||||
|
# Run as netdata user
|
||||||
|
run as user = netdata
|
||||||
|
|
||||||
|
# Bind only to localhost (security)
|
||||||
|
bind socket to IP = 127.0.0.1
|
||||||
|
default port = 19999
|
||||||
|
|
||||||
|
# Disable telemetry and cloud features
|
||||||
|
telemetry enabled = no
|
||||||
|
|
||||||
|
[web]
|
||||||
|
# Web server settings
|
||||||
|
web files owner = root
|
||||||
|
web files group = netdata
|
||||||
|
|
||||||
|
# Only allow access from localhost (reverse proxy)
|
||||||
|
allow connections from = localhost 127.0.0.1
|
||||||
|
allow dashboard from = localhost 127.0.0.1
|
||||||
|
allow management from = localhost 127.0.0.1
|
||||||
|
|
||||||
|
[cloud]
|
||||||
|
# Completely disable Netdata Cloud
|
||||||
|
enabled = no
|
||||||
|
|
||||||
|
[registry]
|
||||||
|
# Disable registry (used for cloud)
|
||||||
|
enabled = no
|
||||||
@@ -4,9 +4,16 @@
|
|||||||
server {
|
server {
|
||||||
listen 80 default_server;
|
listen 80 default_server;
|
||||||
server_name ak-homelab.duckdns.org _;
|
server_name ak-homelab.duckdns.org _;
|
||||||
|
|
||||||
# Redirect HTTP to HTTPS
|
# Allow Let's Encrypt ACME challenges
|
||||||
return 301 https://$host$request_uri;
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Redirect all other HTTP to HTTPS
|
||||||
|
location / {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
@@ -21,6 +28,25 @@ server {
|
|||||||
add_header X-Content-Type-Options "nosniff" always;
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
add_header X-XSS-Protection "1; mode=block" always;
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
|
||||||
|
# qBittorrent Web UI
|
||||||
|
location /qbt/ {
|
||||||
|
proxy_pass http://127.0.0.1:8080/;
|
||||||
|
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_set_header X-Forwarded-Host $host;
|
||||||
|
|
||||||
|
# WebSocket support for real-time updates
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
||||||
|
# qBittorrent specific settings
|
||||||
|
proxy_cookie_path / "/qbt/";
|
||||||
|
proxy_redirect off;
|
||||||
|
}
|
||||||
|
|
||||||
# Main landing page - highest priority
|
# Main landing page - highest priority
|
||||||
location / {
|
location / {
|
||||||
root /var/www/homelab;
|
root /var/www/homelab;
|
||||||
@@ -58,7 +84,8 @@ server {
|
|||||||
# Explicitly allow WebDAV methods
|
# Explicitly allow WebDAV methods
|
||||||
limit_except GET POST PUT DELETE PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK { deny all; }
|
limit_except GET POST PUT DELETE PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK { deny all; }
|
||||||
|
|
||||||
proxy_pass http://127.0.0.1:8082/files$1;
|
# Pass original request URI to preserve URL encoding
|
||||||
|
proxy_pass http://127.0.0.1:8082;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
@@ -84,6 +111,10 @@ server {
|
|||||||
proxy_buffering off;
|
proxy_buffering off;
|
||||||
proxy_request_buffering off;
|
proxy_request_buffering off;
|
||||||
|
|
||||||
|
# Critical: Use HTTP/1.1 and fix connection headers
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
|
||||||
# Critical: Disable nginx response modifications
|
# Critical: Disable nginx response modifications
|
||||||
proxy_redirect off;
|
proxy_redirect off;
|
||||||
}
|
}
|
||||||
@@ -115,6 +146,70 @@ server {
|
|||||||
proxy_request_buffering off;
|
proxy_request_buffering off;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# System monitoring with basic auth (Glances)
|
||||||
|
location /glances/ {
|
||||||
|
auth_basic "Homelab Admin Access";
|
||||||
|
auth_basic_user_file /etc/nginx/auth/glances;
|
||||||
|
proxy_pass http://127.0.0.1:61208/;
|
||||||
|
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_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
}
|
||||||
|
|
||||||
|
# System monitoring (Netdata) - Real-time system metrics
|
||||||
|
location /netdata/ {
|
||||||
|
auth_basic "Homelab Admin Access";
|
||||||
|
auth_basic_user_file /etc/nginx/auth/glances;
|
||||||
|
proxy_pass http://127.0.0.1:19999/;
|
||||||
|
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_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Docker management (Portainer)
|
||||||
|
location /portainer/ {
|
||||||
|
proxy_pass http://127.0.0.1:9000/;
|
||||||
|
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_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Nextcloud cloud storage
|
||||||
|
location /cloud/ {
|
||||||
|
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;
|
||||||
|
|
||||||
|
# Nextcloud specific headers
|
||||||
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
|
proxy_set_header X-Forwarded-Server $host;
|
||||||
|
|
||||||
|
# Large file upload support
|
||||||
|
client_max_body_size 10G;
|
||||||
|
client_body_buffer_size 128k;
|
||||||
|
|
||||||
|
# Upload timeout settings
|
||||||
|
proxy_connect_timeout 300s;
|
||||||
|
proxy_send_timeout 300s;
|
||||||
|
proxy_read_timeout 300s;
|
||||||
|
|
||||||
|
# Buffering settings for uploads
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_request_buffering off;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ssl_certificate /etc/letsencrypt/live/ak-homelab.duckdns.org/fullchain.pem; # managed by Certbot
|
ssl_certificate /etc/letsencrypt/live/ak-homelab.duckdns.org/fullchain.pem; # managed by Certbot
|
||||||
ssl_certificate_key /etc/letsencrypt/live/ak-homelab.duckdns.org/privkey.pem; # managed by Certbot
|
ssl_certificate_key /etc/letsencrypt/live/ak-homelab.duckdns.org/privkey.pem; # managed by Certbot
|
||||||
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||||
|
|||||||
14
config/ssh/banner
Normal file
14
config/ssh/banner
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# SSH Login Banner
|
||||||
|
# Deploy to: /etc/ssh/banner
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
AUTHORIZED ACCESS ONLY
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
This system is for authorized users only. All activity may be monitored and
|
||||||
|
recorded. Unauthorized access is prohibited and will be prosecuted to the
|
||||||
|
full extent of the law.
|
||||||
|
|
||||||
|
If you are not an authorized user, disconnect immediately.
|
||||||
|
|
||||||
|
================================================================================
|
||||||
22
config/ssh/sshd_config_hardening
Normal file
22
config/ssh/sshd_config_hardening
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# SSH Hardening Configuration
|
||||||
|
# Deploy by appending to: /etc/ssh/sshd_config
|
||||||
|
#
|
||||||
|
# Setup commands:
|
||||||
|
# sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
|
||||||
|
# sudo cat config/ssh/sshd_config_hardening >> /etc/ssh/sshd_config
|
||||||
|
# sudo sshd -t
|
||||||
|
# sudo systemctl restart sshd
|
||||||
|
|
||||||
|
# Enhanced security settings
|
||||||
|
ClientAliveInterval 300
|
||||||
|
ClientAliveCountMax 2
|
||||||
|
Compression no
|
||||||
|
LogLevel VERBOSE
|
||||||
|
MaxSessions 3
|
||||||
|
TCPKeepAlive no
|
||||||
|
X11Forwarding no
|
||||||
|
AllowAgentForwarding no
|
||||||
|
AllowTcpForwarding no
|
||||||
|
GatewayPorts no
|
||||||
|
PermitTunnel no
|
||||||
|
Banner /etc/ssh/banner
|
||||||
36
config/sysctl/99-security.conf
Normal file
36
config/sysctl/99-security.conf
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Kernel Security Parameters for Homelab
|
||||||
|
# Deploy to: /etc/sysctl.d/99-security.conf
|
||||||
|
#
|
||||||
|
# Setup commands:
|
||||||
|
# sudo cp config/sysctl/99-security.conf /etc/sysctl.d/
|
||||||
|
# sudo sysctl -p /etc/sysctl.d/99-security.conf
|
||||||
|
|
||||||
|
# Network security
|
||||||
|
net.ipv4.conf.default.rp_filter = 1
|
||||||
|
net.ipv4.conf.all.rp_filter = 1
|
||||||
|
net.ipv4.ip_forward = 0
|
||||||
|
net.ipv4.conf.all.accept_source_route = 0
|
||||||
|
net.ipv4.conf.default.accept_source_route = 0
|
||||||
|
net.ipv4.conf.all.accept_redirects = 0
|
||||||
|
net.ipv4.conf.default.accept_redirects = 0
|
||||||
|
net.ipv4.conf.all.secure_redirects = 0
|
||||||
|
net.ipv4.conf.default.secure_redirects = 0
|
||||||
|
net.ipv4.conf.all.send_redirects = 0
|
||||||
|
net.ipv4.conf.default.send_redirects = 0
|
||||||
|
net.ipv4.icmp_echo_ignore_broadcasts = 1
|
||||||
|
net.ipv4.icmp_ignore_bogus_error_responses = 1
|
||||||
|
net.ipv4.tcp_syncookies = 1
|
||||||
|
net.ipv4.tcp_max_syn_backlog = 2048
|
||||||
|
net.ipv4.tcp_syn_retries = 3
|
||||||
|
net.ipv4.tcp_synack_retries = 3
|
||||||
|
|
||||||
|
# IPv6 security (if enabled)
|
||||||
|
net.ipv6.conf.default.accept_redirects = 0
|
||||||
|
net.ipv6.conf.all.accept_redirects = 0
|
||||||
|
net.ipv6.conf.default.accept_source_route = 0
|
||||||
|
net.ipv6.conf.all.accept_source_route = 0
|
||||||
|
|
||||||
|
# Memory protection
|
||||||
|
kernel.dmesg_restrict = 1
|
||||||
|
kernel.kptr_restrict = 1
|
||||||
|
kernel.yama.ptrace_scope = 1
|
||||||
18
config/systemd/glances-web.service
Normal file
18
config/systemd/glances-web.service
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# DEPLOYMENT LOCATION: /etc/systemd/system/glances-web.service
|
||||||
|
# Deploy with: sudo cp config/systemd/glances-web.service /etc/systemd/system/
|
||||||
|
# Enable with: sudo systemctl daemon-reload && sudo systemctl enable --now glances-web.service
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Glances Web Server
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=glances
|
||||||
|
Group=glances
|
||||||
|
ExecStart=/usr/bin/glances -w -p 61208 --disable-plugin docker
|
||||||
|
Restart=always
|
||||||
|
RestartSec=3
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
13
config/systemd/nginx.service.d/rate-limit.conf
Normal file
13
config/systemd/nginx.service.d/rate-limit.conf
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Nginx Service Rate Limiting Configuration
|
||||||
|
# Deploy to: /etc/systemd/system/nginx.service.d/rate-limit.conf
|
||||||
|
#
|
||||||
|
# Setup commands:
|
||||||
|
# sudo mkdir -p /etc/systemd/system/nginx.service.d
|
||||||
|
# sudo cp config/systemd/nginx.service.d/rate-limit.conf /etc/systemd/system/nginx.service.d/
|
||||||
|
# sudo systemctl daemon-reload
|
||||||
|
# sudo systemctl restart nginx
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
# Limit nginx connections
|
||||||
|
LimitNOFILE=65536
|
||||||
|
LimitNPROC=4096
|
||||||
23
config/systemd/ssh-honeypot.service
Normal file
23
config/systemd/ssh-honeypot.service
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# SSH Honeypot Service
|
||||||
|
# Deploy to: /etc/systemd/system/ssh-honeypot.service
|
||||||
|
#
|
||||||
|
# Setup commands:
|
||||||
|
# sudo cp config/systemd/ssh-honeypot.service /etc/systemd/system/
|
||||||
|
# sudo systemctl daemon-reload
|
||||||
|
# sudo systemctl enable ssh-honeypot.service
|
||||||
|
# sudo systemctl start ssh-honeypot.service
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=SSH Honeypot (Port 22)
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/usr/bin/ncat -l -k -p 22 -c /opt/honeypot/response.sh
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
Group=honeypot
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -15,17 +15,47 @@
|
|||||||
}
|
}
|
||||||
.container {
|
.container {
|
||||||
max-width: 900px; margin: 0 auto;
|
max-width: 900px; margin: 0 auto;
|
||||||
background: white; padding: 40px;
|
background: white; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
||||||
border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
padding: 40px 40px 0 40px;
|
||||||
}
|
}
|
||||||
h1 {
|
h1 {
|
||||||
color: #333; text-align: center; margin-bottom: 10px;
|
color: #333; text-align: center; margin-bottom: 10px;
|
||||||
font-size: 2.5em; font-weight: 300;
|
font-size: 2.5em; font-weight: 300;
|
||||||
}
|
}
|
||||||
.subtitle {
|
.subtitle {
|
||||||
text-align: center; color: #666; margin-bottom: 40px;
|
text-align: center; color: #666; margin-bottom: 30px;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tab Styles */
|
||||||
|
.tab-nav {
|
||||||
|
display: flex; justify-content: center; margin-bottom: 0;
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
.tab-button {
|
||||||
|
background: none; border: none; padding: 15px 30px;
|
||||||
|
font-size: 1.1em; cursor: pointer; color: #666;
|
||||||
|
border-bottom: 3px solid transparent;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
.tab-button.active {
|
||||||
|
color: #667eea; border-bottom-color: #667eea;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.tab-button:hover {
|
||||||
|
color: #667eea; background: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
display: none; padding: 40px;
|
||||||
|
}
|
||||||
|
.tab-content.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.services {
|
.services {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
@@ -36,7 +66,7 @@
|
|||||||
color: white; padding: 30px; border-radius: 8px;
|
color: white; padding: 30px; border-radius: 8px;
|
||||||
text-decoration: none; text-center;
|
text-decoration: none; text-center;
|
||||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
border: none;
|
border: none; display: block;
|
||||||
}
|
}
|
||||||
.service:hover {
|
.service:hover {
|
||||||
transform: translateY(-5px);
|
transform: translateY(-5px);
|
||||||
@@ -54,6 +84,9 @@
|
|||||||
.service.cloud {
|
.service.cloud {
|
||||||
background: linear-gradient(135deg, #0082c9 0%, #30b455 100%);
|
background: linear-gradient(135deg, #0082c9 0%, #30b455 100%);
|
||||||
}
|
}
|
||||||
|
.service.admin {
|
||||||
|
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
|
||||||
|
}
|
||||||
.service i {
|
.service i {
|
||||||
font-size: 3em; margin-bottom: 15px; display: block;
|
font-size: 3em; margin-bottom: 15px; display: block;
|
||||||
}
|
}
|
||||||
@@ -67,42 +100,123 @@
|
|||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
.footer {
|
.footer {
|
||||||
text-align: center; margin-top: 40px; color: #888;
|
text-align: center; padding: 0 40px 40px 40px; color: #888;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.admin-services {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
gap: 25px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Homelab Services</h1>
|
<div class="header">
|
||||||
<p class="subtitle">Self-hosted applications and services</p>
|
<h1>Homelab Services</h1>
|
||||||
|
<p class="subtitle">Self-hosted applications and services</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="services">
|
<div class="tab-nav">
|
||||||
<a href="/gitea/" class="service">
|
<button class="tab-button active" onclick="showTab('home', this)">
|
||||||
<i class="fas fa-code-branch"></i>
|
<i class="fas fa-home"></i> Home
|
||||||
<h3>Gitea</h3>
|
</button>
|
||||||
<p>Git Repository Server</p>
|
<button class="tab-button" onclick="showTab('admin', this)">
|
||||||
</a>
|
<i class="fas fa-cogs"></i> Admin
|
||||||
<a href="/files/" class="service">
|
</button>
|
||||||
<i class="fas fa-folder-open"></i>
|
</div>
|
||||||
<h3>File Server</h3>
|
|
||||||
<p>Upload & Download Files</p>
|
<div id="home-tab" class="tab-content active">
|
||||||
</a>
|
<div class="services">
|
||||||
<a href="#" class="service disabled" onclick="return false;">
|
<a href="/gitea/" class="service">
|
||||||
<i class="fas fa-cloud"></i>
|
<i class="fas fa-code-branch"></i>
|
||||||
<h3>Nextcloud</h3>
|
<h3>Gitea</h3>
|
||||||
<p>Temporarily Disabled</p>
|
<p>Git Repository Server</p>
|
||||||
</a>
|
</a>
|
||||||
<a href="/media/" class="service">
|
<a href="/files/" class="service">
|
||||||
<i class="fas fa-play-circle"></i>
|
<i class="fas fa-folder-open"></i>
|
||||||
<h3>Media Server</h3>
|
<h3>Copyparty</h3>
|
||||||
<p>Movies, Music & TV Shows</p>
|
<p>File Server & WebDAV</p>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="/cloud/" class="service">
|
||||||
|
<i class="fas fa-cloud"></i>
|
||||||
|
<h3>Nextcloud</h3>
|
||||||
|
<p>Cloud Storage & Collaboration</p>
|
||||||
|
</a>
|
||||||
|
<a href="/media/" class="service">
|
||||||
|
<i class="fas fa-play-circle"></i>
|
||||||
|
<h3>Jellyfin</h3>
|
||||||
|
<p>Movies, Music & TV Shows</p>
|
||||||
|
</a>
|
||||||
|
<a href="/qbt/" class="service">
|
||||||
|
<i class="fas fa-download"></i>
|
||||||
|
<h3>qBittorrent</h3>
|
||||||
|
<p>Torrent Downloads</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="admin-tab" class="tab-content">
|
||||||
|
<h2 style="color: #333; margin-bottom: 20px;">Server Administration</h2>
|
||||||
|
<div class="admin-services">
|
||||||
|
<a href="/glances/" class="service admin" target="_blank">
|
||||||
|
<i class="fas fa-chart-line"></i>
|
||||||
|
<h3>Glances</h3>
|
||||||
|
<p>System Monitoring</p>
|
||||||
|
</a>
|
||||||
|
<a href="/netdata/" class="service admin" target="_blank">
|
||||||
|
<i class="fas fa-tachometer-alt"></i>
|
||||||
|
<h3>Netdata</h3>
|
||||||
|
<p>Real-time Monitoring</p>
|
||||||
|
</a>
|
||||||
|
<a href="/portainer/" class="service admin" target="_blank">
|
||||||
|
<i class="fab fa-docker"></i>
|
||||||
|
<h3>Portainer</h3>
|
||||||
|
<p>Docker Management</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 style="color: #333; margin-bottom: 20px; margin-top: 40px;">Local Network</h2>
|
||||||
|
<div class="admin-services">
|
||||||
|
<a href="http://192.168.0.101:5000/" class="service admin" target="_blank">
|
||||||
|
<i class="fas fa-hdd"></i>
|
||||||
|
<h3>NAS Storage</h3>
|
||||||
|
<p>Network Attached Storage</p>
|
||||||
|
</a>
|
||||||
|
<a href="http://localhost:8384/" class="service admin" target="_blank">
|
||||||
|
<i class="fas fa-sync-alt"></i>
|
||||||
|
<h3>Syncthing</h3>
|
||||||
|
<p>File Synchronization</p>
|
||||||
|
</a>
|
||||||
|
<a href="http://192.168.0.1" class="service admin" target="_blank">
|
||||||
|
<i class="fas fa-network-wired"></i>
|
||||||
|
<h3>Router</h3>
|
||||||
|
<p>Network Configuration</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<p><i class="fa fa-home"></i> Powered by ak-homelab.duckdns.org</p>
|
<p><i class="fa fa-home"></i> Powered by ak-homelab.duckdns.org</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function showTab(tabName, buttonElement) {
|
||||||
|
// Hide all tab contents
|
||||||
|
const tabContents = document.querySelectorAll('.tab-content');
|
||||||
|
tabContents.forEach(tab => tab.classList.remove('active'));
|
||||||
|
|
||||||
|
// Remove active class from all buttons
|
||||||
|
const tabButtons = document.querySelectorAll('.tab-button');
|
||||||
|
tabButtons.forEach(button => button.classList.remove('active'));
|
||||||
|
|
||||||
|
// Show selected tab and mark button as active
|
||||||
|
document.getElementById(tabName + '-tab').classList.add('active');
|
||||||
|
buttonElement.classList.add('active');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
200
docs/admin-services-setup.md
Normal file
200
docs/admin-services-setup.md
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
# Admin Services Setup Documentation
|
||||||
|
|
||||||
|
**Date:** 2025-09-09
|
||||||
|
**Status:** ✅ Complete - Landing page with tabbed interface and monitoring services deployed
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document details the setup of administrative services accessible through the homelab landing page. The implementation provides a tabbed interface with monitoring and management tools for the homelab infrastructure.
|
||||||
|
|
||||||
|
## Landing Page Architecture
|
||||||
|
|
||||||
|
### Tab Structure
|
||||||
|
- **Home Tab**: Main services (Gitea, File Server, Media Server)
|
||||||
|
- **Admin Tab**: Administrative services organized in two sections:
|
||||||
|
- **Server Administration**: Remote-accessible monitoring/management
|
||||||
|
- **Local Network**: Local-only network devices
|
||||||
|
|
||||||
|
### Implementation Details
|
||||||
|
|
||||||
|
**File Location:** `/home/hoborg/homelab/config/www/index.html`
|
||||||
|
**Deployment:** `sudo cp config/www/index.html /var/www/homelab/`
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Responsive CSS Grid layout
|
||||||
|
- Font Awesome icons for visual consistency
|
||||||
|
- JavaScript tab switching functionality
|
||||||
|
- Professional gradient styling with hover effects
|
||||||
|
- Separate styling for different service types (admin, disabled, cloud)
|
||||||
|
|
||||||
|
## Admin Services Configuration
|
||||||
|
|
||||||
|
### Server Administration Services
|
||||||
|
|
||||||
|
#### 1. Glances (System Monitoring)
|
||||||
|
- **Status:** ✅ Deployed
|
||||||
|
- **Access:** https://ak-homelab.duckdns.org/glances/
|
||||||
|
- **Port:** 61208 (behind nginx reverse proxy)
|
||||||
|
- **Authentication:** Nginx basic auth (admin / AdminPass2024!)
|
||||||
|
- **Features:** Real-time CPU/RAM/disk metrics, process monitoring
|
||||||
|
|
||||||
|
**Configuration Files:**
|
||||||
|
- Service: `/home/hoborg/homelab/config/systemd/glances-web.service`
|
||||||
|
- Nginx: Reverse proxy with basic auth in `homelab.conf`
|
||||||
|
|
||||||
|
#### 2. Netdata (Real-time Monitoring)
|
||||||
|
- **Status:** ✅ Deployed (replaced Cockpit)
|
||||||
|
- **Access:** https://ak-homelab.duckdns.org/netdata/
|
||||||
|
- **Port:** 19999 (behind nginx reverse proxy)
|
||||||
|
- **Authentication:** Nginx basic auth (admin / AdminPass2024!)
|
||||||
|
- **Privacy:** Cloud features disabled, local-only operation
|
||||||
|
|
||||||
|
**Configuration Files:**
|
||||||
|
- Main config: `/home/hoborg/homelab/config/netdata/netdata.conf`
|
||||||
|
- Deployment script: `/home/hoborg/homelab/scripts/deploy-netdata-config.sh`
|
||||||
|
|
||||||
|
**Privacy Configuration:**
|
||||||
|
```ini
|
||||||
|
[global]
|
||||||
|
bind socket to IP = 127.0.0.1
|
||||||
|
telemetry enabled = no
|
||||||
|
|
||||||
|
[cloud]
|
||||||
|
enabled = no
|
||||||
|
|
||||||
|
[registry]
|
||||||
|
enabled = no
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Portainer (Docker Management)
|
||||||
|
- **Status:** 📋 Planned
|
||||||
|
- **Access:** https://ak-homelab.duckdns.org/portainer/
|
||||||
|
- **Port:** 9000 (behind nginx reverse proxy)
|
||||||
|
- **Authentication:** Built-in user management
|
||||||
|
|
||||||
|
### Local Network Services
|
||||||
|
|
||||||
|
#### 1. NAS Storage
|
||||||
|
- **Access:** http://192.168.0.101:5000/
|
||||||
|
- **Description:** Network Attached Storage management interface
|
||||||
|
- **Icon:** `fas fa-hdd`
|
||||||
|
- **Authentication:** Built-in device authentication
|
||||||
|
|
||||||
|
#### 2. Router Configuration
|
||||||
|
- **Access:** http://192.168.0.1
|
||||||
|
- **Description:** Network router administration
|
||||||
|
- **Icon:** `fas fa-network-wired`
|
||||||
|
- **Authentication:** Router's built-in authentication
|
||||||
|
|
||||||
|
## Security Implementation
|
||||||
|
|
||||||
|
### Nginx Basic Authentication
|
||||||
|
|
||||||
|
**Auth File:** `/etc/nginx/auth/glances`
|
||||||
|
**Credentials:** admin / AdminPass2024!
|
||||||
|
|
||||||
|
**Services using basic auth:**
|
||||||
|
- Glances (system metrics exposure)
|
||||||
|
- Netdata (system metrics exposure)
|
||||||
|
|
||||||
|
**Creation Command:**
|
||||||
|
```bash
|
||||||
|
sudo htpasswd -c /etc/nginx/auth/glances admin
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service-Level Security
|
||||||
|
|
||||||
|
- **Netdata:** Configured for localhost-only access, cloud features disabled
|
||||||
|
- **Glances:** Web server bound to localhost, accessible only through reverse proxy
|
||||||
|
- **Portainer:** Uses built-in authentication with RBAC
|
||||||
|
- **Local Network:** Services remain on local network only (no external exposure)
|
||||||
|
|
||||||
|
## Deployment Scripts
|
||||||
|
|
||||||
|
### 1. Netdata Setup Script
|
||||||
|
**File:** `/home/hoborg/homelab/scripts/setup-netdata.sh`
|
||||||
|
- Installs netdata package
|
||||||
|
- Enables and starts service
|
||||||
|
- Stops/disables Cockpit services
|
||||||
|
- Deploys updated landing page
|
||||||
|
|
||||||
|
### 2. Netdata Configuration Deployment
|
||||||
|
**File:** `/home/hoborg/homelab/scripts/deploy-netdata-config.sh`
|
||||||
|
- Deploys privacy-focused Netdata configuration
|
||||||
|
- Updates nginx configuration with Netdata reverse proxy
|
||||||
|
- Tests configuration and performs rollback on failure
|
||||||
|
- Includes connectivity testing
|
||||||
|
|
||||||
|
## Troubleshooting Steps Completed
|
||||||
|
|
||||||
|
### 1. Cockpit Compatibility Issues
|
||||||
|
**Problem:** Cockpit had infinite loading issues due to MIME type conflicts with reverse proxy
|
||||||
|
**Solution:** Replaced Cockpit with Netdata for better reverse proxy compatibility
|
||||||
|
|
||||||
|
**Error Details:**
|
||||||
|
- Content-Security-Policy errors
|
||||||
|
- MIME type mismatches for static assets
|
||||||
|
- Path rewriting complications with static file serving
|
||||||
|
|
||||||
|
### 2. Configuration Management Approach
|
||||||
|
**Problem:** Initial scripts modified configuration files directly
|
||||||
|
**Solution:** Implemented proper workflow - edit repo files first, then deploy via scripts
|
||||||
|
|
||||||
|
**Workflow:**
|
||||||
|
1. Edit configuration in `/home/hoborg/homelab/config/`
|
||||||
|
2. Test changes locally when possible
|
||||||
|
3. Deploy via simple copy scripts with backup/rollback capabilities
|
||||||
|
4. Update documentation
|
||||||
|
|
||||||
|
## Current Status
|
||||||
|
|
||||||
|
### ✅ Completed
|
||||||
|
- Landing page with tabbed interface
|
||||||
|
- Glances system monitoring with basic auth
|
||||||
|
- Netdata real-time monitoring with privacy configuration
|
||||||
|
- Nginx reverse proxy configuration for all services
|
||||||
|
- Updated documentation and deployment scripts
|
||||||
|
- NAS Storage link added to Local Network section
|
||||||
|
|
||||||
|
### 📋 Pending
|
||||||
|
- Portainer Docker management deployment
|
||||||
|
- Final nginx configuration deployment (for Netdata access)
|
||||||
|
- lazydocker terminal tool installation
|
||||||
|
|
||||||
|
## Access Summary
|
||||||
|
|
||||||
|
### External Access (HTTPS with SSL)
|
||||||
|
- **Glances:** https://ak-homelab.duckdns.org/glances/ (basic auth required)
|
||||||
|
- **Netdata:** https://ak-homelab.duckdns.org/netdata/ (basic auth required)
|
||||||
|
- **Portainer:** https://ak-homelab.duckdns.org/portainer/ (planned, built-in auth)
|
||||||
|
|
||||||
|
### Local Network Access
|
||||||
|
- **NAS Storage:** http://192.168.0.101:5000/ (device auth)
|
||||||
|
- **Router:** http://192.168.0.1 (router auth)
|
||||||
|
|
||||||
|
### Direct Service Access (for testing)
|
||||||
|
- **Netdata Direct:** http://127.0.0.1:19999/ (localhost only after config deployment)
|
||||||
|
- **Glances Direct:** http://127.0.0.1:61208/ (localhost only)
|
||||||
|
|
||||||
|
## Files Modified/Created
|
||||||
|
|
||||||
|
### Configuration Files
|
||||||
|
- `/home/hoborg/homelab/config/www/index.html` - Updated with admin sections
|
||||||
|
- `/home/hoborg/homelab/config/nginx/homelab.conf` - Added Netdata reverse proxy
|
||||||
|
- `/home/hoborg/homelab/config/netdata/netdata.conf` - Privacy-focused configuration
|
||||||
|
- `/home/hoborg/homelab/config/systemd/glances-web.service` - Glances systemd service
|
||||||
|
|
||||||
|
### Scripts Created
|
||||||
|
- `/home/hoborg/homelab/scripts/setup-netdata.sh` - Netdata installation script
|
||||||
|
- `/home/hoborg/homelab/scripts/deploy-netdata-config.sh` - Configuration deployment script
|
||||||
|
|
||||||
|
### Documentation Updated
|
||||||
|
- `/home/hoborg/homelab/docs/services.md` - Updated monitoring services section
|
||||||
|
- `/home/hoborg/homelab/docs/admin-services-setup.md` - This comprehensive setup document
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. Deploy Netdata configuration: `sudo -A ./scripts/deploy-netdata-config.sh`
|
||||||
|
2. Install and configure Portainer for Docker management
|
||||||
|
3. Install lazydocker for SSH-based Docker administration
|
||||||
|
4. Consider additional monitoring tools (htop, iotop alternatives) for terminal use
|
||||||
181
docs/geoip-blocking.md
Normal file
181
docs/geoip-blocking.md
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
# GeoIP Blocking with geoip-shell
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
**Tool:** geoip-shell v0.7.5
|
||||||
|
**Repository:** https://github.com/friendly-bits/geoip-shell
|
||||||
|
**Installation Method:** Manual from GitHub repository
|
||||||
|
**Purpose:** Geographic IP blocking for enhanced security
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone the repository
|
||||||
|
git clone https://github.com/friendly-bits/geoip-shell.git
|
||||||
|
cd geoip-shell
|
||||||
|
|
||||||
|
# Install the tool
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Current Configuration
|
||||||
|
|
||||||
|
### Core Settings
|
||||||
|
- **Firewall Backend:** iptables
|
||||||
|
- **IP Lists Source:** RIPE (Réseaux IP Européens)
|
||||||
|
- **Network Interfaces:** All interfaces protected
|
||||||
|
- **LAN Detection:** Automatic subnet detection enabled
|
||||||
|
|
||||||
|
### Update Mechanism
|
||||||
|
- **Cron Service:** ✅ Enabled
|
||||||
|
- **Update Schedule:** Daily at 4:08 AM (`8 4 * * *`)
|
||||||
|
- **Last Update:** September 17, 2025 at 00:57:41
|
||||||
|
- **Persistence:** ✅ Enabled (survives reboots)
|
||||||
|
- **Backup:** ✅ Automatic IP lists backup enabled
|
||||||
|
|
||||||
|
### Persistence Cron Job
|
||||||
|
- **Schedule:** On reboot (`@reboot`)
|
||||||
|
- **Command:** `/usr/bin/geoip-shell-run.sh restore -a`
|
||||||
|
- **Purpose:** Restores geoip-shell firewall rules after system reboot
|
||||||
|
|
||||||
|
## Inbound Geoblocking Configuration
|
||||||
|
|
||||||
|
### Blocking Mode
|
||||||
|
- **Type:** Whitelist (only specified countries allowed)
|
||||||
|
- **IP Families:** IPv4 and IPv6 supported
|
||||||
|
|
||||||
|
### Whitelisted Countries
|
||||||
|
```
|
||||||
|
AL (Albania), AD (Andorra), AM (Armenia), AT (Austria), AZ (Azerbaijan)
|
||||||
|
BY (Belarus), BE (Belgium), BA (Bosnia and Herzegovina), BG (Bulgaria)
|
||||||
|
HR (Croatia), CY (Cyprus), CZ (Czech Republic), DK (Denmark)
|
||||||
|
EE (Estonia), FO (Faroe Islands), FI (Finland), FR (France)
|
||||||
|
GE (Georgia), DE (Germany), GI (Gibraltar), GR (Greece)
|
||||||
|
GG (Guernsey), HU (Hungary), IS (Iceland), IE (Ireland)
|
||||||
|
IM (Isle of Man), IT (Italy), JE (Jersey), KZ (Kazakhstan)
|
||||||
|
LV (Latvia), LI (Liechtenstein), LT (Lithuania), LU (Luxembourg)
|
||||||
|
MT (Malta), MD (Moldova), MC (Monaco), ME (Montenegro)
|
||||||
|
NL (Netherlands), MK (North Macedonia), NO (Norway), PL (Poland)
|
||||||
|
PT (Portugal), RO (Romania), RU (Russia), SM (San Marino)
|
||||||
|
RS (Serbia), SK (Slovakia), SI (Slovenia), ES (Spain)
|
||||||
|
SE (Sweden), CH (Switzerland), TR (Turkey), UA (Ukraine)
|
||||||
|
GB (United Kingdom), VA (Vatican City)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Network Exceptions (Always Allowed)
|
||||||
|
**IPv4 Networks:**
|
||||||
|
- `172.18.0.0/16` - Docker network
|
||||||
|
- `172.17.0.0/16` - Docker network
|
||||||
|
- `169.254.0.0/16` - Link-local addresses
|
||||||
|
- `192.168.0.0/24` - Local LAN
|
||||||
|
- `172.20.0.0/16` - Docker network
|
||||||
|
- `172.19.0.0/16` - Docker network
|
||||||
|
|
||||||
|
**IPv6 Networks:**
|
||||||
|
- `fdaa:bbcc:ddee::/64` - Custom network
|
||||||
|
- `fe80::/10` - Link-local addresses
|
||||||
|
|
||||||
|
### Protocol Coverage
|
||||||
|
- **TCP:** All destination ports blocked for non-whitelisted countries
|
||||||
|
- **UDP:** All destination ports blocked for non-whitelisted countries
|
||||||
|
|
||||||
|
### Firewall Status
|
||||||
|
- **IPv4 Chain:** ✅ Enabled and active
|
||||||
|
- **IPv6 Chain:** ✅ Enabled and active
|
||||||
|
- **Whitelist Rules:** ✅ Properly configured
|
||||||
|
|
||||||
|
## Outbound Geoblocking
|
||||||
|
- **Status:** Disabled (no outbound restrictions)
|
||||||
|
|
||||||
|
## System Health
|
||||||
|
- **Overall Status:** ✅ No problems detected
|
||||||
|
- **Firewall Integration:** ✅ Working correctly
|
||||||
|
- **Update Process:** ✅ Functioning properly
|
||||||
|
|
||||||
|
## Security Impact
|
||||||
|
|
||||||
|
### Protection Provided
|
||||||
|
1. **Geographic Blocking:** Blocks all traffic from countries not in whitelist
|
||||||
|
2. **Comprehensive Coverage:** Both IPv4 and IPv6 protection
|
||||||
|
3. **Full Protocol Coverage:** TCP and UDP traffic controlled
|
||||||
|
4. **Network Awareness:** Automatically detects and allows local networks
|
||||||
|
5. **Persistence:** Rules survive system reboots
|
||||||
|
6. **Automatic Updates:** IP lists updated daily
|
||||||
|
|
||||||
|
### Integration with Existing Security
|
||||||
|
- **Complements fail2ban:** Provides geographic layer above intrusion detection
|
||||||
|
- **Works with UFW:** Uses iptables backend compatible with UFW
|
||||||
|
- **Docker Compatible:** Automatically allows Docker networks
|
||||||
|
- **LAN Friendly:** Preserves local network access
|
||||||
|
|
||||||
|
## Monitoring and Maintenance
|
||||||
|
|
||||||
|
### Status Checking
|
||||||
|
```bash
|
||||||
|
geoip-shell status
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log Locations
|
||||||
|
- Main logs: `/var/log/geoip-shell.log`
|
||||||
|
- System logs: `journalctl -u geoip-shell`
|
||||||
|
|
||||||
|
### Update Verification
|
||||||
|
- Automatic daily updates at 4:18 AM
|
||||||
|
- Last update timestamp visible in status
|
||||||
|
- Backup of IP lists maintained
|
||||||
|
|
||||||
|
## Configuration Philosophy
|
||||||
|
|
||||||
|
This setup follows a **whitelist approach** focusing on:
|
||||||
|
- **Trusted Regions:** European countries plus select others
|
||||||
|
- **Local Access:** Full LAN and Docker network access preserved
|
||||||
|
- **Minimal Disruption:** Automatic detection of local networks
|
||||||
|
- **Comprehensive Protection:** Both inbound directions covered
|
||||||
|
|
||||||
|
## Manual Setup Process
|
||||||
|
|
||||||
|
The tool was installed manually with interactive prompts rather than scripted installation due to security considerations. Key decisions made during setup:
|
||||||
|
|
||||||
|
1. **Whitelist Mode:** Chosen over blacklist for better control
|
||||||
|
2. **European Focus:** Primary whitelist consists of European countries
|
||||||
|
3. **Network Exceptions:** Docker and LAN networks automatically detected
|
||||||
|
4. **Dual Stack:** Both IPv4 and IPv6 protection enabled
|
||||||
|
5. **Full Protocol Coverage:** TCP and UDP both protected
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
- **Service Not Starting:** Check `systemctl status geoip-shell`
|
||||||
|
- **Rules Not Applied:** Verify iptables chains with `iptables -L`
|
||||||
|
- **Update Failures:** Check `/var/log/geoip-shell.log`
|
||||||
|
|
||||||
|
### Recovery Commands
|
||||||
|
```bash
|
||||||
|
# Restart service
|
||||||
|
sudo systemctl restart geoip-shell
|
||||||
|
|
||||||
|
# Reload configuration
|
||||||
|
sudo geoip-shell reload
|
||||||
|
|
||||||
|
# Check for issues
|
||||||
|
sudo geoip-shell status
|
||||||
|
```
|
||||||
|
|
||||||
|
## Future Considerations
|
||||||
|
|
||||||
|
### Potential Enhancements
|
||||||
|
- **Custom Country Lists:** Add/remove countries based on threat intelligence
|
||||||
|
- **Time-based Rules:** Different rules for different times of day
|
||||||
|
- **Integration with Monitoring:** Alert on blocked country attempts
|
||||||
|
- **Backup Configurations:** Document alternative configurations
|
||||||
|
|
||||||
|
### Monitoring Improvements
|
||||||
|
- **Log Analysis:** Regular review of blocked attempts by country
|
||||||
|
- **Performance Impact:** Monitor system resource usage
|
||||||
|
- **False Positives:** Track legitimate traffic being blocked
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- **Official Repository:** https://github.com/friendly-bits/geoip-shell
|
||||||
|
- **Documentation:** Available in repository wiki
|
||||||
|
- **Issue Tracking:** GitHub issues for bug reports and feature requests
|
||||||
@@ -366,6 +366,245 @@ sudo fail2ban-client status sshd
|
|||||||
sudo fail2ban-client get sshd banned
|
sudo fail2ban-client get sshd banned
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Geographic IP Blocking with geoip-shell
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
**Status:** ✅ **Implemented** - Whitelist mode protecting all interfaces
|
||||||
|
|
||||||
|
**Tool:** geoip-shell v0.7.5
|
||||||
|
**Repository:** https://github.com/friendly-bits/geoip-shell
|
||||||
|
**Purpose:** Geographic IP blocking for enhanced security
|
||||||
|
|
||||||
|
### Current Configuration
|
||||||
|
- **Firewall Backend:** iptables
|
||||||
|
- **IP Lists Source:** RIPE (Réseaux IP Européens)
|
||||||
|
- **Network Interfaces:** All interfaces protected
|
||||||
|
- **LAN Detection:** Automatic subnet detection enabled
|
||||||
|
- **Mode:** Whitelist (only specified countries allowed)
|
||||||
|
- **IP Families:** IPv4 and IPv6 supported
|
||||||
|
- **Update Schedule:** Daily at 4:18 AM
|
||||||
|
- **Last Update:** September 17, 2025 at 00:57:41
|
||||||
|
|
||||||
|
### Whitelisted Countries
|
||||||
|
|
||||||
|
**Configuration:** All EU member states plus European territories (full list accepted by geoip-shell)
|
||||||
|
|
||||||
|
```
|
||||||
|
AL (Albania), AD (Andorra), AM (Armenia), AT (Austria), AX (Åland Islands), AZ (Azerbaijan)
|
||||||
|
BY (Belarus), BE (Belgium), BA (Bosnia and Herzegovina), BG (Bulgaria)
|
||||||
|
HR (Croatia), CY (Cyprus), CZ (Czech Republic), DK (Denmark)
|
||||||
|
EE (Estonia), FO (Faroe Islands), FI (Finland), FR (France)
|
||||||
|
GE (Georgia), DE (Germany), GI (Gibraltar), GR (Greece)
|
||||||
|
GG (Guernsey), HU (Hungary), IS (Iceland), IE (Ireland)
|
||||||
|
IM (Isle of Man), IT (Italy), JE (Jersey), KZ (Kazakhstan)
|
||||||
|
LV (Latvia), LI (Liechtenstein), LT (Lithuania), LU (Luxembourg)
|
||||||
|
MT (Malta), MD (Moldova), MC (Monaco), ME (Montenegro)
|
||||||
|
NL (Netherlands), MK (North Macedonia), NO (Norway), PL (Poland)
|
||||||
|
PT (Portugal), RO (Romania), RU (Russia), SM (San Marino)
|
||||||
|
RS (Serbia), SK (Slovakia), SI (Slovenia), ES (Spain)
|
||||||
|
SE (Sweden), CH (Switzerland), TR (Turkey), UA (Ukraine)
|
||||||
|
GB (United Kingdom), VA (Vatican City)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Network Exceptions (Always Allowed)
|
||||||
|
**IPv4 Networks:**
|
||||||
|
- `172.18.0.0/16` - Docker network
|
||||||
|
- `172.17.0.0/16` - Docker network
|
||||||
|
- `169.254.0.0/16` - Link-local addresses
|
||||||
|
- `192.168.0.0/24` - Local LAN
|
||||||
|
- `172.20.0.0/16` - Docker network
|
||||||
|
- `172.19.0.0/16` - Docker network
|
||||||
|
|
||||||
|
**IPv6 Networks:**
|
||||||
|
- `fdaa:bbcc:ddee::/64` - Custom network
|
||||||
|
- `fe80::/10` - Link-local addresses
|
||||||
|
|
||||||
|
### Security Impact
|
||||||
|
1. **Geographic Blocking:** Blocks all traffic from countries not in whitelist
|
||||||
|
2. **Comprehensive Coverage:** Both IPv4 and IPv6 protection
|
||||||
|
3. **Full Protocol Coverage:** TCP and UDP traffic controlled
|
||||||
|
4. **Network Awareness:** Automatically detects and allows local networks
|
||||||
|
5. **Persistence:** Rules survive system reboots
|
||||||
|
6. **Automatic Updates:** IP lists updated daily
|
||||||
|
|
||||||
|
### Integration with Existing Security
|
||||||
|
- **Complements fail2ban:** Provides geographic layer above intrusion detection
|
||||||
|
- **Works with UFW:** Uses iptables backend compatible with UFW
|
||||||
|
- **Docker Compatible:** Automatically allows Docker networks
|
||||||
|
- **LAN Friendly:** Preserves local network access
|
||||||
|
|
||||||
|
### Monitoring
|
||||||
|
```bash
|
||||||
|
# Check geoip-shell status
|
||||||
|
geoip-shell status
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
journalctl -u geoip-shell
|
||||||
|
tail -f /var/log/geoip-shell.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Installation and Setup
|
||||||
|
|
||||||
|
**Installation Method:** Interactive CLI setup using geoip-shell installer
|
||||||
|
|
||||||
|
The tool was configured using its interactive setup wizard, which prompted for configuration choices and automatically applied all necessary changes to the firewall. This approach ensures proper integration with the existing firewall backend.
|
||||||
|
|
||||||
|
**Key Configuration Decisions:**
|
||||||
|
|
||||||
|
1. **Whitelist Mode:** Chosen over blacklist for better control
|
||||||
|
2. **European Focus:** All EU member states plus European territories
|
||||||
|
3. **Country List:** Complete list of countries accepted by geoip-shell
|
||||||
|
4. **Network Exceptions:** Docker and LAN networks automatically detected
|
||||||
|
5. **Dual Stack:** Both IPv4 and IPv6 protection enabled
|
||||||
|
6. **Full Protocol Coverage:** TCP and UDP both protected
|
||||||
|
|
||||||
|
**Setup Command:**
|
||||||
|
```bash
|
||||||
|
# Download and run installer
|
||||||
|
curl -fsSL https://raw.githubusercontent.com/friendly-bits/geoip-shell/main/install.sh | bash
|
||||||
|
|
||||||
|
# Run configuration wizard (prompted automatically after install)
|
||||||
|
geoip-shell configure
|
||||||
|
```
|
||||||
|
|
||||||
|
During the interactive setup, the wizard prompted for:
|
||||||
|
- Firewall backend selection (iptables chosen)
|
||||||
|
- IP list source (RIPE selected)
|
||||||
|
- Mode selection (whitelist chosen)
|
||||||
|
- Country selection (all EU countries entered)
|
||||||
|
- Network interface configuration (all interfaces selected)
|
||||||
|
|
||||||
|
### Detailed Documentation
|
||||||
|
For complete setup details, see **[docs/geoip-blocking.md](docs/geoip-blocking.md)**
|
||||||
|
|
||||||
|
## Permanent Ban System for Repeat Offenders
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
**Status:** ✅ **Implemented** - Automated permanent banning of persistent attackers
|
||||||
|
|
||||||
|
**Script:** `scripts/permanent-ban-repeat-offenders.sh`
|
||||||
|
**Purpose:** Automatically identify and permanently ban IPs that have been banned by fail2ban more than a threshold number of times
|
||||||
|
|
||||||
|
### How It Works
|
||||||
|
|
||||||
|
#### Detection Logic
|
||||||
|
1. **Log Analysis:** Scans `/var/log/fail2ban.log*` for ban entries
|
||||||
|
2. **IP Extraction:** Extracts IP addresses from ban log entries
|
||||||
|
3. **Frequency Counting:** Counts how many times each IP has been banned
|
||||||
|
4. **Threshold Check:** Identifies IPs banned more than the threshold (4 times)
|
||||||
|
|
||||||
|
#### Permanent Banning Process
|
||||||
|
For each repeat offender:
|
||||||
|
1. **Country Lookup:** Uses `whois` to determine the country of origin
|
||||||
|
2. **Banlist Update:** Adds IP to `/etc/fail2ban/permanent-banlist.conf`
|
||||||
|
3. **Firewall Rule:** Creates permanent iptables DROP rule
|
||||||
|
4. **Persistence:** Saves iptables rules to `/etc/iptables/iptables.rules`
|
||||||
|
5. **Service Reload:** Reloads fail2ban to recognize the updated banlist
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
#### Threshold Settings
|
||||||
|
```bash
|
||||||
|
THRESHOLD=4 # Ban after 4 fail2ban bans
|
||||||
|
```
|
||||||
|
|
||||||
|
#### File Locations
|
||||||
|
- **Log File:** `/var/log/permanent-ban.log`
|
||||||
|
- **Banlist:** `/etc/fail2ban/permanent-banlist.conf`
|
||||||
|
- **Iptables Rules:** `/etc/iptables/iptables.rules`
|
||||||
|
|
||||||
|
#### Cron Schedule
|
||||||
|
- **Frequency:** Daily at 2:00 AM (`0 2 * * *`)
|
||||||
|
- **User:** root
|
||||||
|
- **Command:** `/home/hoborg/homelab/scripts/permanent-ban-repeat-offenders.sh`
|
||||||
|
|
||||||
|
### Security Benefits
|
||||||
|
|
||||||
|
#### Multi-Layer Protection
|
||||||
|
1. **fail2ban:** Temporary bans for suspicious activity
|
||||||
|
2. **Permanent Bans:** Long-term blocking of persistent attackers
|
||||||
|
3. **Geographic Blocking:** Country-level filtering via geoip-shell
|
||||||
|
4. **Network-Level:** iptables rules at the firewall level
|
||||||
|
|
||||||
|
#### Attack Prevention
|
||||||
|
- **Brute Force:** Blocks IPs that repeatedly attempt attacks
|
||||||
|
- **Botnets:** Prevents automated attacks from compromised hosts
|
||||||
|
- **Persistence:** Maintains bans across system reboots
|
||||||
|
- **Resource Protection:** Reduces server load from repeat offenders
|
||||||
|
|
||||||
|
### Monitoring and Maintenance
|
||||||
|
|
||||||
|
#### Log Analysis
|
||||||
|
```bash
|
||||||
|
# View permanent ban activity
|
||||||
|
tail -f /var/log/permanent-ban.log
|
||||||
|
|
||||||
|
# Check current permanent bans
|
||||||
|
cat /etc/fail2ban/permanent-banlist.conf
|
||||||
|
|
||||||
|
# View iptables permanent rules
|
||||||
|
iptables -L | grep DROP
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Manual Execution
|
||||||
|
```bash
|
||||||
|
# Run the script manually (requires root)
|
||||||
|
sudo /home/hoborg/homelab/scripts/permanent-ban-repeat-offenders.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Unban Procedure
|
||||||
|
To manually unban a permanently banned IP:
|
||||||
|
```bash
|
||||||
|
# Remove from banlist
|
||||||
|
sudo sed -i "/^192\.168\.1\.100/d" /etc/fail2ban/permanent-banlist.conf
|
||||||
|
|
||||||
|
# Remove iptables rule (find the rule number first)
|
||||||
|
sudo iptables -L --line-numbers | grep "192.168.1.100"
|
||||||
|
sudo iptables -D INPUT <rule_number>
|
||||||
|
|
||||||
|
# Save iptables rules
|
||||||
|
sudo iptables-save > /etc/iptables/iptables.rules
|
||||||
|
|
||||||
|
# Reload fail2ban
|
||||||
|
sudo systemctl reload fail2ban
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integration with Security Stack
|
||||||
|
|
||||||
|
#### Complementary Tools
|
||||||
|
- **fail2ban:** Provides temporary bans that feed into permanent ban detection
|
||||||
|
- **geoip-shell:** Geographic blocking at the network level
|
||||||
|
- **UFW:** Additional firewall layer
|
||||||
|
- **SSH Hardening:** Reduces initial attack surface
|
||||||
|
|
||||||
|
#### Workflow
|
||||||
|
```
|
||||||
|
Attack Attempt → fail2ban Detection → Temporary Ban → Repeat Offense → Permanent Ban → Geographic Block
|
||||||
|
```
|
||||||
|
|
||||||
|
### Troubleshooting
|
||||||
|
|
||||||
|
#### Common Issues
|
||||||
|
- **Script Not Running:** Check cron job configuration
|
||||||
|
- **Permission Errors:** Ensure script is executable and paths are correct
|
||||||
|
- **whois Failures:** Some IPs may not return country information
|
||||||
|
- **iptables-save Issues:** Check if iptables-persistent is installed
|
||||||
|
|
||||||
|
#### Diagnostic Commands
|
||||||
|
```bash
|
||||||
|
# Check cron service
|
||||||
|
sudo systemctl status cron
|
||||||
|
|
||||||
|
# Test script manually
|
||||||
|
sudo bash -x /home/hoborg/homelab/scripts/permanent-ban-repeat-offenders.sh
|
||||||
|
|
||||||
|
# Verify iptables rules
|
||||||
|
sudo iptables -L -n | grep DROP
|
||||||
|
|
||||||
|
# Check fail2ban integration
|
||||||
|
sudo fail2ban-client status
|
||||||
|
```
|
||||||
|
|
||||||
## Router Configuration
|
## Router Configuration
|
||||||
|
|
||||||
### Port Forwarding
|
### Port Forwarding
|
||||||
|
|||||||
121
docs/qbittorrent-setup.md
Normal file
121
docs/qbittorrent-setup.md
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# qBittorrent Setup Guide
|
||||||
|
|
||||||
|
This document provides comprehensive step-by-step instructions for setting up qBittorrent in your homelab environment, including both automated deployment and manual configuration.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- qBittorrent container running via Docker
|
||||||
|
- NAS directory structure set up at `/mnt/nas/torrent/`
|
||||||
|
- Nginx reverse proxy configured for `/qbt/` path
|
||||||
|
|
||||||
|
## Current NAS Structure
|
||||||
|
|
||||||
|
The following directory structure should already exist:
|
||||||
|
|
||||||
|
```
|
||||||
|
/mnt/nas/torrent/
|
||||||
|
├── watch/
|
||||||
|
│ ├── music/
|
||||||
|
│ ├── videos/
|
||||||
|
│ ├── documents/
|
||||||
|
│ └── private/
|
||||||
|
└── complete/
|
||||||
|
├── music/
|
||||||
|
├── videos/
|
||||||
|
├── documents/
|
||||||
|
└── private/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Manual Configuration Steps
|
||||||
|
|
||||||
|
### 1. Access qBittorrent Web UI
|
||||||
|
|
||||||
|
1. Open your browser and navigate to: `https://ak-homelab.duckdns.org/qbt/`
|
||||||
|
2. Login with:
|
||||||
|
- **Username**: `admin`
|
||||||
|
- **Password**: Check Docker logs for temporary password:
|
||||||
|
```bash
|
||||||
|
docker logs qbittorrent 2>&1 | grep "temporary password"
|
||||||
|
```
|
||||||
|
3. **IMPORTANT**: Change the default password on first login
|
||||||
|
|
||||||
|
### 2. Configure Basic Settings
|
||||||
|
|
||||||
|
1. Go to **Tools → Options → Downloads**
|
||||||
|
2. Set **Default Torrent Management Mode**: `Automatic`
|
||||||
|
3. Set **Default Save Path**: `/downloads/complete`
|
||||||
|
4. Enable **Auto managed torrents**
|
||||||
|
5. Click **Apply**
|
||||||
|
|
||||||
|
### 3. Create Download Categories
|
||||||
|
|
||||||
|
Right-click in the main torrent area and select **Add category** for each:
|
||||||
|
|
||||||
|
| Category | Save Path |
|
||||||
|
|----------|-----------|
|
||||||
|
| `music` | `/downloads/complete/music` |
|
||||||
|
| `videos` | `/downloads/complete/videos` |
|
||||||
|
| `documents` | `/downloads/complete/documents` |
|
||||||
|
| `private` | `/downloads/complete/private` |
|
||||||
|
|
||||||
|
### 4. Set Up Watch Folders
|
||||||
|
|
||||||
|
1. Go to **Tools → Options → Downloads**
|
||||||
|
2. Navigate to **Monitored Folder** section
|
||||||
|
3. Click **Add** to create watch folders:
|
||||||
|
|
||||||
|
| Watch Folder Path | Category |
|
||||||
|
|------------------|----------|
|
||||||
|
| `/downloads/watch/music` | `music` |
|
||||||
|
| `/downloads/watch/videos` | `videos` |
|
||||||
|
| `/downloads/watch/documents` | `documents` |
|
||||||
|
| `/downloads/watch/private` | `private` |
|
||||||
|
|
||||||
|
### 5. Test Configuration
|
||||||
|
|
||||||
|
1. Download a test `.torrent` file
|
||||||
|
2. Drop it into `/mnt/nas/torrent/watch/music/`
|
||||||
|
3. Verify it appears in qBittorrent with the correct category
|
||||||
|
4. Confirm it downloads to `/mnt/nas/torrent/complete/music/`
|
||||||
|
|
||||||
|
### 6. Optional: Hardlink Organization
|
||||||
|
|
||||||
|
After confirming downloads work correctly, you can set up automatic hardlinking to organize files for Jellyfin access:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/organize-torrents.py
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates hardlinks from completed torrents to appropriate media directories without duplicating files.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **Permission errors**: Ensure qBittorrent container has read/write access to mounted volumes
|
||||||
|
2. **Watch folders not working**: Check that the paths are correctly mapped in docker-compose.yml
|
||||||
|
3. **Categories not auto-assigning**: Verify watch folder paths match exactly
|
||||||
|
|
||||||
|
### Docker Volume Mapping
|
||||||
|
|
||||||
|
Ensure your `docker-compose.yml` includes these volume mappings:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- /mnt/nas/torrent:/downloads
|
||||||
|
- qbittorrent-config:/config
|
||||||
|
```
|
||||||
|
|
||||||
|
### Logs
|
||||||
|
|
||||||
|
Check container logs for issues:
|
||||||
|
```bash
|
||||||
|
docker logs qbittorrent
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Recommendations
|
||||||
|
|
||||||
|
1. Change default admin password immediately
|
||||||
|
2. Enable 2FA if available in your qBittorrent version
|
||||||
|
3. Consider using authentication bypass only for local network access
|
||||||
|
4. Regularly update the container image for security patches
|
||||||
248
docs/security-configurations.md
Normal file
248
docs/security-configurations.md
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
# Security Configuration Files
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document catalogs all security-related configuration files in the homelab repository and their deployment locations.
|
||||||
|
|
||||||
|
## SSH Security Configurations
|
||||||
|
|
||||||
|
### SSH Honeypot
|
||||||
|
- **Config File**: `config/systemd/ssh-honeypot.service`
|
||||||
|
- **Deploy To**: `/etc/systemd/system/ssh-honeypot.service`
|
||||||
|
- **Purpose**: Systemd service for SSH honeypot on port 22
|
||||||
|
- **Dependencies**: `config/honeypot/response.sh`
|
||||||
|
|
||||||
|
- **Config File**: `config/honeypot/response.sh`
|
||||||
|
- **Deploy To**: `/opt/honeypot/response.sh`
|
||||||
|
- **Purpose**: Response script for honeypot connections
|
||||||
|
- **Log File**: `/var/log/honeypot.log`
|
||||||
|
|
||||||
|
### SSH Service Hardening
|
||||||
|
- **System File**: `/etc/ssh/sshd_config`
|
||||||
|
- **Key Settings**:
|
||||||
|
- `Port 2222` (moved from default port 22)
|
||||||
|
- `PermitRootLogin no`
|
||||||
|
- `PasswordAuthentication no`
|
||||||
|
- `PubkeyAuthentication yes`
|
||||||
|
|
||||||
|
## Network Security
|
||||||
|
|
||||||
|
### Nginx Security Headers
|
||||||
|
- **Config File**: `config/nginx/homelab.conf`
|
||||||
|
- **Deploy To**: `/etc/nginx/sites-available/homelab`
|
||||||
|
- **Security Features**:
|
||||||
|
- SSL/TLS configuration
|
||||||
|
- Security headers (HSTS, CSP, etc.)
|
||||||
|
- Rate limiting
|
||||||
|
- Access controls
|
||||||
|
|
||||||
|
### NetworkManager Security
|
||||||
|
- **Config File**: `config/networkmanager/01-homelab.conf`
|
||||||
|
- **Deploy To**: `/etc/NetworkManager/conf.d/01-homelab.conf`
|
||||||
|
- **Purpose**: Static IP and interface security settings
|
||||||
|
|
||||||
|
## Service-Specific Security
|
||||||
|
|
||||||
|
### Gitea Security
|
||||||
|
- **Config File**: `config/docker/gitea/docker-compose.yml`
|
||||||
|
- **Deploy To**: `/opt/docker/gitea/docker-compose.yml`
|
||||||
|
- **Security Features**:
|
||||||
|
- Custom SSH port (2223) for Git operations
|
||||||
|
- Web UI bound to localhost only (127.0.0.1:3000) for reverse proxy
|
||||||
|
- Non-root user operation via USER_UID/USER_GID environment variables
|
||||||
|
- Resource limits (1G memory)
|
||||||
|
- Health checking for service monitoring
|
||||||
|
- s6-overlay compatible configuration (no read-only filesystem)
|
||||||
|
- **Security Notes**:
|
||||||
|
- SSH port 2223 bound to all interfaces for external Git access
|
||||||
|
- Web port 3000 bound to localhost only for nginx proxy security
|
||||||
|
- Avoid Docker `user:` directive (breaks s6-overlay init system)
|
||||||
|
- Use environment variables for privilege dropping instead
|
||||||
|
|
||||||
|
### Jellyfin Security
|
||||||
|
- **Config File**: `config/docker/jellyfin/docker-compose.yml`
|
||||||
|
- **Security Features**:
|
||||||
|
- User/group restrictions
|
||||||
|
- Volume mount security
|
||||||
|
- Network isolation
|
||||||
|
|
||||||
|
### qBittorrent Security
|
||||||
|
- **Config File**: `config/docker/qbittorrent/docker-compose.yml`
|
||||||
|
- **Security Features**:
|
||||||
|
- VPN integration capability
|
||||||
|
- Web UI access controls
|
||||||
|
- File permission restrictions
|
||||||
|
|
||||||
|
## System Security Services
|
||||||
|
|
||||||
|
### Copyparty File Server
|
||||||
|
- **Config File**: `config/systemd/copyparty.service`
|
||||||
|
- **Deploy To**: `/etc/systemd/system/copyparty.service`
|
||||||
|
- **Config File**: `config/copyparty/copyparty.conf`
|
||||||
|
- **Deploy To**: `~/.config/copyparty/copyparty.conf`
|
||||||
|
- **Security Features**:
|
||||||
|
- WebDAV authentication
|
||||||
|
- Access controls
|
||||||
|
- Upload restrictions
|
||||||
|
|
||||||
|
## Security Hardening Configurations
|
||||||
|
|
||||||
|
### Fail2ban Intrusion Prevention
|
||||||
|
- **Config File**: `config/fail2ban/jail.local`
|
||||||
|
- **Deploy To**: `/etc/fail2ban/jail.local`
|
||||||
|
- **Purpose**: Automated IP banning for SSH, web, and service attacks
|
||||||
|
|
||||||
|
- **Config Files**: `config/fail2ban/filter.d/`
|
||||||
|
- **Deploy To**: `/etc/fail2ban/filter.d/`
|
||||||
|
- **Filters**:
|
||||||
|
- `sshd-ddos.conf` - SSH connection flooding protection
|
||||||
|
- `nginx-badbots.conf` - Web scanner and bot detection
|
||||||
|
- `gitea-auth.conf` - Gitea authentication failure detection
|
||||||
|
|
||||||
|
### SSH Security Hardening
|
||||||
|
- **Config File**: `config/ssh/sshd_config_hardening`
|
||||||
|
- **Deploy To**: Append to `/etc/ssh/sshd_config`
|
||||||
|
- **Purpose**: Enhanced SSH security settings
|
||||||
|
|
||||||
|
- **Config File**: `config/ssh/banner`
|
||||||
|
- **Deploy To**: `/etc/ssh/banner`
|
||||||
|
- **Purpose**: Legal warning banner for SSH connections
|
||||||
|
|
||||||
|
### Kernel Security Parameters
|
||||||
|
- **Config File**: `config/sysctl/99-security.conf`
|
||||||
|
- **Deploy To**: `/etc/sysctl.d/99-security.conf`
|
||||||
|
- **Purpose**: Network and memory protection parameters
|
||||||
|
|
||||||
|
### Docker Security Configuration
|
||||||
|
- **Config File**: `config/docker/daemon.json`
|
||||||
|
- **Deploy To**: `/etc/docker/daemon.json`
|
||||||
|
- **Purpose**: Docker daemon security hardening
|
||||||
|
|
||||||
|
### Service Rate Limiting
|
||||||
|
- **Config File**: `config/systemd/nginx.service.d/rate-limit.conf`
|
||||||
|
- **Deploy To**: `/etc/systemd/system/nginx.service.d/rate-limit.conf`
|
||||||
|
- **Purpose**: Nginx resource limits and connection throttling
|
||||||
|
|
||||||
|
## Monitoring and Logging
|
||||||
|
|
||||||
|
### Service Monitoring
|
||||||
|
- **Config File**: `config/systemd/glances-web.service`
|
||||||
|
- **Deploy To**: `/etc/systemd/system/glances-web.service`
|
||||||
|
- **Purpose**: System monitoring with web interface
|
||||||
|
|
||||||
|
### System Logging
|
||||||
|
- **Config File**: `config/systemd/01-server-logind.conf`
|
||||||
|
- **Deploy To**: `/etc/systemd/logind.conf.d/01-server-logind.conf`
|
||||||
|
- **Purpose**: Login and session security settings
|
||||||
|
|
||||||
|
## Security Documentation
|
||||||
|
|
||||||
|
### Setup Guides
|
||||||
|
- `docs/ssh-honeypot-setup.md` - SSH honeypot installation and configuration
|
||||||
|
- `docs/ssh-intrusion-monitoring.md` - Comprehensive SSH monitoring guide
|
||||||
|
- `docs/qbittorrent-setup.md` - Secure torrent client setup
|
||||||
|
|
||||||
|
### Security Procedures
|
||||||
|
- `docs/security-configurations.md` - This file (configuration catalog)
|
||||||
|
- Various service-specific security notes in configuration files
|
||||||
|
|
||||||
|
## Deployment Security
|
||||||
|
|
||||||
|
### File Permissions
|
||||||
|
All configuration files include deployment commands with appropriate permissions:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Service files
|
||||||
|
sudo chmod 644 /etc/systemd/system/*.service
|
||||||
|
|
||||||
|
# Scripts
|
||||||
|
sudo chmod +x /opt/honeypot/response.sh
|
||||||
|
|
||||||
|
# Config files
|
||||||
|
sudo chmod 644 /etc/nginx/sites-available/*
|
||||||
|
sudo chmod 600 ~/.config/copyparty/copyparty.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service Security
|
||||||
|
```bash
|
||||||
|
# Enable services securely
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable --now servicename.service
|
||||||
|
|
||||||
|
# Verify service status
|
||||||
|
sudo systemctl status servicename.service
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Validation
|
||||||
|
|
||||||
|
### Configuration Testing
|
||||||
|
```bash
|
||||||
|
# Test nginx configuration
|
||||||
|
sudo nginx -t
|
||||||
|
|
||||||
|
# Verify SSH configuration
|
||||||
|
sudo sshd -t
|
||||||
|
|
||||||
|
# Check systemd service syntax
|
||||||
|
sudo systemd-analyze verify /etc/systemd/system/servicename.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### Security Scanning
|
||||||
|
```bash
|
||||||
|
# Check listening ports
|
||||||
|
ss -tlnp
|
||||||
|
|
||||||
|
# Verify service users and permissions
|
||||||
|
sudo systemctl show servicename.service
|
||||||
|
|
||||||
|
# Check file permissions
|
||||||
|
find config/ -type f -ls
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Updates
|
||||||
|
|
||||||
|
### Regular Maintenance
|
||||||
|
1. **Weekly**: Review honeypot logs, update fail2ban rules
|
||||||
|
2. **Monthly**: Update service configurations, security patches
|
||||||
|
3. **Quarterly**: Full security audit, penetration testing
|
||||||
|
4. **Annually**: Certificate renewal, security policy review
|
||||||
|
|
||||||
|
### Configuration Backup
|
||||||
|
All configurations are version-controlled in git:
|
||||||
|
```bash
|
||||||
|
# Backup current configs
|
||||||
|
git add config/ docs/
|
||||||
|
git commit -m "Update security configurations"
|
||||||
|
|
||||||
|
# Restore from backup
|
||||||
|
git checkout HEAD -- config/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Contacts and Escalation
|
||||||
|
|
||||||
|
### Log Locations
|
||||||
|
- **Security Incidents**: `/var/log/security.log`
|
||||||
|
- **Authentication**: `/var/log/auth.log`
|
||||||
|
- **Honeypot**: `/var/log/honeypot.log`
|
||||||
|
- **Service Logs**: `journalctl -u servicename.service`
|
||||||
|
|
||||||
|
### Incident Response
|
||||||
|
1. Immediate containment (block IPs, disable services)
|
||||||
|
2. Evidence preservation (copy logs, take snapshots)
|
||||||
|
3. Impact assessment (check for compromise)
|
||||||
|
4. Recovery procedures (restore from known-good configs)
|
||||||
|
5. Post-incident review (update procedures and configurations)
|
||||||
|
|
||||||
|
## Compliance and Standards
|
||||||
|
|
||||||
|
### Security Frameworks
|
||||||
|
- **Network Security**: Defense in depth with multiple layers
|
||||||
|
- **Access Control**: Principle of least privilege
|
||||||
|
- **Monitoring**: Comprehensive logging and alerting
|
||||||
|
- **Incident Response**: Documented procedures and escalation
|
||||||
|
|
||||||
|
### Audit Trail
|
||||||
|
- All configuration changes tracked in git
|
||||||
|
- Service modifications logged via systemd
|
||||||
|
- Security events captured in dedicated log files
|
||||||
|
- Regular security reviews documented in commit messages
|
||||||
448
docs/security-hardening.md
Normal file
448
docs/security-hardening.md
Normal file
@@ -0,0 +1,448 @@
|
|||||||
|
# Homelab Security Hardening Guide
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
This document tracks the security hardening process for the homelab infrastructure. Based on security audit findings, we've identified critical vulnerabilities that need immediate attention.
|
||||||
|
|
||||||
|
## Critical Security Issues (IMMEDIATE ACTION REQUIRED)
|
||||||
|
|
||||||
|
### 🚨 Port Exposure Vulnerabilities
|
||||||
|
- **qBittorrent**: Currently binding to `0.0.0.0:6881` (exposed to all interfaces)
|
||||||
|
- **Gitea**: Currently binding to `0.0.0.0:3000` and `0.0.0.0:2223`
|
||||||
|
- **Portainer**: Docker management interface exposed on port 9000
|
||||||
|
|
||||||
|
**Status**: 🔴 NOT ADDRESSED
|
||||||
|
**Priority**: CRITICAL
|
||||||
|
**Impact**: Services accessible from internet without authentication
|
||||||
|
|
||||||
|
### 🚨 Missing Intrusion Prevention
|
||||||
|
- **fail2ban**: Not installed or running
|
||||||
|
- **Firewall**: UFW/iptables not properly configured
|
||||||
|
|
||||||
|
**Status**: 🔴 NOT ADDRESSED
|
||||||
|
**Priority**: CRITICAL
|
||||||
|
**Impact**: No protection against brute force attacks
|
||||||
|
|
||||||
|
### 🚨 SSL/TLS Missing
|
||||||
|
- **HTTPS**: SSL certificates were previously configured but may have been lost
|
||||||
|
- **Let's Encrypt**: Configuration exists in git history but needs restoration
|
||||||
|
- **Git History**: Found SSL config in commit `2cd1d87` with Let's Encrypt certificates
|
||||||
|
|
||||||
|
**Status**: 🟡 PARTIALLY ADDRESSED (config exists, needs deployment)
|
||||||
|
**Priority**: CRITICAL
|
||||||
|
**Impact**: All traffic unencrypted, vulnerable to MITM attacks
|
||||||
|
|
||||||
|
**Recovery Steps:**
|
||||||
|
```bash
|
||||||
|
# Restore SSL configuration from git
|
||||||
|
git show 2cd1d87:config/nginx/homelab.conf > config/nginx/homelab-ssl.conf
|
||||||
|
|
||||||
|
# Install certbot and get certificates
|
||||||
|
sudo pacman -S certbot certbot-nginx
|
||||||
|
sudo certbot --nginx -d ak-homelab.duckdns.org
|
||||||
|
|
||||||
|
# Deploy SSL-enabled nginx config
|
||||||
|
sudo cp config/nginx/homelab-ssl.conf /etc/nginx/sites-available/homelab
|
||||||
|
sudo nginx -t && sudo systemctl reload nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Scripts Available
|
||||||
|
|
||||||
|
### ✅ Container Hardening (`scripts/harden-containers.sh`)
|
||||||
|
- Docker daemon configuration hardening
|
||||||
|
- Resource limits and security profiles
|
||||||
|
- Custom seccomp profiles
|
||||||
|
- Container security monitoring scripts
|
||||||
|
|
||||||
|
**Status**: 🟡 READY TO DEPLOY
|
||||||
|
**Next Step**: Run script and deploy hardened templates
|
||||||
|
|
||||||
|
### ✅ Credential Security (`scripts/secure-credentials.sh`)
|
||||||
|
- Secure credential storage in `/opt/homelab/secrets/`
|
||||||
|
- Docker secrets implementation
|
||||||
|
- Password generation utilities
|
||||||
|
- Access audit tools
|
||||||
|
|
||||||
|
**Status**: 🟡 READY TO DEPLOY
|
||||||
|
**Next Step**: Run script and migrate existing credentials
|
||||||
|
|
||||||
|
### ✅ Security Audit (`scripts/security-audit.sh`)
|
||||||
|
- Comprehensive system security assessment
|
||||||
|
- Credential exposure detection
|
||||||
|
- Service analysis and user audit
|
||||||
|
- Log analysis capabilities
|
||||||
|
|
||||||
|
**Status**: 🟡 READY TO USE
|
||||||
|
**Next Step**: Run initial audit to establish baseline
|
||||||
|
|
||||||
|
### ✅ Fail2ban Setup (`scripts/setup-fail2ban.sh`)
|
||||||
|
- SSH protection (port 2222)
|
||||||
|
- Nginx rate limiting and bot protection
|
||||||
|
- Custom filters for homelab services
|
||||||
|
- Attack analysis and monitoring tools
|
||||||
|
|
||||||
|
**Status**: 🟡 READY TO DEPLOY
|
||||||
|
**Next Step**: Install and configure fail2ban
|
||||||
|
|
||||||
|
### ✅ SSL Security (`scripts/ssl-security-audit.sh`)
|
||||||
|
- SSL/TLS configuration hardening
|
||||||
|
- Certificate monitoring and renewal
|
||||||
|
- Security headers implementation
|
||||||
|
- SSL testing and validation tools
|
||||||
|
|
||||||
|
**Status**: 🟡 READY TO DEPLOY
|
||||||
|
**Next Step**: Set up Let's Encrypt certificates first
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
### Phase 1: Critical Security Fixes (Do NOW)
|
||||||
|
|
||||||
|
#### 1. Fix Port Exposure
|
||||||
|
```bash
|
||||||
|
# Bind services to localhost only
|
||||||
|
sudo docker update --publish-add "127.0.0.1:8080:8080" qbittorrent
|
||||||
|
sudo docker update --publish-rm "0.0.0.0:8080:8080" qbittorrent
|
||||||
|
sudo docker update --publish-add "127.0.0.1:3000:3000" gitea
|
||||||
|
sudo docker update --publish-rm "0.0.0.0:3000:3000" gitea
|
||||||
|
sudo docker update --publish-add "127.0.0.1:2223:22" gitea
|
||||||
|
sudo docker update --publish-rm "0.0.0.0:2223:22" gitea
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Install Fail2ban
|
||||||
|
```bash
|
||||||
|
sudo -A ./scripts/setup-fail2ban.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Configure Basic Firewall
|
||||||
|
```bash
|
||||||
|
sudo ufw enable
|
||||||
|
sudo ufw allow 2222/tcp # SSH
|
||||||
|
sudo ufw allow 80/tcp # HTTP (temporary)
|
||||||
|
sudo ufw allow 443/tcp # HTTPS
|
||||||
|
sudo ufw allow 60000:61000/udp # Mosh UDP ports
|
||||||
|
sudo ufw --force reload
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. SSH Hardening with Mosh Support
|
||||||
|
```bash
|
||||||
|
# Install Mosh for mobile SSH
|
||||||
|
sudo pacman -S mosh
|
||||||
|
|
||||||
|
# Edit /etc/ssh/sshd_config
|
||||||
|
sudo nano /etc/ssh/sshd_config
|
||||||
|
|
||||||
|
# Add these security settings:
|
||||||
|
# Port 2222 (already done)
|
||||||
|
# PermitRootLogin no
|
||||||
|
# PasswordAuthentication no # DISABLE AFTER KEY SETUP
|
||||||
|
# PubkeyAuthentication yes
|
||||||
|
# AllowUsers hoborg
|
||||||
|
# ClientAliveInterval 300
|
||||||
|
# ClientAliveCountMax 2
|
||||||
|
# MaxAuthTries 3
|
||||||
|
|
||||||
|
# Test SSH key authentication first
|
||||||
|
ssh-copy-id -i ~/.ssh/id_ed25519.pub hoborg@ak-homelab.duckdns.org -p 2222
|
||||||
|
|
||||||
|
# Then disable password authentication
|
||||||
|
# PasswordAuthentication no
|
||||||
|
|
||||||
|
# Restart SSH
|
||||||
|
sudo systemctl restart sshd
|
||||||
|
|
||||||
|
# Test Mosh connectivity
|
||||||
|
mosh hoborg@ak-homelab.duckdns.org --ssh="ssh -p 2222"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Set Up SSL Certificates
|
||||||
|
```bash
|
||||||
|
sudo pacman -S certbot certbot-nginx
|
||||||
|
sudo certbot --nginx -d ak-homelab.duckdns.org
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Container Security
|
||||||
|
|
||||||
|
#### 1. Harden Docker Configuration
|
||||||
|
```bash
|
||||||
|
sudo -A ./scripts/harden-containers.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Deploy Hardened Container Templates
|
||||||
|
```bash
|
||||||
|
sudo -A /opt/docker/monitoring/deploy-hardened-containers.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Secure Credentials
|
||||||
|
```bash
|
||||||
|
sudo -A ./scripts/secure-credentials.sh
|
||||||
|
/opt/homelab/secrets/generate-passwords.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: SSH Hardening
|
||||||
|
|
||||||
|
#### 1. Set Up SSH Security Keys
|
||||||
|
- Generate SSH keys on management devices
|
||||||
|
- Add public keys to `~/.ssh/authorized_keys`
|
||||||
|
- Test key-based authentication
|
||||||
|
|
||||||
|
#### 2. Disable Password Authentication
|
||||||
|
```bash
|
||||||
|
# Edit /etc/ssh/sshd_config
|
||||||
|
PasswordAuthentication no
|
||||||
|
ChallengeResponseAuthentication no
|
||||||
|
UsePAM no
|
||||||
|
|
||||||
|
# Ensure Mosh compatibility
|
||||||
|
# Mosh uses UDP ports 60000-61000
|
||||||
|
sudo ufw allow 60000:61000/udp
|
||||||
|
|
||||||
|
# Restart SSH
|
||||||
|
sudo systemctl restart sshd
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Test Mosh Connectivity
|
||||||
|
```bash
|
||||||
|
# Install mosh if not present
|
||||||
|
sudo pacman -S mosh
|
||||||
|
|
||||||
|
# Test connection
|
||||||
|
mosh user@ak-homelab.duckdns.org --ssh="ssh -p 2222"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Monitoring & Alerting
|
||||||
|
|
||||||
|
#### 1. Set Up System Monitoring
|
||||||
|
```bash
|
||||||
|
sudo -A ./scripts/setup-netdata.sh
|
||||||
|
sudo -A ./scripts/setup-glances.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Configure SSL Monitoring
|
||||||
|
```bash
|
||||||
|
sudo -A ./scripts/ssl-security-audit.sh
|
||||||
|
sudo systemctl enable ssl-monitor.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Set Up Regular Security Audits
|
||||||
|
```bash
|
||||||
|
# Add to cron for weekly audits
|
||||||
|
echo "0 2 * * 1 sudo -A /home/hoborg/homelab/scripts/security-audit.sh" | sudo tee -a /etc/cron.d/homelab-security
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: VPN Setup (WireGuard)
|
||||||
|
|
||||||
|
#### 1. Install WireGuard
|
||||||
|
```bash
|
||||||
|
sudo pacman -S wireguard-tools
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Generate Server Keys
|
||||||
|
```bash
|
||||||
|
# Generate server keys
|
||||||
|
wg genkey | tee server_private.key | wg pubkey > server_public.key
|
||||||
|
|
||||||
|
# Generate client keys (on client device)
|
||||||
|
wg genkey | tee client_private.key | wg pubkey > client_public.key
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Server Configuration (/etc/wireguard/wg0.conf)
|
||||||
|
```ini
|
||||||
|
[Interface]
|
||||||
|
PrivateKey = <SERVER_PRIVATE_KEY>
|
||||||
|
Address = 10.0.0.1/24
|
||||||
|
ListenPort = 51820
|
||||||
|
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o enp4s0 -j MASQUERADE
|
||||||
|
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o enp4s0 -j MASQUERADE
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = <CLIENT_PUBLIC_KEY>
|
||||||
|
AllowedIPs = 10.0.0.2/32
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Client Configuration
|
||||||
|
```ini
|
||||||
|
[Interface]
|
||||||
|
PrivateKey = <CLIENT_PRIVATE_KEY>
|
||||||
|
Address = 10.0.0.2/24
|
||||||
|
DNS = 1.1.1.1
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = <SERVER_PUBLIC_KEY>
|
||||||
|
Endpoint = ak-homelab.duckdns.org:51820
|
||||||
|
AllowedIPs = 0.0.0.0/0
|
||||||
|
PersistentKeepalive = 25
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5. Enable VPN Service
|
||||||
|
```bash
|
||||||
|
sudo systemctl enable wg-quick@wg0
|
||||||
|
sudo systemctl start wg-quick@wg0
|
||||||
|
|
||||||
|
# Update firewall
|
||||||
|
sudo ufw allow 51820/udp
|
||||||
|
sudo ufw reload
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6. Router Port Forwarding
|
||||||
|
- Forward UDP port 51820 to homelab server
|
||||||
|
- Update DuckDNS to include VPN endpoint
|
||||||
|
|
||||||
|
#### 7. Test VPN Connectivity
|
||||||
|
```bash
|
||||||
|
# On client: Check VPN status
|
||||||
|
wg show
|
||||||
|
|
||||||
|
# Test homelab access through VPN
|
||||||
|
ssh hoborg@10.0.0.1 -p 2222
|
||||||
|
curl https://ak-homelab.duckdns.org
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Checklist
|
||||||
|
|
||||||
|
### Network Security
|
||||||
|
- [ ] Ports bound to localhost only
|
||||||
|
- [ ] Firewall configured and enabled
|
||||||
|
- [ ] Fail2ban installed and running
|
||||||
|
- [ ] VPN server configured
|
||||||
|
- [ ] SSH hardened (keys only, custom port)
|
||||||
|
|
||||||
|
### SSL/TLS Security
|
||||||
|
- [ ] Let's Encrypt certificates installed
|
||||||
|
- [ ] SSL configuration hardened
|
||||||
|
- [ ] HSTS headers configured
|
||||||
|
- [ ] Certificate monitoring active
|
||||||
|
- [ ] Perfect Forward Secrecy enabled
|
||||||
|
|
||||||
|
### Container Security
|
||||||
|
- [ ] Docker daemon hardened
|
||||||
|
- [ ] Containers run as non-root users
|
||||||
|
- [ ] Resource limits applied
|
||||||
|
- [ ] Security profiles enabled
|
||||||
|
- [ ] No privileged containers
|
||||||
|
|
||||||
|
### Credential Security
|
||||||
|
- [ ] Credentials moved to secure location
|
||||||
|
- [ ] Docker secrets implemented
|
||||||
|
- [ ] Strong passwords generated
|
||||||
|
- [ ] Access logging enabled
|
||||||
|
|
||||||
|
### Monitoring & Alerting
|
||||||
|
- [ ] System monitoring active (Netdata/Glances)
|
||||||
|
- [ ] Security event monitoring
|
||||||
|
- [ ] Log analysis configured
|
||||||
|
- [ ] Automated alerts set up
|
||||||
|
|
||||||
|
## Testing Procedures
|
||||||
|
|
||||||
|
### Security Testing
|
||||||
|
1. **Port Scanning**: `nmap -sV ak-homelab.duckdns.org`
|
||||||
|
2. **SSL Testing**: `sslscan ak-homelab.duckdns.org`
|
||||||
|
3. **Container Security**: `/opt/docker/monitoring/container-security-check.sh`
|
||||||
|
4. **Fail2ban Status**: `fail2ban-client status`
|
||||||
|
|
||||||
|
### Functionality Testing
|
||||||
|
1. **SSH Access**: Test key-based and password authentication
|
||||||
|
2. **Mosh Connectivity**: Test mobile SSH sessions
|
||||||
|
3. **VPN Access**: Test remote connectivity
|
||||||
|
4. **Service Access**: Verify all services work through Nginx proxy
|
||||||
|
5. **SSL Redirect**: Ensure HTTP redirects to HTTPS
|
||||||
|
|
||||||
|
## Emergency Procedures
|
||||||
|
|
||||||
|
### Security Incident Response
|
||||||
|
1. **Isolate**: Disconnect affected systems from network
|
||||||
|
2. **Assess**: Run security audit to identify compromise
|
||||||
|
3. **Contain**: Block malicious IPs, change credentials
|
||||||
|
4. **Recover**: Restore from clean backups
|
||||||
|
5. **Learn**: Update procedures based on incident
|
||||||
|
|
||||||
|
### Backup Security
|
||||||
|
- [ ] Encrypt backups
|
||||||
|
- [ ] Store offsite securely
|
||||||
|
- [ ] Test restoration procedures
|
||||||
|
- [ ] Include configuration backups
|
||||||
|
|
||||||
|
## Previous AI Agent Recommendations
|
||||||
|
|
||||||
|
### From CLAUDE.md (Voice Assistant Setup)
|
||||||
|
- ✅ Voice server configured for Claude Code
|
||||||
|
- ✅ Piper TTS integration working
|
||||||
|
- ✅ Mosh compatibility considerations noted
|
||||||
|
|
||||||
|
### From Git History Analysis
|
||||||
|
- **SSL Configuration**: Found complete Let's Encrypt setup in commit `2cd1d87`
|
||||||
|
- **WebDAV Support**: Advanced nginx configuration with security headers
|
||||||
|
- **Service Architecture**: Well-documented reverse proxy setup
|
||||||
|
|
||||||
|
### From network-security.md
|
||||||
|
- ✅ SSH port changed to 2222
|
||||||
|
- ✅ Router port forwarding updated
|
||||||
|
- ✅ Mosh configured (ISP UDP blocking noted)
|
||||||
|
- ✅ WireGuard VPN documentation complete
|
||||||
|
- ✅ fail2ban configuration documented
|
||||||
|
- ✅ UFW firewall setup documented
|
||||||
|
|
||||||
|
## Current Status Assessment
|
||||||
|
|
||||||
|
### ✅ Completed Items
|
||||||
|
- SSH port hardening (2222)
|
||||||
|
- Router port forwarding updates
|
||||||
|
- Voice assistant integration
|
||||||
|
- Network security documentation
|
||||||
|
- VPN setup documentation
|
||||||
|
|
||||||
|
### 🔴 Critical Issues (Immediate Action Required)
|
||||||
|
- Port exposure vulnerabilities
|
||||||
|
- Missing fail2ban installation
|
||||||
|
- SSL certificate restoration needed
|
||||||
|
- SSH password authentication still enabled
|
||||||
|
|
||||||
|
### 🟡 Partially Complete
|
||||||
|
- SSL configuration exists in git (needs deployment)
|
||||||
|
- Security scripts created (need execution)
|
||||||
|
- VPN documentation complete (needs implementation)
|
||||||
|
|
||||||
|
## Next Steps Priority
|
||||||
|
|
||||||
|
1. **IMMEDIATE**: Fix port exposure and install fail2ban
|
||||||
|
2. **HIGH**: Restore SSL certificates from git history
|
||||||
|
3. **MEDIUM**: Execute security hardening scripts
|
||||||
|
4. **MEDIUM**: Set up SSH key authentication and disable passwords
|
||||||
|
5. **LOW**: Implement WireGuard VPN
|
||||||
|
6. **LOW**: Set up monitoring and alerting
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
### Security Resources
|
||||||
|
- [Docker Security Best Practices](https://docs.docker.com/develop/dev-best-practices/security/)
|
||||||
|
- [OWASP Docker Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html)
|
||||||
|
- [Mozilla SSL Configuration Generator](https://ssl-config.mozilla.org/)
|
||||||
|
- [WireGuard Documentation](https://www.wireguard.com/)
|
||||||
|
- [Let's Encrypt Certbot](https://certbot.eff.org/)
|
||||||
|
|
||||||
|
### Tools Used
|
||||||
|
- fail2ban: Intrusion prevention
|
||||||
|
- UFW: Firewall management
|
||||||
|
- certbot: SSL certificate management
|
||||||
|
- Docker: Container security features
|
||||||
|
- Netdata/Glances: System monitoring
|
||||||
|
|
||||||
|
## Status Updates
|
||||||
|
|
||||||
|
### 2025-09-12: Initial Assessment
|
||||||
|
- Identified critical port exposure vulnerabilities
|
||||||
|
- Found missing fail2ban and SSL certificates
|
||||||
|
- Created comprehensive hardening plan
|
||||||
|
- Documented all security scripts and their purposes
|
||||||
|
|
||||||
|
### Next Update: [Date]
|
||||||
|
- [Progress made]
|
||||||
|
- [Issues resolved]
|
||||||
|
- [Next steps]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated**: 2025-09-12
|
||||||
|
**Security Status**: 🔴 CRITICAL - Immediate action required
|
||||||
|
**SSL Recovery**: Configuration found in git history (commit 2cd1d87)
|
||||||
|
**VPN Ready**: Complete WireGuard setup documentation available
|
||||||
|
**SSH Status**: Port hardened, password auth needs disabling
|
||||||
255
docs/services.md
255
docs/services.md
@@ -4,12 +4,12 @@ Planning and configuration for self-hosted services and applications.
|
|||||||
|
|
||||||
## Nginx Reverse Proxy
|
## Nginx Reverse Proxy
|
||||||
|
|
||||||
**Status**: ✅ Active
|
**Status**: ✅ Active
|
||||||
**Port**: 80 (HTTP), 443 (HTTPS)
|
**Port**: 80 (HTTP), 443 (HTTPS)
|
||||||
**Configuration**: `/etc/nginx/sites-available/homelab`
|
**Configuration**: `/etc/nginx/sites-available/homelab`
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
- Path-based routing to services (`/gitea/`, `/files/`, `/media/`)
|
- Path-based routing to services (`/gitea/`, `/files/`, `/media/`)
|
||||||
- SSL termination with Let's Encrypt certificates
|
- SSL termination with Let's Encrypt certificates
|
||||||
- Security headers (XSS protection, content type sniffing prevention)
|
- Security headers (XSS protection, content type sniffing prevention)
|
||||||
- WebDAV support for Copyparty file server
|
- WebDAV support for Copyparty file server
|
||||||
@@ -160,7 +160,7 @@ docker-compose up -d
|
|||||||
|
|
||||||
**Port Assignments:**
|
**Port Assignments:**
|
||||||
- **System SSH**: 2222 (for server administration)
|
- **System SSH**: 2222 (for server administration)
|
||||||
- **Gitea SSH**: 2223 (for Git operations)
|
- **Gitea SSH**: 2223 (for Git operations)
|
||||||
- **Gitea Web**: 3000 (web interface)
|
- **Gitea Web**: 3000 (web interface)
|
||||||
|
|
||||||
**Database**: SQLite (default, stored in container volume)
|
**Database**: SQLite (default, stored in container volume)
|
||||||
@@ -264,16 +264,27 @@ sudo pacman -S copyparty
|
|||||||
- **SSL**: Let's Encrypt certificates with automatic renewal
|
- **SSL**: Let's Encrypt certificates with automatic renewal
|
||||||
|
|
||||||
**User Accounts:**
|
**User Accounts:**
|
||||||
- **guest**: Standard user with read/write access to shared areas
|
- **guest**: Standard user with read/write (`rw`) access to shared areas
|
||||||
- **hoborg**: Admin user with access to all areas including private folder
|
- **hoborg**: Admin user with full access (`rwmd` - read/write/move/delete) to all areas including private folder
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- ✅ File upload/download via web interface
|
||||||
|
- ✅ WebDAV support for X-plore File Manager, rclone, etc.
|
||||||
|
- ✅ File deletion via WebDAV (requires `d` permission)
|
||||||
|
- ✅ Drag & drop upload in web interface
|
||||||
|
- ✅ Support for files with spaces/special characters
|
||||||
|
- ✅ Large file upload support (up to 10GB)
|
||||||
|
- ✅ Resume interrupted uploads
|
||||||
|
- ✅ File deduplication and integrity checking
|
||||||
|
|
||||||
**Volume Structure:**
|
**Volume Structure:**
|
||||||
```
|
```
|
||||||
/shared → /home/hoborg/shared (guest, hoborg: rw)
|
/shared → /home/hoborg/shared (guest: rw, hoborg: rwmd)
|
||||||
/documents → /home/hoborg/Documents (guest, hoborg: rw)
|
/documents → /home/hoborg/Documents (hoborg: rwmd)
|
||||||
/music → /home/hoborg/Music (guest, hoborg: rw)
|
/music → /home/hoborg/Music (guest: rw, hoborg: rwmd)
|
||||||
/videos → /home/hoborg/Videos (guest, hoborg: rw)
|
/videos → /home/hoborg/Videos (guest: rw, hoborg: rwmd)
|
||||||
/private → /home/hoborg/private (hoborg only: rw)
|
/pictures → /home/hoborg/Pictures (guest: rw, hoborg: rwmd)
|
||||||
|
/private → /home/hoborg/private (hoborg only: rwmd)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Features Enabled:**
|
**Features Enabled:**
|
||||||
@@ -311,6 +322,29 @@ sudo systemctl disable copyparty
|
|||||||
- **Systemd service**: `/home/hoborg/homelab/config/systemd/copyparty.service`
|
- **Systemd service**: `/home/hoborg/homelab/config/systemd/copyparty.service`
|
||||||
- **Nginx integration**: Path `/files/` in homelab.conf
|
- **Nginx integration**: Path `/files/` in homelab.conf
|
||||||
|
|
||||||
|
**WebDAV Client Setup:**
|
||||||
|
|
||||||
|
*X-plore File Manager (Android):*
|
||||||
|
- Server: `ak-homelab.duckdns.org`
|
||||||
|
- Path: `/files/shared/` (or other folder paths)
|
||||||
|
- Protocol: HTTPS (port 443)
|
||||||
|
- Username: `hoborg`
|
||||||
|
- Password: [your password]
|
||||||
|
|
||||||
|
*rclone configuration:*
|
||||||
|
```bash
|
||||||
|
rclone config create homelab-webdav webdav \
|
||||||
|
url=https://ak-homelab.duckdns.org/files/ \
|
||||||
|
vendor=other \
|
||||||
|
user=hoborg \
|
||||||
|
pass=$(rclone obscure "your_password")
|
||||||
|
```
|
||||||
|
|
||||||
|
**Troubleshooting:**
|
||||||
|
- For issues with files containing spaces, see [docs/troubleshooting/webdav-copyparty.md](troubleshooting/webdav-copyparty.md)
|
||||||
|
- Check nginx WebDAV configuration for URL encoding issues
|
||||||
|
- Verify copyparty permissions include `d` flag for delete operations
|
||||||
|
|
||||||
**Testing Confirmed:**
|
**Testing Confirmed:**
|
||||||
- ✅ File uploads working (including video files)
|
- ✅ File uploads working (including video files)
|
||||||
- ✅ WebDAV folder uploads from Android (X-plore File Manager)
|
- ✅ WebDAV folder uploads from Android (X-plore File Manager)
|
||||||
@@ -442,6 +476,17 @@ sudo docker-compose logs -f jellyfin
|
|||||||
- **Immich**: Modern photo backup solution
|
- **Immich**: Modern photo backup solution
|
||||||
- **LibrePhotos**: Privacy-focused alternative
|
- **LibrePhotos**: Privacy-focused alternative
|
||||||
|
|
||||||
|
### Torrent Management
|
||||||
|
|
||||||
|
**Recommendation: Use NAS Direct Torrenting**
|
||||||
|
|
||||||
|
For homelab with NAS storage migration:
|
||||||
|
- **NAS Direct**: Internet → NAS (50% less network traffic, better performance)
|
||||||
|
- **Laptop → NAS**: Downloads go laptop → network → NAS (double network load)
|
||||||
|
- **Access**: Use Synology Download Station via Local Network admin section
|
||||||
|
- **Integration**: Mount NAS shares for Jellyfin media access
|
||||||
|
- **Efficiency**: Keeps local network clear for other services
|
||||||
|
|
||||||
## Monitoring & Logging
|
## Monitoring & Logging
|
||||||
|
|
||||||
### System Monitoring
|
### System Monitoring
|
||||||
@@ -498,7 +543,7 @@ sudo usermod -aG docker hoborg
|
|||||||
- No auto-discovery of services
|
- No auto-discovery of services
|
||||||
- Requires config reload for changes
|
- Requires config reload for changes
|
||||||
|
|
||||||
#### Traefik
|
#### Traefik
|
||||||
✅ **Pros:**
|
✅ **Pros:**
|
||||||
- Docker-native auto-discovery via labels
|
- Docker-native auto-discovery via labels
|
||||||
- Automatic HTTPS with Let's Encrypt
|
- Automatic HTTPS with Let's Encrypt
|
||||||
@@ -636,7 +681,7 @@ server {
|
|||||||
|
|
||||||
### Router Port Forwarding Requirements
|
### Router Port Forwarding Requirements
|
||||||
- **HTTP**: Port 80 → 192.168.0.100:80
|
- **HTTP**: Port 80 → 192.168.0.100:80
|
||||||
- **HTTPS**: Port 443 → 192.168.0.100:443
|
- **HTTPS**: Port 443 → 192.168.0.100:443
|
||||||
- **Remove**: Direct port 3000 forwarding (will go through nginx)
|
- **Remove**: Direct port 3000 forwarding (will go through nginx)
|
||||||
|
|
||||||
### SSL Certificates
|
### SSL Certificates
|
||||||
@@ -715,3 +760,187 @@ sudo -u postgres pg_dump gitea > /backup/gitea-db-$DATE.sql
|
|||||||
- **Bookstack**: Documentation wiki
|
- **Bookstack**: Documentation wiki
|
||||||
- What is this for? How does it compare to Logseq?
|
- What is this for? How does it compare to Logseq?
|
||||||
- **FreshRSS**: RSS feed aggregator
|
- **FreshRSS**: RSS feed aggregator
|
||||||
|
|
||||||
|
## System Monitoring & Management
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
**Status:** 🚧 **PLANNED** - Implementing hybrid monitoring and management solution
|
||||||
|
|
||||||
|
**Selected Tools:**
|
||||||
|
- **Portainer** - Docker container management (web UI with built-in auth)
|
||||||
|
- **Glances** - Real-time system monitoring (web + terminal, nginx basic auth required)
|
||||||
|
- **Netdata** - Real-time system monitoring with rich dashboards (web UI, nginx basic auth)
|
||||||
|
- **lazydocker** - Terminal-based Docker management (SSH sessions)
|
||||||
|
|
||||||
|
### Architecture Decision
|
||||||
|
|
||||||
|
**Hybrid Approach Rationale:**
|
||||||
|
- **SSH workflow**: lazydocker + glances terminal mode for command-line administration
|
||||||
|
- **Web overview**: Glances for quick system status checks
|
||||||
|
- **Real-time monitoring**: Netdata for detailed system metrics and historical data
|
||||||
|
- **Docker UI**: Portainer for comprehensive container management
|
||||||
|
|
||||||
|
### Authentication Strategy
|
||||||
|
|
||||||
|
- **Portainer**: ✅ Built-in user authentication and RBAC
|
||||||
|
- **Glances**: ⚠️ Nginx basic auth required (exposes system metrics)
|
||||||
|
- **Netdata**: ⚠️ Nginx basic auth required (exposes system metrics, cloud features disabled)
|
||||||
|
- **Router**: ✅ Has own administrative login
|
||||||
|
- **NAS Storage**: ✅ Has own administrative login
|
||||||
|
|
||||||
|
**Nginx basic auth implemented** for monitoring services that expose system information without built-in authentication.
|
||||||
|
|
||||||
|
### Service Details
|
||||||
|
|
||||||
|
#### Portainer (Docker Management)
|
||||||
|
**Status:** 📋 **Planned**
|
||||||
|
- **Access**: https://ak-homelab.duckdns.org/portainer/
|
||||||
|
- **Port**: 9000 (behind reverse proxy)
|
||||||
|
- **Authentication**: Built-in user accounts with role-based permissions
|
||||||
|
- **Features**: Container lifecycle, image management, volume management, stack deployment
|
||||||
|
|
||||||
|
#### Glances (System Monitoring)
|
||||||
|
**Status:** 📋 **Planned**
|
||||||
|
- **Access**: https://ak-homelab.duckdns.org/glances/ (nginx basic auth)
|
||||||
|
- **Port**: 61208 (behind reverse proxy with auth)
|
||||||
|
- **Authentication**: Nginx basic auth (due to no built-in authentication)
|
||||||
|
- **Features**: Real-time CPU/RAM/disk metrics, process monitoring, network stats
|
||||||
|
- **Terminal mode**: Available via SSH for command-line monitoring
|
||||||
|
|
||||||
|
#### Netdata (Real-time System Monitoring)
|
||||||
|
**Status:** ✅ **DEPLOYED**
|
||||||
|
- **Access**: https://ak-homelab.duckdns.org/netdata/ (nginx basic auth)
|
||||||
|
- **Port**: 19999 (behind reverse proxy with auth)
|
||||||
|
- **Authentication**: Nginx basic auth (same credentials as Glances: admin/AdminPass2024!)
|
||||||
|
- **Configuration**: Privacy-focused local-only setup with cloud features disabled
|
||||||
|
- **Features**: Real-time system metrics, network monitoring, process tracking, historical data
|
||||||
|
|
||||||
|
#### lazydocker (Terminal Docker Tools)
|
||||||
|
**Status:** 📋 **Planned**
|
||||||
|
- **Access**: SSH terminal only
|
||||||
|
- **Installation**: `pacman -S lazydocker`
|
||||||
|
- **Usage**: Command-line Docker container management for SSH workflows
|
||||||
|
|
||||||
|
### URL Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Landing Page - Admin Tab:
|
||||||
|
Server Administration:
|
||||||
|
├── Glances → https://ak-homelab.duckdns.org/glances/ (nginx basic auth)
|
||||||
|
├── Netdata → https://ak-homelab.duckdns.org/netdata/ (nginx basic auth)
|
||||||
|
└── Portainer → https://ak-homelab.duckdns.org/portainer/ (built-in auth)
|
||||||
|
|
||||||
|
Local Network:
|
||||||
|
├── NAS Storage → http://192.168.0.101:5000/ (built-in auth)
|
||||||
|
└── Router → http://192.168.0.1 (built-in auth)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Implementation Plan
|
||||||
|
|
||||||
|
1. **Package Installation**
|
||||||
|
```bash
|
||||||
|
sudo pacman -S glances cockpit lazydocker
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Portainer Deployment**
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name portainer \
|
||||||
|
-p 9000:9000 \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
-v portainer_data:/data \
|
||||||
|
portainer/portainer-ce
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Service Configuration**
|
||||||
|
- Enable Cockpit: `sudo systemctl enable --now cockpit.socket`
|
||||||
|
- Configure Glances web mode: `glances -w -p 61208`
|
||||||
|
- Create systemd service for Glances web server
|
||||||
|
|
||||||
|
4. **Nginx Configuration**
|
||||||
|
- Add reverse proxy configurations for all services
|
||||||
|
- Configure basic auth for Glances endpoint
|
||||||
|
- SSL termination for all admin services
|
||||||
|
|
||||||
|
5. **Landing Page Update**
|
||||||
|
- Add all admin service links to Admin tab
|
||||||
|
- Include authentication indicators
|
||||||
|
|
||||||
|
### Security Considerations
|
||||||
|
|
||||||
|
**Data Exposure Analysis (Glances):**
|
||||||
|
- **Exposed**: System metrics, process names, resource usage, network stats
|
||||||
|
- **Not Exposed**: File contents, passwords, configuration details, logs
|
||||||
|
- **Risk Level**: Medium (reconnaissance data for attackers)
|
||||||
|
- **Mitigation**: Nginx basic auth prevents unauthorized access
|
||||||
|
|
||||||
|
**Service Hardening:**
|
||||||
|
- All services behind HTTPS with SSL certificates
|
||||||
|
- Each service handles authentication independently
|
||||||
|
- No shared credentials between services
|
||||||
|
- Services isolated behind reverse proxy
|
||||||
|
|
||||||
|
## Remote Desktop Access
|
||||||
|
|
||||||
|
### TigerVNC
|
||||||
|
|
||||||
|
**Status:** ✅ **INSTALLED** - VNC server for remote desktop access
|
||||||
|
|
||||||
|
**Installation:**
|
||||||
|
```bash
|
||||||
|
# Install TigerVNC server and client
|
||||||
|
sudo pacman -S tigervnc
|
||||||
|
```
|
||||||
|
|
||||||
|
**Service Configuration:**
|
||||||
|
```bash
|
||||||
|
# Configure user for VNC display :1
|
||||||
|
echo ":1=hoborg" | sudo tee -a /etc/tigervnc/vncserver.users
|
||||||
|
|
||||||
|
# Set VNC password
|
||||||
|
vncpasswd
|
||||||
|
|
||||||
|
# Configure desktop environment (~/.vnc/xstartup)
|
||||||
|
#!/bin/bash
|
||||||
|
xrdb $HOME/.Xresources
|
||||||
|
startxfce4 &
|
||||||
|
|
||||||
|
# Make executable
|
||||||
|
chmod +x ~/.vnc/xstartup
|
||||||
|
```
|
||||||
|
|
||||||
|
**Service Management:**
|
||||||
|
```bash
|
||||||
|
# Enable and start VNC service
|
||||||
|
sudo systemctl enable vncserver@:1.service
|
||||||
|
sudo systemctl start vncserver@:1.service
|
||||||
|
|
||||||
|
# Check service status
|
||||||
|
sudo systemctl status vncserver@:1.service
|
||||||
|
|
||||||
|
# Service uses vncsession-start for proper X11 session management
|
||||||
|
```
|
||||||
|
|
||||||
|
**Access Details:**
|
||||||
|
- **Display**: `:1` (port 5901)
|
||||||
|
- **Local Access**: VNC client to `192.168.0.100:5901`
|
||||||
|
- **External Access**: Requires router port forwarding 5901→192.168.0.100:5901
|
||||||
|
- **Security**: Password authentication, consider SSH tunneling for external access
|
||||||
|
|
||||||
|
**Client Connection:**
|
||||||
|
- **Windows**: TigerVNC Viewer to `192.168.0.100:5901`
|
||||||
|
- **SSH Tunnel**: `ssh -L 5901:localhost:5901 hoborg@192.168.0.100 -p 2222`
|
||||||
|
- **Tunneled Access**: VNC client to `localhost:5901`
|
||||||
|
|
||||||
|
**Service Features:**
|
||||||
|
- ✅ Systemd integration with proper session management
|
||||||
|
- ✅ Automatic startup on boot
|
||||||
|
- ✅ User-specific VNC sessions via `/etc/tigervnc/vncserver.users`
|
||||||
|
- ✅ Uses `vncsession-start` for robust X11 handling
|
||||||
|
- ✅ Proper PID file management in `/run/vncsession-:1.pid`
|
||||||
|
|
||||||
|
**Security Considerations:**
|
||||||
|
- VNC traffic is unencrypted - use SSH tunneling for remote access
|
||||||
|
- Firewall configuration needed for direct external access
|
||||||
|
- Consider VPN access instead of direct port forwarding
|
||||||
|
|||||||
196
docs/ssh-honeypot-setup.md
Normal file
196
docs/ssh-honeypot-setup.md
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
# SSH Honeypot Setup
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The SSH honeypot is a deception service that listens on port 22 (the default SSH port) to detect and log unauthorized access attempts. The real SSH service runs on port 2222 for legitimate access.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
- **Honeypot**: Port 22 - Fake SSH service for logging attacks
|
||||||
|
- **Real SSH**: Port 2222 - Actual SSH access for administrators
|
||||||
|
- **Gitea SSH**: Port 2223 - Git repository access
|
||||||
|
|
||||||
|
## Configuration Files
|
||||||
|
|
||||||
|
### Service Configuration
|
||||||
|
**File**: `config/systemd/ssh-honeypot.service`
|
||||||
|
**Deploy to**: `/etc/systemd/system/ssh-honeypot.service`
|
||||||
|
|
||||||
|
The systemd service uses `ncat` to listen on port 22 and execute a response script for each connection attempt.
|
||||||
|
|
||||||
|
### Response Script
|
||||||
|
**File**: `config/honeypot/response.sh`
|
||||||
|
**Deploy to**: `/opt/honeypot/response.sh`
|
||||||
|
|
||||||
|
The script logs each connection attempt and sends a fake SSH banner to make attackers believe they've reached a real SSH service.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Deploy service file
|
||||||
|
sudo cp config/systemd/ssh-honeypot.service /etc/systemd/system/
|
||||||
|
|
||||||
|
# 2. Create honeypot directory and deploy script
|
||||||
|
sudo mkdir -p /opt/honeypot
|
||||||
|
sudo cp config/honeypot/response.sh /opt/honeypot/
|
||||||
|
sudo chmod +x /opt/honeypot/response.sh
|
||||||
|
|
||||||
|
# 3. Create log file
|
||||||
|
sudo touch /var/log/honeypot.log
|
||||||
|
sudo chmod 644 /var/log/honeypot.log
|
||||||
|
|
||||||
|
# 4. Create honeypot group (if needed)
|
||||||
|
sudo groupadd honeypot || true
|
||||||
|
|
||||||
|
# 5. Enable and start service
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable ssh-honeypot.service
|
||||||
|
sudo systemctl start ssh-honeypot.service
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check service status
|
||||||
|
sudo systemctl status ssh-honeypot.service
|
||||||
|
|
||||||
|
# Verify port 22 is listening
|
||||||
|
ss -tlnp | grep :22
|
||||||
|
|
||||||
|
# Test connection
|
||||||
|
telnet localhost 22
|
||||||
|
|
||||||
|
# Check logs
|
||||||
|
tail -f /var/log/honeypot.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Log Format
|
||||||
|
|
||||||
|
Each connection attempt is logged with:
|
||||||
|
- Timestamp
|
||||||
|
- Source IP address
|
||||||
|
- Connection event
|
||||||
|
|
||||||
|
Example log entry:
|
||||||
|
```
|
||||||
|
Thu Sep 12 20:18:32 CEST 2025: SSH honeypot connection from 192.168.1.100
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
### Benefits
|
||||||
|
- **Early Detection**: Identifies reconnaissance and attack attempts
|
||||||
|
- **Threat Intelligence**: Captures attacker IP addresses and timing
|
||||||
|
- **Deception**: Misleads attackers away from real services
|
||||||
|
|
||||||
|
### Limitations
|
||||||
|
- **Internal Only**: Only logs connections from within the network
|
||||||
|
- **Basic Logging**: Simple timestamp and IP logging only
|
||||||
|
- **No Interaction**: Closes connection after sending banner
|
||||||
|
|
||||||
|
## Monitoring
|
||||||
|
|
||||||
|
### Real-time Monitoring
|
||||||
|
```bash
|
||||||
|
# Monitor honeypot logs
|
||||||
|
tail -f /var/log/honeypot.log
|
||||||
|
|
||||||
|
# Monitor service logs
|
||||||
|
journalctl -u ssh-honeypot.service -f
|
||||||
|
|
||||||
|
# Check connection counts
|
||||||
|
grep "honeypot connection" /var/log/honeypot.log | wc -l
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log Analysis
|
||||||
|
```bash
|
||||||
|
# Show unique attacking IPs
|
||||||
|
grep "honeypot connection" /var/log/honeypot.log | \
|
||||||
|
awk '{print $NF}' | sort | uniq -c | sort -nr
|
||||||
|
|
||||||
|
# Show attack frequency by hour
|
||||||
|
grep "honeypot connection" /var/log/honeypot.log | \
|
||||||
|
awk '{print $4}' | cut -d: -f1 | sort | uniq -c
|
||||||
|
|
||||||
|
# Recent attacks (last 24 hours)
|
||||||
|
grep "$(date +%Y-%m-%d)" /var/log/honeypot.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration with Real SSH
|
||||||
|
|
||||||
|
### SSH Configuration
|
||||||
|
Ensure your real SSH service (`/etc/ssh/sshd_config`) is configured to listen on port 2222:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
Port 2222
|
||||||
|
PermitRootLogin no
|
||||||
|
PasswordAuthentication no
|
||||||
|
PubkeyAuthentication yes
|
||||||
|
```
|
||||||
|
|
||||||
|
### Router/Firewall Rules
|
||||||
|
- Port 22: No external forwarding (honeypot is internal only)
|
||||||
|
- Port 2222: Forward to 192.168.0.100:2222 for legitimate SSH access
|
||||||
|
- Port 2223: Forward to 192.168.0.100:2223 for Gitea SSH access
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Service Won't Start
|
||||||
|
```bash
|
||||||
|
# Check if port 22 is already in use
|
||||||
|
ss -tlnp | grep :22
|
||||||
|
|
||||||
|
# Check service logs
|
||||||
|
journalctl -u ssh-honeypot.service -n 20
|
||||||
|
|
||||||
|
# Verify permissions
|
||||||
|
ls -la /opt/honeypot/response.sh
|
||||||
|
ls -la /var/log/honeypot.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### No Logs Generated
|
||||||
|
```bash
|
||||||
|
# Test script manually
|
||||||
|
sudo /opt/honeypot/response.sh
|
||||||
|
|
||||||
|
# Check log file permissions
|
||||||
|
ls -la /var/log/honeypot.log
|
||||||
|
|
||||||
|
# Verify ncat can access script
|
||||||
|
sudo -u honeypot /opt/honeypot/response.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Permission Errors
|
||||||
|
```bash
|
||||||
|
# Fix log permissions
|
||||||
|
sudo chmod 644 /var/log/honeypot.log
|
||||||
|
|
||||||
|
# Fix script permissions
|
||||||
|
sudo chmod +x /opt/honeypot/response.sh
|
||||||
|
|
||||||
|
# Run as root if needed (remove Group=honeypot from service file)
|
||||||
|
sudo systemctl edit ssh-honeypot.service
|
||||||
|
```
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
### Log Rotation
|
||||||
|
Consider setting up logrotate for `/var/log/honeypot.log`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# /etc/logrotate.d/honeypot
|
||||||
|
/var/log/honeypot.log {
|
||||||
|
weekly
|
||||||
|
rotate 4
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
missingok
|
||||||
|
notifempty
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Regular Tasks
|
||||||
|
- Monitor logs weekly for attack patterns
|
||||||
|
- Archive old logs monthly
|
||||||
|
- Review and update response script as needed
|
||||||
|
- Verify service is running after system updates
|
||||||
288
docs/ssh-intrusion-monitoring.md
Normal file
288
docs/ssh-intrusion-monitoring.md
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
# SSH Intrusion Monitoring Guide
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This guide covers comprehensive SSH intrusion detection and monitoring for the homelab environment, including both honeypot analysis and real SSH service monitoring.
|
||||||
|
|
||||||
|
## Log Files and Locations
|
||||||
|
|
||||||
|
### Primary Log Files
|
||||||
|
- **Honeypot Logs**: `/var/log/honeypot.log` - Attack attempts on port 22
|
||||||
|
- **SSH Authentication**: `/var/log/auth.log` - Real SSH attempts on port 2222
|
||||||
|
- **System Security**: `/var/log/secure` or `/var/log/security.log` - General security events
|
||||||
|
- **Service Logs**: `journalctl -u sshd.service` - SSH daemon logs
|
||||||
|
|
||||||
|
### Service Status Monitoring
|
||||||
|
```bash
|
||||||
|
# Check honeypot service
|
||||||
|
systemctl status ssh-honeypot.service
|
||||||
|
|
||||||
|
# Check real SSH service
|
||||||
|
systemctl status sshd.service
|
||||||
|
|
||||||
|
# Monitor both services
|
||||||
|
systemctl status ssh-honeypot.service sshd.service
|
||||||
|
```
|
||||||
|
|
||||||
|
## Honeypot Monitoring
|
||||||
|
|
||||||
|
### Real-time Monitoring
|
||||||
|
```bash
|
||||||
|
# Live honeypot attack monitoring
|
||||||
|
tail -f /var/log/honeypot.log
|
||||||
|
|
||||||
|
# Monitor with timestamps
|
||||||
|
tail -f /var/log/honeypot.log | while read line; do echo "$(date '+%H:%M:%S') $line"; done
|
||||||
|
```
|
||||||
|
|
||||||
|
### Attack Analysis
|
||||||
|
```bash
|
||||||
|
# Count total attack attempts
|
||||||
|
grep -c "honeypot connection" /var/log/honeypot.log
|
||||||
|
|
||||||
|
# Show unique attacking IP addresses with attempt counts
|
||||||
|
grep "honeypot connection" /var/log/honeypot.log | \
|
||||||
|
awk '{print $NF}' | sort | uniq -c | sort -nr
|
||||||
|
|
||||||
|
# Attacks by hour of day
|
||||||
|
grep "honeypot connection" /var/log/honeypot.log | \
|
||||||
|
awk '{print $4}' | cut -d: -f1 | sort | uniq -c | sort -k2 -n
|
||||||
|
|
||||||
|
# Recent attacks (last 24 hours)
|
||||||
|
grep "$(date '+%a %b %d')" /var/log/honeypot.log
|
||||||
|
|
||||||
|
# Attacks from specific IP
|
||||||
|
grep "192.168.1.100" /var/log/honeypot.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Geographic Analysis
|
||||||
|
```bash
|
||||||
|
# Get country information for attacking IPs (requires geoip)
|
||||||
|
grep "honeypot connection" /var/log/honeypot.log | \
|
||||||
|
awk '{print $NF}' | sort -u | \
|
||||||
|
while read ip; do
|
||||||
|
echo -n "$ip: "
|
||||||
|
geoiplookup "$ip" 2>/dev/null | head -1 | cut -d: -f2
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
## Real SSH Monitoring
|
||||||
|
|
||||||
|
### Authentication Monitoring
|
||||||
|
```bash
|
||||||
|
# Monitor real SSH authentication attempts
|
||||||
|
tail -f /var/log/auth.log | grep sshd
|
||||||
|
|
||||||
|
# Failed password attempts
|
||||||
|
grep "Failed password" /var/log/auth.log | tail -10
|
||||||
|
|
||||||
|
# Successful logins
|
||||||
|
grep "Accepted password\|Accepted publickey" /var/log/auth.log | tail -10
|
||||||
|
|
||||||
|
# Invalid users attempting login
|
||||||
|
grep "Invalid user" /var/log/auth.log | tail -10
|
||||||
|
```
|
||||||
|
|
||||||
|
### Connection Analysis
|
||||||
|
```bash
|
||||||
|
# Current SSH connections
|
||||||
|
ss -tnp | grep :2222
|
||||||
|
|
||||||
|
# Active SSH sessions
|
||||||
|
who -u
|
||||||
|
|
||||||
|
# Login history
|
||||||
|
last -n 20
|
||||||
|
|
||||||
|
# Failed login attempts by IP
|
||||||
|
grep "Failed password" /var/log/auth.log | \
|
||||||
|
awk '{print $(NF-3)}' | sort | uniq -c | sort -nr
|
||||||
|
```
|
||||||
|
|
||||||
|
### Brute Force Detection
|
||||||
|
```bash
|
||||||
|
# Show IPs with multiple failed attempts
|
||||||
|
grep "Failed password" /var/log/auth.log | \
|
||||||
|
awk '{print $(NF-3)}' | sort | uniq -c | \
|
||||||
|
awk '$1 > 5 {print $2 " (" $1 " attempts)"}'
|
||||||
|
|
||||||
|
# Recent failed attempts (last hour)
|
||||||
|
grep "$(date '+%b %d %H:')" /var/log/auth.log | \
|
||||||
|
grep "Failed password"
|
||||||
|
|
||||||
|
# Successful logins after failed attempts (potential compromise)
|
||||||
|
grep -A5 -B5 "Accepted" /var/log/auth.log | \
|
||||||
|
grep -E "Failed password|Accepted"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Network-Level Monitoring
|
||||||
|
|
||||||
|
### Port Scanning Detection
|
||||||
|
```bash
|
||||||
|
# Check for connection attempts to common ports
|
||||||
|
ss -tlnp | grep -E ":(22|2222|2223|80|443|8080)"
|
||||||
|
|
||||||
|
# Monitor connection attempts (requires netstat)
|
||||||
|
netstat -tln | grep LISTEN | grep -E ":(22|2222|2223)"
|
||||||
|
|
||||||
|
# Check iptables logs (if logging enabled)
|
||||||
|
grep "DROP" /var/log/kern.log | tail -10
|
||||||
|
```
|
||||||
|
|
||||||
|
### Active Connection Monitoring
|
||||||
|
```bash
|
||||||
|
# Show all network connections
|
||||||
|
ss -tuln
|
||||||
|
|
||||||
|
# Monitor new connections
|
||||||
|
watch -n 2 'ss -tn | grep :2222'
|
||||||
|
|
||||||
|
# Check for unusual processes using network
|
||||||
|
lsof -i :2222
|
||||||
|
lsof -i :22
|
||||||
|
```
|
||||||
|
|
||||||
|
## Alerting and Notifications
|
||||||
|
|
||||||
|
### Simple Alert Scripts
|
||||||
|
Create monitoring scripts for common scenarios:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Alert on honeypot activity
|
||||||
|
#!/bin/bash
|
||||||
|
# /usr/local/bin/honeypot-alert.sh
|
||||||
|
LAST_CHECK="/tmp/honeypot-last-check"
|
||||||
|
LOG_FILE="/var/log/honeypot.log"
|
||||||
|
|
||||||
|
if [ ! -f "$LAST_CHECK" ]; then
|
||||||
|
touch "$LAST_CHECK"
|
||||||
|
fi
|
||||||
|
|
||||||
|
NEW_ATTACKS=$(find "$LOG_FILE" -newer "$LAST_CHECK" | wc -l)
|
||||||
|
if [ "$NEW_ATTACKS" -gt 0 ]; then
|
||||||
|
echo "ALERT: $NEW_ATTACKS new honeypot attacks detected"
|
||||||
|
tail -n "$NEW_ATTACKS" "$LOG_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
touch "$LAST_CHECK"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fail2ban Integration
|
||||||
|
Monitor fail2ban status for automatic IP blocking:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check fail2ban status
|
||||||
|
sudo fail2ban-client status
|
||||||
|
|
||||||
|
# Check SSH jail specifically
|
||||||
|
sudo fail2ban-client status sshd
|
||||||
|
|
||||||
|
# Show banned IPs
|
||||||
|
sudo fail2ban-client get sshd banip
|
||||||
|
|
||||||
|
# Unban IP if needed
|
||||||
|
sudo fail2ban-client set sshd unbanip 192.168.1.100
|
||||||
|
```
|
||||||
|
|
||||||
|
## Log Analysis Tools
|
||||||
|
|
||||||
|
### Basic Analysis Commands
|
||||||
|
```bash
|
||||||
|
# Most common attacking IPs across both services
|
||||||
|
(grep "honeypot connection" /var/log/honeypot.log | awk '{print $NF}'; \
|
||||||
|
grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}') | \
|
||||||
|
sort | uniq -c | sort -nr | head -10
|
||||||
|
|
||||||
|
# Timeline of attacks (both honeypot and real SSH)
|
||||||
|
(grep "honeypot connection" /var/log/honeypot.log | sed 's/honeypot/HONEYPOT/'; \
|
||||||
|
grep "Failed password" /var/log/auth.log | sed 's/Failed password/REAL_SSH/') | \
|
||||||
|
sort -k1,3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Advanced Analysis
|
||||||
|
```bash
|
||||||
|
# Attack patterns by time of day
|
||||||
|
grep -E "(honeypot connection|Failed password)" \
|
||||||
|
/var/log/honeypot.log /var/log/auth.log | \
|
||||||
|
awk '{print $4}' | cut -d: -f1 | sort | uniq -c | \
|
||||||
|
sort -k2 -n
|
||||||
|
|
||||||
|
# Correlation between honeypot and real SSH attacks
|
||||||
|
comm -12 \
|
||||||
|
<(grep "honeypot connection" /var/log/honeypot.log | awk '{print $NF}' | sort -u) \
|
||||||
|
<(grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort -u)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Incident Response
|
||||||
|
|
||||||
|
### Immediate Response
|
||||||
|
```bash
|
||||||
|
# Block suspicious IP immediately
|
||||||
|
sudo iptables -A INPUT -s SUSPICIOUS_IP -j DROP
|
||||||
|
|
||||||
|
# Check current connections from IP
|
||||||
|
ss -tn | grep SUSPICIOUS_IP
|
||||||
|
|
||||||
|
# Kill any active sessions from IP
|
||||||
|
sudo pkill -f "sshd.*SUSPICIOUS_IP"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Investigation Steps
|
||||||
|
1. **Identify Attack Source**: Analyze logs to determine origin IP and attack pattern
|
||||||
|
2. **Check Compromise**: Look for successful logins after failed attempts
|
||||||
|
3. **Assess Impact**: Check for file modifications, new users, or privilege escalation
|
||||||
|
4. **Implement Blocks**: Add IP to fail2ban or firewall rules
|
||||||
|
5. **Monitor**: Continue monitoring for related activity
|
||||||
|
|
||||||
|
### Forensic Analysis
|
||||||
|
```bash
|
||||||
|
# Check for privilege escalation attempts
|
||||||
|
grep -i "sudo\|su\|root" /var/log/auth.log | tail -20
|
||||||
|
|
||||||
|
# Look for file access patterns
|
||||||
|
find /var/log -name "*.log" -mtime -1 -exec grep "SUSPICIOUS_IP" {} \;
|
||||||
|
|
||||||
|
# Check for new user accounts or modifications
|
||||||
|
grep -E "(useradd|usermod|passwd)" /var/log/auth.log | tail -10
|
||||||
|
```
|
||||||
|
|
||||||
|
## Automated Monitoring Setup
|
||||||
|
|
||||||
|
### Cron Jobs for Regular Monitoring
|
||||||
|
```bash
|
||||||
|
# Add to crontab (crontab -e)
|
||||||
|
# Check for new attacks every 5 minutes
|
||||||
|
*/5 * * * * /usr/local/bin/honeypot-alert.sh
|
||||||
|
|
||||||
|
# Daily security report
|
||||||
|
0 9 * * * /usr/local/bin/daily-security-report.sh
|
||||||
|
|
||||||
|
# Weekly log cleanup
|
||||||
|
0 2 * * 0 /usr/local/bin/cleanup-old-logs.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### System Integration
|
||||||
|
- Configure rsyslog to separate security logs
|
||||||
|
- Set up log rotation for security logs
|
||||||
|
- Integrate with monitoring systems (Nagios, Zabbix, etc.)
|
||||||
|
- Configure email alerts for critical events
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### Monitoring Frequency
|
||||||
|
- **Real-time**: Honeypot attacks, SSH authentication failures
|
||||||
|
- **Hourly**: Connection pattern analysis, unusual activity
|
||||||
|
- **Daily**: Attack summary, trend analysis, IP reputation checks
|
||||||
|
- **Weekly**: Comprehensive security review, log archiving
|
||||||
|
|
||||||
|
### Log Retention
|
||||||
|
- **Honeypot logs**: 30 days for analysis, 90 days archived
|
||||||
|
- **SSH auth logs**: 90 days active, 1 year archived
|
||||||
|
- **Security logs**: 180 days active, 2 years archived
|
||||||
|
- **Incident logs**: Permanent retention for forensic analysis
|
||||||
|
|
||||||
|
### Performance Considerations
|
||||||
|
- Monitor log file sizes and implement rotation
|
||||||
|
- Use efficient grep/awk patterns for large log files
|
||||||
|
- Consider log aggregation tools for high-volume environments
|
||||||
|
- Archive old logs to prevent disk space issues
|
||||||
233
docs/system-backups.md
Normal file
233
docs/system-backups.md
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
# System Backups
|
||||||
|
|
||||||
|
Complete backup strategy for the homelab using Restic for encrypted, deduplicated backups to NAS.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
**Backup Tool**: Restic (modern, encrypted, deduplicated)
|
||||||
|
**Backup Destination**: NAS at `/mnt/nas/backups/homelab-restic`
|
||||||
|
**Backup Frequency**: Daily (can be automated with systemd timer)
|
||||||
|
**Encryption**: AES-256, password stored in `/home/hoborg/creds/restic-password.txt`
|
||||||
|
|
||||||
|
## What Gets Backed Up
|
||||||
|
|
||||||
|
### Full System Backup Includes:
|
||||||
|
1. **Package Lists**
|
||||||
|
- All explicitly installed packages (pacman)
|
||||||
|
- All installed packages (including dependencies)
|
||||||
|
- AUR/foreign packages list
|
||||||
|
- Allows exact system recreation after reinstall
|
||||||
|
|
||||||
|
2. **System Configuration** (`/etc/`)
|
||||||
|
- Nginx configuration
|
||||||
|
- Systemd services
|
||||||
|
- Network configuration
|
||||||
|
- All system-wide configs
|
||||||
|
- Excludes: shadow/gshadow password files
|
||||||
|
|
||||||
|
3. **Docker Volumes**
|
||||||
|
- Gitea (Git repositories and database)
|
||||||
|
- Jellyfin (media library configuration)
|
||||||
|
- Nextcloud (cloud storage data and database)
|
||||||
|
- Portainer (container management data)
|
||||||
|
|
||||||
|
### What's NOT Backed Up (Intentionally):
|
||||||
|
- **User data in `/home`**: Separate partition, safe from system reinstalls
|
||||||
|
- **Media files**: Already stored on NAS (Music, Videos, Pictures)
|
||||||
|
- **User documents**: Synced via Nextcloud/Copyparty or on separate partition
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Initial Setup (One-Time)
|
||||||
|
|
||||||
|
1. **Initialize the backup repository:**
|
||||||
|
```bash
|
||||||
|
sudo /home/hoborg/homelab/scripts/backup-init.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
- Install Restic if needed
|
||||||
|
- Create backup directory on NAS (`/mnt/nas/backups/homelab-restic`)
|
||||||
|
- Generate encryption password (saved to `/home/hoborg/creds/restic-password.txt`)
|
||||||
|
- Initialize the Restic repository
|
||||||
|
|
||||||
|
⚠️ **IMPORTANT**: Back up the password file! Without it, backups cannot be restored.
|
||||||
|
|
||||||
|
### Running Backups
|
||||||
|
|
||||||
|
**Manual backup:**
|
||||||
|
```bash
|
||||||
|
sudo /home/hoborg/homelab/scripts/backup-system-full.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Automated backups** (optional - setup systemd timer later):
|
||||||
|
- Create systemd service and timer
|
||||||
|
- Schedule daily backups (e.g., 3 AM)
|
||||||
|
- Logs written to `/var/log/homelab-backup.log`
|
||||||
|
|
||||||
|
## Backup Scripts
|
||||||
|
|
||||||
|
### `backup-init.sh`
|
||||||
|
Initializes the Restic repository. Run once during setup.
|
||||||
|
|
||||||
|
Location: `/home/hoborg/homelab/scripts/backup-init.sh`
|
||||||
|
|
||||||
|
### `backup-system-full.sh`
|
||||||
|
Performs full system backup including packages, configs, and Docker volumes.
|
||||||
|
|
||||||
|
Location: `/home/hoborg/homelab/scripts/backup-system-full.sh`
|
||||||
|
|
||||||
|
What it does:
|
||||||
|
1. Exports package lists (pacman explicit, all packages, AUR packages)
|
||||||
|
2. Exports Docker volumes to compressed tarballs
|
||||||
|
3. Backs up entire `/etc/` directory
|
||||||
|
4. Prunes old backups (keeps: 7 daily, 4 weekly, 6 monthly, 2 yearly)
|
||||||
|
5. Shows backup statistics
|
||||||
|
|
||||||
|
## Restore Procedures
|
||||||
|
|
||||||
|
### List Available Backups
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export RESTIC_PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
|
||||||
|
restic -r /mnt/nas/backups/homelab-restic snapshots
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore After System Reinstall
|
||||||
|
|
||||||
|
1. **Restore backup to temporary location:**
|
||||||
|
```bash
|
||||||
|
export RESTIC_PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
|
||||||
|
restic -r /mnt/nas/backups/homelab-restic restore latest --target /tmp/restore
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Reinstall packages:**
|
||||||
|
```bash
|
||||||
|
# Reinstall all explicitly installed packages
|
||||||
|
sudo pacman -S --needed $(cat /tmp/restore/tmp/system-backup-*/pacman-explicit.txt)
|
||||||
|
|
||||||
|
# Reinstall AUR packages
|
||||||
|
yay -S --needed $(cat /tmp/restore/tmp/system-backup-*/aur-packages.txt)
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Restore system configs:**
|
||||||
|
```bash
|
||||||
|
sudo cp -r /tmp/restore/etc/* /etc/
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Restore Docker volumes:**
|
||||||
|
```bash
|
||||||
|
# Example: Restore Gitea
|
||||||
|
docker-compose -f /opt/docker/gitea/docker-compose.yml down
|
||||||
|
|
||||||
|
sudo tar xzf /tmp/restore/tmp/docker-backup-*/gitea-data.tar.gz -C /tmp/gitea-restore
|
||||||
|
|
||||||
|
docker run --rm -v gitea_gitea:/data -v /tmp/gitea-restore:/backup alpine \
|
||||||
|
sh -c "rm -rf /data/* && cp -a /backup/* /data/"
|
||||||
|
|
||||||
|
docker-compose -f /opt/docker/gitea/docker-compose.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rollback After Broken Update
|
||||||
|
|
||||||
|
If a system update breaks something:
|
||||||
|
|
||||||
|
1. **Restore specific config:**
|
||||||
|
```bash
|
||||||
|
export RESTIC_PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
|
||||||
|
|
||||||
|
# Restore nginx config
|
||||||
|
restic -r /mnt/nas/backups/homelab-restic restore latest \
|
||||||
|
--target /tmp/restore --include /etc/nginx/
|
||||||
|
|
||||||
|
sudo cp -r /tmp/restore/etc/nginx/* /etc/nginx/
|
||||||
|
sudo systemctl reload nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Downgrade packages:**
|
||||||
|
```bash
|
||||||
|
# Check which packages were updated
|
||||||
|
grep -A5 "upgraded" /var/log/pacman.log | tail -20
|
||||||
|
|
||||||
|
# Downgrade specific package to cached version
|
||||||
|
sudo pacman -U /var/cache/pacman/pkg/package-old-version.pkg.tar.zst
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore Specific Docker Volume
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export RESTIC_PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
|
||||||
|
|
||||||
|
# List files in backup
|
||||||
|
restic -r /mnt/nas/backups/homelab-restic ls latest | grep docker-backup
|
||||||
|
|
||||||
|
# Restore specific volume tarball
|
||||||
|
restic -r /mnt/nas/backups/homelab-restic restore latest \
|
||||||
|
--target /tmp/restore \
|
||||||
|
--include /tmp/docker-backup-*/nextcloud-data.tar.gz
|
||||||
|
|
||||||
|
# Extract and restore to Docker volume
|
||||||
|
mkdir /tmp/nextcloud-data
|
||||||
|
tar xzf /tmp/restore/tmp/docker-backup-*/nextcloud-data.tar.gz -C /tmp/nextcloud-data
|
||||||
|
|
||||||
|
docker run --rm -v nextcloud_nextcloud_data:/data -v /tmp/nextcloud-data:/backup alpine \
|
||||||
|
sh -c "rm -rf /data/* && cp -a /backup/* /data/"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Backup Retention Policy
|
||||||
|
|
||||||
|
The backup script automatically prunes old backups:
|
||||||
|
- **Daily**: Keep last 7 days
|
||||||
|
- **Weekly**: Keep last 4 weeks
|
||||||
|
- **Monthly**: Keep last 6 months
|
||||||
|
- **Yearly**: Keep last 2 years
|
||||||
|
|
||||||
|
This balances storage space with recovery options.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "Repository not initialized" Error
|
||||||
|
|
||||||
|
Run the initialization script:
|
||||||
|
```bash
|
||||||
|
sudo /home/hoborg/homelab/scripts/backup-init.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### "Password incorrect" Error
|
||||||
|
|
||||||
|
Check that password file exists and is readable:
|
||||||
|
```bash
|
||||||
|
ls -l /home/hoborg/creds/restic-password.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Backup Size
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export RESTIC_PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
|
||||||
|
restic -r /mnt/nas/backups/homelab-restic stats --mode restore-size
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verify Backup Integrity
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export RESTIC_PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
|
||||||
|
restic -r /mnt/nas/backups/homelab-restic check
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
- Backups are encrypted with AES-256
|
||||||
|
- Password file has `600` permissions (owner read/write only)
|
||||||
|
- Shadow/gshadow files are excluded from backups
|
||||||
|
- NAS should have access restrictions
|
||||||
|
- Consider offsite backup copy of password file
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
- [ ] Automated systemd timer setup
|
||||||
|
- [ ] Pre-backup notifications
|
||||||
|
- [ ] Post-backup success/failure notifications
|
||||||
|
- [ ] Backup verification script
|
||||||
|
- [ ] Web dashboard for backup status
|
||||||
|
- [ ] Offsite backup replication (optional)
|
||||||
86
docs/troubleshooting/gitea-troubleshooting-summary.md
Normal file
86
docs/troubleshooting/gitea-troubleshooting-summary.md
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# Gitea Docker Troubleshooting - Complete Resolution
|
||||||
|
|
||||||
|
## The Problem
|
||||||
|
Gitea container failing with: `s6-svscan: fatal: unable to open .s6-svscan/lock: Read-only file system`
|
||||||
|
|
||||||
|
## Root Cause Analysis
|
||||||
|
The issue was **Docker security hardening interfering with Gitea's s6-overlay init system**.
|
||||||
|
|
||||||
|
### What Broke Gitea
|
||||||
|
1. **`read_only: true`** - Prevented s6-overlay from creating lock files
|
||||||
|
2. **`user: "1000:1000"`** - Forced non-root start, breaking s6-overlay initialization
|
||||||
|
3. **`Missing config values`**
|
||||||
|
- GITEA__server__ROOT_URL=https://ak-homelab.duckdns.org/gitea/
|
||||||
|
- GITEA__server__SSH_PORT=2223
|
||||||
|
4. **`Overy restrictive port config`**
|
||||||
|
- "127.0.0.1:3000:3000" # Web UI localhost only: ok, since requests come in via reverse proxy
|
||||||
|
- "127.0.0.1:2223:22" # Git port on localhost: NOT OK, this prevents git push/pull from outside
|
||||||
|
- "2223:22" # <- Fixed config
|
||||||
|
5. **Overly restrictive security caps** - Prevented proper container initialization
|
||||||
|
|
||||||
|
### Key Understanding: s6-overlay Init System
|
||||||
|
Gitea uses s6-overlay which requires:
|
||||||
|
1. **Start as root** to set up supervision tree and create lock files
|
||||||
|
2. **Then drop privileges** to user specified by `USER_UID`/`USER_GID` environment variables
|
||||||
|
|
||||||
|
Setting `user: "1000:1000"` at Docker level bypassed this process.
|
||||||
|
|
||||||
|
## The Solution
|
||||||
|
|
||||||
|
### Working Docker Compose Configuration
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
server:
|
||||||
|
image: gitea/gitea:latest
|
||||||
|
container_name: gitea
|
||||||
|
environment:
|
||||||
|
- USER_UID=1000
|
||||||
|
- USER_GID=1000
|
||||||
|
- GITEA__server__ROOT_URL=https://ak-homelab.duckdns.org/gitea/
|
||||||
|
- GITEA__server__SSH_PORT=2223
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# DO NOT set user: directive - breaks s6-overlay init system
|
||||||
|
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:3000:3000" # Web UI - localhost only (reverse proxy)
|
||||||
|
- "2223:22" # SSH - all interfaces (direct Git access)
|
||||||
|
|
||||||
|
# Reasonable resource limits only
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 1G
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Configuration Points
|
||||||
|
|
||||||
|
1. **No Docker User Directive**: Let s6-overlay handle user switching via `USER_UID`/`USER_GID`
|
||||||
|
2. **ROOT_URL**: Required for asset serving through reverse proxy
|
||||||
|
3. **SSH_PORT**: Must match external port for correct clone URLs
|
||||||
|
4. **Port Binding**: SSH needs all interfaces, web UI can be localhost-only
|
||||||
|
|
||||||
|
## Critical Port Configuration
|
||||||
|
- **Web UI**: `"127.0.0.1:3000:3000"` (through nginx reverse proxy)
|
||||||
|
- **SSH Git**: `"2223:22"` (direct access, NOT localhost-only)
|
||||||
|
|
||||||
|
**Why SSH can't be localhost-only**: Git operations come from external router/clients and need direct container access.
|
||||||
|
|
||||||
|
## Asset Loading Fix
|
||||||
|
Added `GITEA__server__ROOT_URL=https://ak-homelab.duckdns.org/gitea/` so Gitea serves assets with correct paths through reverse proxy.
|
||||||
|
|
||||||
|
## Repository Migration
|
||||||
|
For fresh Gitea instances, bare repositories must be pushed via Git protocol:
|
||||||
|
```bash
|
||||||
|
git clone /path/to/bare/repo.git working-copy
|
||||||
|
cd working-copy
|
||||||
|
git remote set-url origin https://ak-homelab.duckdns.org/gitea/user/repo.git
|
||||||
|
git push -u origin --all
|
||||||
|
git push origin --tags
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lessons Learned
|
||||||
|
1. **Understand service requirements** before applying security hardening
|
||||||
|
2. **Some services need specific initialization processes** (s6-overlay)
|
||||||
|
3. **Network access patterns matter** (SSH vs HTTP proxy requirements)
|
||||||
|
4. **Test incrementally** rather than applying multiple security changes at once
|
||||||
@@ -39,6 +39,27 @@ server {
|
|||||||
- Error: `HTTP/1.1 403 Forbidden`
|
- Error: `HTTP/1.1 403 Forbidden`
|
||||||
|
|
||||||
**Solution**: Add `d` (delete) permission to user accounts:
|
**Solution**: Add `d` (delete) permission to user accounts:
|
||||||
|
|
||||||
|
### 3. URL Encoding Issues
|
||||||
|
**Problem**: Files/folders with spaces or special characters in names caused HTTP 400 errors.
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- Files without spaces upload successfully
|
||||||
|
- Files with spaces in path fail: `HTTP/1.1 400 Bad Request`
|
||||||
|
- Logs show "bad headers" errors from copyparty
|
||||||
|
- URLs like `/files/folder/file%20name.txt` fail
|
||||||
|
|
||||||
|
**Solution**: Pass original request URI to preserve URL encoding:
|
||||||
|
```nginx
|
||||||
|
location ~ ^/files(/.*)?$ {
|
||||||
|
# Pass original request URI to preserve URL encoding
|
||||||
|
proxy_pass http://127.0.0.1:8082;
|
||||||
|
# ... other proxy settings
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Instead of `proxy_pass http://127.0.0.1:8082/files$1;` which manipulates the path.
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
[/shared]
|
[/shared]
|
||||||
/home/hoborg/shared
|
/home/hoborg/shared
|
||||||
@@ -115,7 +136,8 @@ server {
|
|||||||
# Explicitly allow WebDAV methods
|
# Explicitly allow WebDAV methods
|
||||||
limit_except GET POST PUT DELETE PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK { deny all; }
|
limit_except GET POST PUT DELETE PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK { deny all; }
|
||||||
|
|
||||||
proxy_pass http://127.0.0.1:8082/files$1;
|
# Pass original request URI to preserve URL encoding
|
||||||
|
proxy_pass http://127.0.0.1:8082;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
@@ -140,6 +162,10 @@ server {
|
|||||||
# Critical: Streaming uploads for WebDAV
|
# Critical: Streaming uploads for WebDAV
|
||||||
proxy_buffering off;
|
proxy_buffering off;
|
||||||
proxy_request_buffering off;
|
proxy_request_buffering off;
|
||||||
|
|
||||||
|
# Critical: Use HTTP/1.1 and fix connection headers
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
|
||||||
# Critical: Disable nginx response modifications
|
# Critical: Disable nginx response modifications
|
||||||
proxy_redirect off;
|
proxy_redirect off;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
# Voice Assistant Setup
|
# Voice Assistant Setup
|
||||||
|
|
||||||
|
⚠️ **STATUS: DISABLED** - onnxruntime package was removed to free disk space (1.2GB). Voice functionality is currently unavailable.
|
||||||
|
|
||||||
This document describes how to set up AI voice capabilities for Claude Code using local TTS (Text-to-Speech) services.
|
This document describes how to set up AI voice capabilities for Claude Code using local TTS (Text-to-Speech) services.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
@@ -8,6 +10,7 @@ The voice assistant setup uses:
|
|||||||
- **Piper TTS**: Local neural text-to-speech engine for generating natural-sounding speech
|
- **Piper TTS**: Local neural text-to-speech engine for generating natural-sounding speech
|
||||||
- **FastAPI**: HTTP server wrapper to make Piper compatible with voice-mode
|
- **FastAPI**: HTTP server wrapper to make Piper compatible with voice-mode
|
||||||
- **Ryan voice model**: Professional male US English voice for AI assistant personality
|
- **Ryan voice model**: Professional male US English voice for AI assistant personality
|
||||||
|
- **onnxruntime**: ML inference library (removed - required for TTS)
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
|
|||||||
49
opencode.json
Normal file
49
opencode.json
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://opencode.ai/config.json",
|
||||||
|
"permission": {
|
||||||
|
"bash": {
|
||||||
|
"git branch -d": "ask",
|
||||||
|
"git branch -D": "ask",
|
||||||
|
"git status": "allow",
|
||||||
|
"git diff": "allow",
|
||||||
|
"git log": "allow",
|
||||||
|
"git show": "allow",
|
||||||
|
"git branch": "allow",
|
||||||
|
"git remote": "allow",
|
||||||
|
"git fetch": "allow",
|
||||||
|
"git pull": "allow",
|
||||||
|
"git merge": "ask",
|
||||||
|
"git rebase": "ask",
|
||||||
|
"git commit": "ask",
|
||||||
|
"git push": "ask",
|
||||||
|
"git checkout": "ask",
|
||||||
|
"git switch": "ask",
|
||||||
|
"git reset": "ask",
|
||||||
|
"git revert": "ask",
|
||||||
|
"git rm": "ask",
|
||||||
|
"git mv": "ask",
|
||||||
|
"git worktree": "ask",
|
||||||
|
"cat": "allow",
|
||||||
|
"tail": "allow",
|
||||||
|
"head": "allow",
|
||||||
|
"less": "allow",
|
||||||
|
"more": "allow",
|
||||||
|
"grep": "allow",
|
||||||
|
"find": "allow",
|
||||||
|
"ls": "allow",
|
||||||
|
"pwd": "allow",
|
||||||
|
"cd": "allow",
|
||||||
|
"mkdir": "ask",
|
||||||
|
"rm": "ask",
|
||||||
|
"rmdir": "ask",
|
||||||
|
"mv": "ask",
|
||||||
|
"cp": "ask",
|
||||||
|
"touch": "ask",
|
||||||
|
"chmod": "ask",
|
||||||
|
"chown": "ask",
|
||||||
|
"su": "deny",
|
||||||
|
"sudo": "deny",
|
||||||
|
"*": "ask"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
95
scripts/backup-homelab.sh
Executable file
95
scripts/backup-homelab.sh
Executable file
@@ -0,0 +1,95 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Backup Docker volumes and system configs to NAS using Restic
|
||||||
|
# Can be run manually or via systemd timer
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
BACKUP_REPO="/mnt/nas/backups/homelab-restic"
|
||||||
|
PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
|
||||||
|
LOG_FILE="/var/log/homelab-backup.log"
|
||||||
|
DOCKER_BACKUP_DIR="/tmp/docker-backup-$$"
|
||||||
|
|
||||||
|
# Export password
|
||||||
|
export RESTIC_PASSWORD_FILE="$PASSWORD_FILE"
|
||||||
|
|
||||||
|
# Logging function
|
||||||
|
log() {
|
||||||
|
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
log "========================================"
|
||||||
|
log "Starting Homelab Backup"
|
||||||
|
log "========================================"
|
||||||
|
|
||||||
|
# Check if repository exists
|
||||||
|
if ! restic -r "$BACKUP_REPO" snapshots &>/dev/null; then
|
||||||
|
log "ERROR: Backup repository not initialized. Run backup-init.sh first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create temporary directory for Docker volume exports
|
||||||
|
mkdir -p "$DOCKER_BACKUP_DIR"
|
||||||
|
log "Created temporary backup directory: $DOCKER_BACKUP_DIR"
|
||||||
|
|
||||||
|
# Export Docker volumes
|
||||||
|
log "Exporting Docker volumes..."
|
||||||
|
|
||||||
|
# Gitea
|
||||||
|
log " - Exporting Gitea data..."
|
||||||
|
docker run --rm -v gitea_gitea:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
|
||||||
|
tar czf /backup/gitea-data.tar.gz -C /data .
|
||||||
|
|
||||||
|
# Jellyfin
|
||||||
|
log " - Exporting Jellyfin config..."
|
||||||
|
docker run --rm -v jellyfin_jellyfin_config:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
|
||||||
|
tar czf /backup/jellyfin-config.tar.gz -C /data .
|
||||||
|
|
||||||
|
# Nextcloud data
|
||||||
|
log " - Exporting Nextcloud data..."
|
||||||
|
docker run --rm -v nextcloud_nextcloud_data:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
|
||||||
|
tar czf /backup/nextcloud-data.tar.gz -C /data .
|
||||||
|
|
||||||
|
# Nextcloud database
|
||||||
|
log " - Exporting Nextcloud database..."
|
||||||
|
docker run --rm -v nextcloud_nextcloud_db:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
|
||||||
|
tar czf /backup/nextcloud-db.tar.gz -C /data .
|
||||||
|
|
||||||
|
# Portainer
|
||||||
|
log " - Exporting Portainer data..."
|
||||||
|
docker run --rm -v portainer_data:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
|
||||||
|
tar czf /backup/portainer-data.tar.gz -C /data .
|
||||||
|
|
||||||
|
log "✓ Docker volumes exported"
|
||||||
|
|
||||||
|
# Run backup
|
||||||
|
log "Running Restic backup..."
|
||||||
|
restic -r "$BACKUP_REPO" backup \
|
||||||
|
--verbose \
|
||||||
|
--tag homelab-docker \
|
||||||
|
--tag homelab-configs \
|
||||||
|
/etc/nginx/sites-available/homelab \
|
||||||
|
/etc/systemd/system/copyparty.service \
|
||||||
|
/home/hoborg/.config/copyparty/ \
|
||||||
|
"$DOCKER_BACKUP_DIR"
|
||||||
|
|
||||||
|
# Cleanup temporary files
|
||||||
|
log "Cleaning up temporary files..."
|
||||||
|
rm -rf "$DOCKER_BACKUP_DIR"
|
||||||
|
|
||||||
|
# Prune old backups (keep: 7 daily, 4 weekly, 6 monthly, 2 yearly)
|
||||||
|
log "Pruning old backups..."
|
||||||
|
restic -r "$BACKUP_REPO" forget \
|
||||||
|
--keep-daily 7 \
|
||||||
|
--keep-weekly 4 \
|
||||||
|
--keep-monthly 6 \
|
||||||
|
--keep-yearly 2 \
|
||||||
|
--prune
|
||||||
|
|
||||||
|
# Show backup stats
|
||||||
|
log "Backup statistics:"
|
||||||
|
restic -r "$BACKUP_REPO" stats --mode restore-size
|
||||||
|
|
||||||
|
log "========================================"
|
||||||
|
log "Backup completed successfully!"
|
||||||
|
log "========================================"
|
||||||
74
scripts/backup-init.sh
Executable file
74
scripts/backup-init.sh
Executable file
@@ -0,0 +1,74 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Initialize Restic backup repository on NAS
|
||||||
|
# Run once to set up the backup system
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
BACKUP_REPO="/mnt/nas/backups/homelab-restic"
|
||||||
|
PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
|
||||||
|
|
||||||
|
echo "========================================"
|
||||||
|
echo "Restic Backup Repository Initialization"
|
||||||
|
echo "========================================"
|
||||||
|
echo ""
|
||||||
|
echo "This will:"
|
||||||
|
echo " 1. Install restic if needed"
|
||||||
|
echo " 2. Create backup repository at: $BACKUP_REPO"
|
||||||
|
echo " 3. Generate and save encryption password"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if restic is installed
|
||||||
|
if ! command -v restic &> /dev/null; then
|
||||||
|
echo "Installing restic..."
|
||||||
|
pacman -S --needed --noconfirm restic
|
||||||
|
echo "✓ Restic installed"
|
||||||
|
else
|
||||||
|
echo "✓ Restic already installed ($(restic version))"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create backup directory on NAS
|
||||||
|
echo ""
|
||||||
|
echo "Creating backup directory on NAS..."
|
||||||
|
mkdir -p "$BACKUP_REPO"
|
||||||
|
echo "✓ Directory created: $BACKUP_REPO"
|
||||||
|
|
||||||
|
# Generate random password if doesn't exist
|
||||||
|
echo ""
|
||||||
|
if [ -f "$PASSWORD_FILE" ]; then
|
||||||
|
echo "✓ Password file already exists: $PASSWORD_FILE"
|
||||||
|
else
|
||||||
|
echo "Generating encryption password..."
|
||||||
|
mkdir -p /home/hoborg/creds
|
||||||
|
openssl rand -base64 32 > "$PASSWORD_FILE"
|
||||||
|
chmod 600 "$PASSWORD_FILE"
|
||||||
|
echo "✓ Password saved to: $PASSWORD_FILE"
|
||||||
|
echo "⚠️ IMPORTANT: Back up this password file! Without it, backups cannot be restored."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Export password for restic
|
||||||
|
export RESTIC_PASSWORD_FILE="$PASSWORD_FILE"
|
||||||
|
|
||||||
|
# Initialize repository
|
||||||
|
echo ""
|
||||||
|
echo "Initializing Restic repository..."
|
||||||
|
if restic -r "$BACKUP_REPO" snapshots &>/dev/null; then
|
||||||
|
echo "✓ Repository already initialized"
|
||||||
|
else
|
||||||
|
restic -r "$BACKUP_REPO" init
|
||||||
|
echo "✓ Repository initialized"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "========================================"
|
||||||
|
echo "Backup Repository Ready!"
|
||||||
|
echo "========================================"
|
||||||
|
echo ""
|
||||||
|
echo "Repository location: $BACKUP_REPO"
|
||||||
|
echo "Password file: $PASSWORD_FILE"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " 1. Run backup-homelab.sh to create first backup"
|
||||||
|
echo " 2. Test restore with backup-restore.sh"
|
||||||
|
echo " 3. Enable automated backups with systemd timer"
|
||||||
|
echo ""
|
||||||
128
scripts/backup-system-full.sh
Executable file
128
scripts/backup-system-full.sh
Executable file
@@ -0,0 +1,128 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Full system backup - packages, configs, and Docker volumes
|
||||||
|
# Can be run manually or via systemd timer
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
BACKUP_REPO="/mnt/nas/backups/homelab-restic"
|
||||||
|
PASSWORD_FILE="/home/hoborg/creds/restic-password.txt"
|
||||||
|
LOG_FILE="/var/log/homelab-backup.log"
|
||||||
|
DOCKER_BACKUP_DIR="/tmp/docker-backup-$$"
|
||||||
|
SYSTEM_BACKUP_DIR="/tmp/system-backup-$$"
|
||||||
|
|
||||||
|
# Export password
|
||||||
|
export RESTIC_PASSWORD_FILE="$PASSWORD_FILE"
|
||||||
|
|
||||||
|
# Logging function
|
||||||
|
log() {
|
||||||
|
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
log "========================================"
|
||||||
|
log "Starting Full System Backup"
|
||||||
|
log "========================================"
|
||||||
|
|
||||||
|
# Check if repository exists
|
||||||
|
if ! restic -r "$BACKUP_REPO" snapshots &>/dev/null; then
|
||||||
|
log "ERROR: Backup repository not initialized. Run backup-init.sh first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create temporary directories
|
||||||
|
mkdir -p "$DOCKER_BACKUP_DIR"
|
||||||
|
mkdir -p "$SYSTEM_BACKUP_DIR"
|
||||||
|
log "Created temporary directories"
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 1. Export Package Lists
|
||||||
|
# ============================================
|
||||||
|
log "Exporting package lists..."
|
||||||
|
pacman -Qqe > "$SYSTEM_BACKUP_DIR/pacman-explicit.txt" # Explicitly installed packages
|
||||||
|
pacman -Qq > "$SYSTEM_BACKUP_DIR/pacman-all.txt" # All installed packages
|
||||||
|
pacman -Qqm > "$SYSTEM_BACKUP_DIR/aur-packages.txt" # AUR/foreign packages only
|
||||||
|
log "✓ Package lists exported ($(wc -l < $SYSTEM_BACKUP_DIR/pacman-explicit.txt) explicit, $(wc -l < $SYSTEM_BACKUP_DIR/aur-packages.txt) AUR)"
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 2. Export Docker Volumes
|
||||||
|
# ============================================
|
||||||
|
log "Exporting Docker volumes..."
|
||||||
|
|
||||||
|
# Gitea
|
||||||
|
log " - Exporting Gitea data..."
|
||||||
|
docker run --rm -v gitea_gitea:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
|
||||||
|
tar czf /backup/gitea-data.tar.gz -C /data .
|
||||||
|
|
||||||
|
# Jellyfin
|
||||||
|
log " - Exporting Jellyfin config..."
|
||||||
|
docker run --rm -v jellyfin_jellyfin_config:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
|
||||||
|
tar czf /backup/jellyfin-config.tar.gz -C /data .
|
||||||
|
|
||||||
|
# Nextcloud data
|
||||||
|
log " - Exporting Nextcloud data..."
|
||||||
|
docker run --rm -v nextcloud_nextcloud_data:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
|
||||||
|
tar czf /backup/nextcloud-data.tar.gz -C /data .
|
||||||
|
|
||||||
|
# Nextcloud database
|
||||||
|
log " - Exporting Nextcloud database..."
|
||||||
|
docker run --rm -v nextcloud_nextcloud_db:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
|
||||||
|
tar czf /backup/nextcloud-db.tar.gz -C /data .
|
||||||
|
|
||||||
|
# Portainer
|
||||||
|
log " - Exporting Portainer data..."
|
||||||
|
docker run --rm -v portainer_data:/data -v "$DOCKER_BACKUP_DIR":/backup alpine \
|
||||||
|
tar czf /backup/portainer-data.tar.gz -C /data .
|
||||||
|
|
||||||
|
log "✓ Docker volumes exported"
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 3. Run Backup
|
||||||
|
# ============================================
|
||||||
|
log "Running Restic backup..."
|
||||||
|
restic -r "$BACKUP_REPO" backup \
|
||||||
|
--verbose \
|
||||||
|
--tag full-system \
|
||||||
|
--exclude '/etc/shadow*' \
|
||||||
|
--exclude '/etc/gshadow*' \
|
||||||
|
--exclude '/etc/passwd-' \
|
||||||
|
--exclude '/etc/group-' \
|
||||||
|
/etc/ \
|
||||||
|
"$DOCKER_BACKUP_DIR" \
|
||||||
|
"$SYSTEM_BACKUP_DIR"
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 4. Cleanup
|
||||||
|
# ============================================
|
||||||
|
log "Cleaning up temporary files..."
|
||||||
|
rm -rf "$DOCKER_BACKUP_DIR"
|
||||||
|
rm -rf "$SYSTEM_BACKUP_DIR"
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 5. Prune Old Backups
|
||||||
|
# ============================================
|
||||||
|
log "Pruning old backups..."
|
||||||
|
restic -r "$BACKUP_REPO" forget \
|
||||||
|
--keep-daily 7 \
|
||||||
|
--keep-weekly 4 \
|
||||||
|
--keep-monthly 3 \
|
||||||
|
--keep-yearly 1 \
|
||||||
|
--prune
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 6. Show Stats
|
||||||
|
# ============================================
|
||||||
|
log "Backup statistics:"
|
||||||
|
restic -r "$BACKUP_REPO" stats --mode restore-size
|
||||||
|
|
||||||
|
log "========================================"
|
||||||
|
log "Full system backup completed!"
|
||||||
|
log "========================================"
|
||||||
|
log ""
|
||||||
|
log "Backed up:"
|
||||||
|
log " - /etc/ (all system configs)"
|
||||||
|
log " - Docker volumes (Gitea, Jellyfin, Nextcloud, Portainer)"
|
||||||
|
log " - Package lists (pacman + AUR)"
|
||||||
|
log ""
|
||||||
|
log "To restore packages after reinstall:"
|
||||||
|
log " pacman -S --needed \$(cat pacman-explicit.txt)"
|
||||||
|
log " yay -S --needed \$(cat aur-packages.txt)"
|
||||||
64
scripts/permanent-ban-repeat-offenders.sh
Executable file
64
scripts/permanent-ban-repeat-offenders.sh
Executable file
@@ -0,0 +1,64 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Permanent ban script for IPs that have been banned more than 4 times by fail2ban
|
||||||
|
# Run via cron: 0 */6 * * * /home/hoborg/homelab/scripts/permanent-ban-repeat-offenders.sh
|
||||||
|
|
||||||
|
LOGFILE="/var/log/permanent-ban.log"
|
||||||
|
BANLIST_FILE="/etc/fail2ban/permanent-banlist.conf"
|
||||||
|
THRESHOLD=4
|
||||||
|
|
||||||
|
# Function to log with timestamp
|
||||||
|
log_message() {
|
||||||
|
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOGFILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ensure permanent banlist file exists
|
||||||
|
if [ ! -f "$BANLIST_FILE" ]; then
|
||||||
|
touch "$BANLIST_FILE"
|
||||||
|
chmod 644 "$BANLIST_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_message "Starting repeat offender analysis (threshold: $THRESHOLD bans)"
|
||||||
|
|
||||||
|
# Get list of IPs with ban counts from fail2ban logs
|
||||||
|
REPEAT_OFFENDERS=$(grep "Ban " /var/log/fail2ban.log* | \
|
||||||
|
grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | \
|
||||||
|
sort | uniq -c | sort -nr | \
|
||||||
|
awk -v threshold="$THRESHOLD" '$1 > threshold {print $2 " " $1}')
|
||||||
|
|
||||||
|
if [ -z "$REPEAT_OFFENDERS" ]; then
|
||||||
|
log_message "No repeat offenders found above threshold"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Process each repeat offender
|
||||||
|
echo "$REPEAT_OFFENDERS" | while read ip ban_count; do
|
||||||
|
# Skip if already permanently banned
|
||||||
|
if grep -q "^$ip" "$BANLIST_FILE"; then
|
||||||
|
log_message "IP $ip already permanently banned (skip)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get country information
|
||||||
|
COUNTRY=$(whois "$ip" 2>/dev/null | grep -i "country:" | head -1 | awk '{print $2}')
|
||||||
|
|
||||||
|
# Add to permanent banlist
|
||||||
|
echo "$ip # Banned $(date '+%Y-%m-%d') - $ban_count offenses - Country: $COUNTRY" >> "$BANLIST_FILE"
|
||||||
|
|
||||||
|
# Add permanent iptables rule
|
||||||
|
iptables -I INPUT -s "$ip" -j DROP
|
||||||
|
|
||||||
|
# Make iptables rule persistent (systemd approach)
|
||||||
|
if command -v iptables-save >/dev/null 2>&1; then
|
||||||
|
iptables-save > /etc/iptables/iptables.rules 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_message "PERMANENTLY BANNED: $ip ($ban_count offenses, Country: $COUNTRY)"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Reload fail2ban to ensure it sees the updated banlist
|
||||||
|
if systemctl is-active --quiet fail2ban; then
|
||||||
|
log_message "Reloading fail2ban service"
|
||||||
|
systemctl reload fail2ban
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_message "Repeat offender analysis completed"
|
||||||
130
scripts/setup-docker-symlinks.sh
Executable file
130
scripts/setup-docker-symlinks.sh
Executable file
@@ -0,0 +1,130 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Set up symlinks for Docker configurations to avoid manual copying
|
||||||
|
|
||||||
|
set -e # Exit on error
|
||||||
|
|
||||||
|
echo "==================================================================="
|
||||||
|
echo "Docker Configuration Symlink Setup"
|
||||||
|
echo "==================================================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
REPO_ROOT="/home/hoborg/homelab"
|
||||||
|
|
||||||
|
# Function to create symlink with backup
|
||||||
|
create_symlink() {
|
||||||
|
local service=$1
|
||||||
|
local target=$2
|
||||||
|
local link=$3
|
||||||
|
|
||||||
|
echo "-------------------------------------------------------------------"
|
||||||
|
echo "Setting up: $service"
|
||||||
|
echo "-------------------------------------------------------------------"
|
||||||
|
echo "Target: $target"
|
||||||
|
echo "Link: $link"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if target exists
|
||||||
|
if [ ! -f "$target" ]; then
|
||||||
|
echo "ERROR: Target file does not exist: $target"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create parent directory if it doesn't exist
|
||||||
|
local link_dir=$(dirname "$link")
|
||||||
|
if [ ! -d "$link_dir" ]; then
|
||||||
|
echo "Creating directory: $link_dir"
|
||||||
|
mkdir -p "$link_dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup existing file if it's not already a symlink
|
||||||
|
if [ -f "$link" ] && [ ! -L "$link" ]; then
|
||||||
|
backup="${link}.backup.$(date +%Y%m%d_%H%M%S)"
|
||||||
|
echo "Backing up existing file to: $backup"
|
||||||
|
cp "$link" "$backup"
|
||||||
|
rm "$link"
|
||||||
|
elif [ -L "$link" ]; then
|
||||||
|
echo "Removing existing symlink"
|
||||||
|
rm "$link"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create symlink
|
||||||
|
ln -s "$target" "$link"
|
||||||
|
|
||||||
|
if [ -L "$link" ]; then
|
||||||
|
echo "✓ Symlink created successfully"
|
||||||
|
else
|
||||||
|
echo "✗ Failed to create symlink"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Gitea docker-compose.yml
|
||||||
|
create_symlink "Gitea docker-compose" \
|
||||||
|
"$REPO_ROOT/config/docker/gitea/docker-compose.yml" \
|
||||||
|
"/opt/docker/gitea/docker-compose.yml"
|
||||||
|
|
||||||
|
# Jellyfin docker-compose.yml
|
||||||
|
create_symlink "Jellyfin docker-compose" \
|
||||||
|
"$REPO_ROOT/config/docker/jellyfin/docker-compose.yml" \
|
||||||
|
"/opt/docker/jellyfin/docker-compose.yml"
|
||||||
|
|
||||||
|
# qBittorrent docker-compose.yml
|
||||||
|
create_symlink "qBittorrent docker-compose" \
|
||||||
|
"$REPO_ROOT/config/docker/qbittorrent/docker-compose.yml" \
|
||||||
|
"/opt/docker/qbittorrent/docker-compose.yml"
|
||||||
|
|
||||||
|
# Portainer docker-compose.yml
|
||||||
|
create_symlink "Portainer docker-compose" \
|
||||||
|
"$REPO_ROOT/config/docker/portainer/docker-compose.yml" \
|
||||||
|
"/opt/docker/portainer/docker-compose.yml"
|
||||||
|
|
||||||
|
# Copyparty docker-compose.yml
|
||||||
|
create_symlink "Copyparty docker-compose" \
|
||||||
|
"$REPO_ROOT/config/docker/copyparty/docker-compose.yml" \
|
||||||
|
"/opt/docker/copyparty/docker-compose.yml"
|
||||||
|
|
||||||
|
# Glances docker-compose.yml
|
||||||
|
create_symlink "Glances docker-compose" \
|
||||||
|
"$REPO_ROOT/config/docker/glances/docker-compose.yml" \
|
||||||
|
"/opt/docker/glances/docker-compose.yml"
|
||||||
|
|
||||||
|
# Syncthing docker-compose.yml
|
||||||
|
create_symlink "Syncthing docker-compose" \
|
||||||
|
"$REPO_ROOT/config/docker/syncthing/docker-compose.yml" \
|
||||||
|
"/opt/docker/syncthing/docker-compose.yml"
|
||||||
|
|
||||||
|
# Docker daemon config
|
||||||
|
create_symlink "Docker daemon config" \
|
||||||
|
"$REPO_ROOT/config/docker/daemon.json" \
|
||||||
|
"/etc/docker/daemon.json"
|
||||||
|
|
||||||
|
# Landing page
|
||||||
|
create_symlink "Landing page" \
|
||||||
|
"$REPO_ROOT/config/www/index.html" \
|
||||||
|
"/var/www/homelab/index.html"
|
||||||
|
|
||||||
|
echo "==================================================================="
|
||||||
|
echo "Summary"
|
||||||
|
echo "==================================================================="
|
||||||
|
echo ""
|
||||||
|
echo "✓ All symlinks created successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "Notes:"
|
||||||
|
echo " - Original files backed up with timestamp"
|
||||||
|
echo " - Edit files in $REPO_ROOT/config/docker/"
|
||||||
|
echo " - Changes take effect immediately (no copying needed)"
|
||||||
|
echo " - For daemon.json: restart docker after changes"
|
||||||
|
echo " - For docker-compose: restart containers after changes"
|
||||||
|
echo ""
|
||||||
|
echo "Verify symlinks:"
|
||||||
|
echo " ls -l /opt/docker/gitea/docker-compose.yml"
|
||||||
|
echo " ls -l /opt/docker/jellyfin/docker-compose.yml"
|
||||||
|
echo " ls -l /opt/docker/qbittorrent/docker-compose.yml"
|
||||||
|
echo " ls -l /opt/docker/portainer/docker-compose.yml"
|
||||||
|
echo " ls -l /opt/docker/copyparty/docker-compose.yml"
|
||||||
|
echo " ls -l /opt/docker/glances/docker-compose.yml"
|
||||||
|
echo " ls -l /opt/docker/syncthing/docker-compose.yml"
|
||||||
|
echo " ls -l /etc/docker/daemon.json"
|
||||||
|
echo ""
|
||||||
28
scripts/setup-glances.sh
Executable file
28
scripts/setup-glances.sh
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Install and configure Glances monitoring service
|
||||||
|
# Run with: sudo -A ./scripts/setup-glances.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=== Installing Glances monitoring service ==="
|
||||||
|
pacman -S --noconfirm glances python-fastapi uvicorn python-jinja
|
||||||
|
|
||||||
|
echo "=== Creating glances user ==="
|
||||||
|
useradd -r -s /bin/false glances 2>/dev/null || echo "User glances already exists"
|
||||||
|
|
||||||
|
echo "=== Deploying Glances systemd service ==="
|
||||||
|
cp /home/hoborg/homelab/config/systemd/glances-web.service /etc/systemd/system/
|
||||||
|
systemctl daemon-reload
|
||||||
|
|
||||||
|
echo "=== Enabling and starting Glances service ==="
|
||||||
|
systemctl enable glances-web
|
||||||
|
systemctl start glances-web
|
||||||
|
|
||||||
|
echo "=== Checking Glances service status ==="
|
||||||
|
systemctl status glances-web --no-pager -l
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Glances installation complete! ==="
|
||||||
|
echo "Local access: http://127.0.0.1:61208/"
|
||||||
|
echo "External access: https://ak-homelab.duckdns.org/glances/ (requires nginx config)"
|
||||||
|
echo "Basic auth: admin / AdminPass2024!"
|
||||||
32
scripts/setup-netdata.sh
Executable file
32
scripts/setup-netdata.sh
Executable file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Install and configure Netdata monitoring service
|
||||||
|
# Run with: sudo -A ./scripts/setup-netdata.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=== Installing Netdata monitoring service ==="
|
||||||
|
pacman -S --noconfirm netdata
|
||||||
|
|
||||||
|
echo "=== Enabling and starting Netdata service ==="
|
||||||
|
systemctl enable netdata
|
||||||
|
systemctl start netdata
|
||||||
|
|
||||||
|
echo "=== Checking Netdata service status ==="
|
||||||
|
systemctl status netdata --no-pager -l
|
||||||
|
|
||||||
|
echo "=== Stopping and disabling Cockpit services ==="
|
||||||
|
systemctl stop cockpit cockpit.socket 2>/dev/null || echo "Cockpit services not running"
|
||||||
|
systemctl disable cockpit cockpit.socket 2>/dev/null || echo "Cockpit services not enabled"
|
||||||
|
|
||||||
|
echo "=== Deploying updated landing page ==="
|
||||||
|
cp /home/hoborg/homelab/config/www/index.html /var/www/homelab/
|
||||||
|
echo "✅ Landing page updated with Netdata link"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Netdata installation complete! ==="
|
||||||
|
echo "Local access: http://127.0.0.1:19999/"
|
||||||
|
echo "External access: https://ak-homelab.duckdns.org/netdata/ (after nginx config deploy)"
|
||||||
|
echo ""
|
||||||
|
echo "To deploy nginx config separately:"
|
||||||
|
echo "sudo cp /home/hoborg/homelab/config/nginx/homelab.conf /etc/nginx/sites-available/homelab"
|
||||||
|
echo "sudo nginx -t && sudo systemctl reload nginx"
|
||||||
112
scripts/setup-security-hardening.sh
Executable file
112
scripts/setup-security-hardening.sh
Executable file
@@ -0,0 +1,112 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Security Hardening Setup Script
|
||||||
|
# Run with: sudo -A ./scripts/setup-security-hardening.sh
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "=== Security Hardening Setup ==="
|
||||||
|
echo "Deploying security configurations from config/ directory"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
log_info() {
|
||||||
|
echo -e "${GREEN}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warn() {
|
||||||
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if running as root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
echo "This script must be run as root (use sudo -A)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
REPO_DIR="/home/hoborg/homelab"
|
||||||
|
|
||||||
|
# 1. Deploy SSH hardening
|
||||||
|
log_info "Deploying SSH security configuration..."
|
||||||
|
if [ -f "$REPO_DIR/config/ssh/sshd_config_hardening" ]; then
|
||||||
|
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup || true
|
||||||
|
cat "$REPO_DIR/config/ssh/sshd_config_hardening" >> /etc/ssh/sshd_config
|
||||||
|
cp "$REPO_DIR/config/ssh/banner" /etc/ssh/banner
|
||||||
|
chmod 644 /etc/ssh/banner
|
||||||
|
log_info "SSH hardening applied"
|
||||||
|
else
|
||||||
|
log_warn "SSH hardening config not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Deploy kernel security parameters
|
||||||
|
log_info "Deploying kernel security parameters..."
|
||||||
|
if [ -f "$REPO_DIR/config/sysctl/99-security.conf" ]; then
|
||||||
|
cp "$REPO_DIR/config/sysctl/99-security.conf" /etc/sysctl.d/
|
||||||
|
sysctl -p /etc/sysctl.d/99-security.conf
|
||||||
|
log_info "Kernel security parameters applied"
|
||||||
|
else
|
||||||
|
log_warn "Sysctl security config not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Deploy Docker security configuration
|
||||||
|
log_info "Deploying Docker security configuration..."
|
||||||
|
if [ -f "$REPO_DIR/config/docker/daemon.json" ]; then
|
||||||
|
mkdir -p /etc/docker
|
||||||
|
cp "$REPO_DIR/config/docker/daemon.json" /etc/docker/
|
||||||
|
systemctl restart docker
|
||||||
|
log_info "Docker security configuration applied"
|
||||||
|
else
|
||||||
|
log_warn "Docker daemon config not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Deploy fail2ban configuration
|
||||||
|
log_info "Deploying fail2ban configuration..."
|
||||||
|
if [ -f "$REPO_DIR/config/fail2ban/jail.local" ]; then
|
||||||
|
# Install fail2ban if needed
|
||||||
|
if ! command -v fail2ban-server >/dev/null; then
|
||||||
|
log_info "Installing fail2ban..."
|
||||||
|
pacman -S --noconfirm fail2ban
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Deploy config files
|
||||||
|
cp "$REPO_DIR/config/fail2ban/jail.local" /etc/fail2ban/
|
||||||
|
cp "$REPO_DIR/config/fail2ban/filter.d/"*.conf /etc/fail2ban/filter.d/
|
||||||
|
|
||||||
|
# Enable and restart
|
||||||
|
systemctl enable fail2ban
|
||||||
|
systemctl restart fail2ban
|
||||||
|
|
||||||
|
log_info "fail2ban configuration applied"
|
||||||
|
else
|
||||||
|
log_warn "fail2ban config not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 5. Deploy nginx rate limiting
|
||||||
|
log_info "Deploying nginx rate limiting..."
|
||||||
|
if [ -f "$REPO_DIR/config/systemd/nginx.service.d/rate-limit.conf" ]; then
|
||||||
|
mkdir -p /etc/systemd/system/nginx.service.d
|
||||||
|
cp "$REPO_DIR/config/systemd/nginx.service.d/rate-limit.conf" /etc/systemd/system/nginx.service.d/
|
||||||
|
systemctl daemon-reload
|
||||||
|
log_info "Nginx rate limiting applied"
|
||||||
|
else
|
||||||
|
log_warn "Nginx rate limiting config not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Security Hardening Complete ==="
|
||||||
|
echo
|
||||||
|
echo "Applied configurations:"
|
||||||
|
echo " ✓ SSH hardening and banner"
|
||||||
|
echo " ✓ Kernel security parameters"
|
||||||
|
echo " ✓ Docker security configuration"
|
||||||
|
echo " ✓ fail2ban intrusion prevention"
|
||||||
|
echo " ✓ Nginx rate limiting"
|
||||||
|
echo
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " 1. Test SSH connections: ssh -p 2222 user@localhost"
|
||||||
|
echo " 2. Check fail2ban status: fail2ban-client status"
|
||||||
|
echo " 3. Verify services: systemctl status sshd nginx docker"
|
||||||
|
echo " 4. Monitor logs: tail -f /var/log/honeypot.log"
|
||||||
Reference in New Issue
Block a user