Compare commits

...

51 Commits

Author SHA1 Message Date
059daa77aa Fix SSL certificate renewal for Let's Encrypt
Allow ACME challenge requests before HTTPS redirect:
- Add location block for /.well-known/acme-challenge/
- Prevent immediate redirect that was blocking Let's Encrypt verification
- Fixes 'Timeout during connect' errors during certbot renewal
2025-11-15 22:30:01 +01:00
9aa881d895 Document voice assistant TTS service status
Mark TTS functionality as disabled due to onnxruntime removal
(freed 1.2GB disk space during cleanup)
2025-10-11 18:25:08 +02:00
ab4a4cfc9c Add comprehensive backup system documentation
Document complete backup strategy covering:
- Setup and initialization procedures
- What gets backed up (and what doesn't)
- Multiple restore scenarios (full reinstall, rollback, specific volumes)
- Retention policy explanation
- Troubleshooting and security notes
2025-10-11 18:24:58 +02:00
646d3e59bc Add comprehensive system backup scripts
Implement full system backup with Restic including:
- Docker volume exports (Gitea, Jellyfin, Nextcloud, Portainer)
- System configuration backup (/etc/)
- Package list exports (pacman explicit, all, AUR)
- Automated retention policy (7 daily, 4 weekly, 3 monthly, 1 yearly)
- Separate temporary directories for Docker and system data
2025-10-11 18:24:51 +02:00
ad7270aa74 Add Restic backup repository initialization script
Initialize encrypted backup repository on NAS with:
- Auto-install of Restic if needed
- Secure password generation and storage
- Repository initialization with AES-256 encryption
2025-10-11 18:24:41 +02:00
7e64557f0b Add Nextcloud reverse proxy configuration to nginx 2025-10-11 17:25:56 +02:00
acc30e7c3d Enable Nextcloud link on landing page 2025-10-11 17:25:44 +02:00
3d57327146 Re-enable Nextcloud service with Docker configuration 2025-10-11 17:25:31 +02:00
d0727bd8a7 Document dockerization decisions and status
- Mark dockerization task as complete
- Document reasons for non-dockerized services:
  - Glances/Netdata: Need full system access for monitoring
  - Syncthing: Docker permission issues with config directory
  - Nginx: Reverse proxy requires system integration
- Update service status: Copyparty now dockerized
- Remove Cockpit references (removed from system)
- Update VNC documentation to reflect temporary usage pattern
2025-10-06 22:45:58 +02:00
7eaf16f53e Update symlink setup script for all Docker services
- Add automatic directory creation in create_symlink function
- Include copyparty, portainer, and qbittorrent configs
- Add landing page symlink for easier updates
- Update verification commands to include all services
2025-10-06 22:45:36 +02:00
bab97f7166 Add Copyparty Docker configuration
- Replace config file with command-line arguments for Docker compatibility
- Enable file search, deduplication, and partial upload features
- Configure reverse proxy support with xff-src and rproxy flags
- Add password database integration with chpw support
- Map all NAS volumes with appropriate permissions
- Fix health check to use /files/ path
- Remove obsolete copyparty.conf (incompatible with Docker image)
2025-10-06 22:43:58 +02:00
6894c5f175 Update geoip-shell documentation with implementation details
- Document interactive installation process
- Add complete EU country whitelist configuration
- Include setup commands and wizard prompts
- Clarify that geoip-shell was used instead of manual iptables setup
2025-10-06 20:40:27 +02:00
6761c8903d Add Docker configuration symlink setup script
- Automates symlinking of docker-compose.yml files from repo to /opt/docker
- Includes Gitea, Jellyfin, qBittorrent, Portainer
- Symlinks daemon.json to /etc/docker
- Creates timestamped backups before replacing files
- Eliminates need for manual config copying
2025-10-06 20:40:11 +02:00
a8bbf5ea4a Disable Nextcloud service
- Renamed docker-compose.yml to .disabled
- Service can be re-enabled by renaming file back to .yml
- Nextcloud not currently needed with Copyparty in place
2025-10-06 20:39:54 +02:00
fefdc14398 Add Portainer docker-compose configuration
- Web UI for Docker container management
- Bound to localhost (reverse proxy recommended)
- Uses named volume for persistent data
- Configured for Europe/Budapest timezone
- Resource limits and health checks included
2025-10-06 20:39:33 +02:00
802d275d6e Fix Docker daemon config: Remove invalid JSON comments
- Removed comments from daemon.json (JSON doesn't support comments)
- Synced with deployed working version
- Maintains minimal working configuration with logging only
2025-10-06 20:39:21 +02:00
dc16d0302d Add installers folder to copyparty configuration
- New /installers volume for game installers and ISO files
- Accessible to both guest and hoborg users
- Complements existing torrent categorization structure
2025-10-06 20:39:01 +02:00
64a43c8a96 docs: Add CLAUDE.md and permanent ban script
- Add CLAUDE.md with AI assistant configuration
- Add scripts/permanent-ban-repeat-offenders.sh for automated permanent banning
- Script automatically detects and permanently bans IPs banned >4 times by fail2ban
- Integrates with iptables and geoip-shell for comprehensive security
2025-09-17 01:09:41 +02:00
43cfd25798 docs: Update cron schedules to match actual configuration
- Correct permanent ban script cron schedule: 2:00 AM daily (not every 6 hours)
- Correct geoip-shell update cron schedule: 4:08 AM daily (not 4:18 AM)
- Add geoip-shell persistence cron job that runs on reboot
2025-09-17 01:08:51 +02:00
00f4fcbc1c docs: Add comprehensive geoip-shell and permanent ban documentation
- Add docs/geoip-blocking.md with complete geoip-shell setup documentation
- Update README.md to include geoip blocking in goals, status, and documentation structure
- Update docs/network-security.md with geoip blocking and permanent ban sections
- Mark geoip blocking task as completed in TODO.md
- Document permanent-ban-repeat-offenders.sh script and its cron job
2025-09-17 01:08:13 +02:00
31a9568fd4 Add gitea security notes 2025-09-13 20:51:53 +02:00
12ff0e15bd Add Gitea troubleshooting documentation
- Document common Docker issues and filesystem permission problems
- Include service management and configuration validation steps
- Provide systematic debugging approach for Gitea deployment issues
2025-09-13 20:50:43 +02:00
cff0ee6acb Update project documentation
- Add security hardening guidelines to CLAUDE.md with container-specific notes
- Update TODO.md with new security and dockerization tasks
- Add geoblocking and syncthing sync items to task list
2025-09-13 20:50:31 +02:00
de6fa58726 Update homepage: Replace Transmission with qBittorrent 2025-09-13 20:50:14 +02:00
e608ce6d5b Update Docker configurations for balanced security and functionality
- Simplify daemon.json to minimal working version, removing problematic
  security settings that caused read-only filesystem issues
- Update Gitea docker-compose.yml to working configuration:
  - Remove read-only filesystem (breaks s6-overlay init)
  - Keep user privilege dropping via USER_UID/USER_GID
  - Bind SSH port directly for Git operations
  - Maintain localhost binding for web interface
2025-09-13 20:49:34 +02:00
66e3249ced Update qBittorrent setup documentation
- Enhanced configuration and security guidance
- Updated setup procedures and best practices
2025-09-12 20:41:06 +02:00
398862c72b Update CLAUDE.md with latest project documentation
- Updated project instructions and configuration guidance
- Enhanced development workflow documentation
2025-09-12 20:40:50 +02:00
9f4f79a85e Add development tooling documentation
- AGENTS.md: Documentation for AI agents and development workflows
- opencode.json: OpenCode configuration file for testing alternative AI tools
2025-09-12 20:40:36 +02:00
06f837f4a1 Add project gitignore 2025-09-12 20:40:21 +02:00
0065113a19 docs: Add development tools section with OpenCode evaluation
- Document OpenCode as tested alternative to Claude Code
- Note: OpenCode is excellent but causes laptop overheating during intensive use
2025-09-12 20:39:17 +02:00
5a4eb2fd72 Add automated security hardening setup script
- scripts/setup-security-hardening.sh: One-command deployment of all security configurations
- Includes SSH hardening, kernel parameters, Docker security, fail2ban, and nginx rate limiting
- Provides status output and next steps for verification
2025-09-12 20:39:02 +02:00
ad9a3ab23e Add comprehensive security documentation
- docs/ssh-honeypot-setup.md: Complete SSH honeypot installation and monitoring guide
- docs/ssh-intrusion-monitoring.md: SSH attack detection and analysis procedures
- docs/security-configurations.md: Updated catalog of all security configuration files
- Includes installation procedures, monitoring commands, and troubleshooting guides
2025-09-12 20:38:49 +02:00
8bbe8e0e28 Add Docker daemon and service hardening configurations
- config/docker/daemon.json: Docker security hardening with logging limits and security options
- config/systemd/nginx.service.d/rate-limit.conf: Nginx resource limits and connection throttling
- Includes deployment instructions for container and service security
2025-09-12 20:38:33 +02:00
5e714f4e45 Add SSH hardening and kernel security configurations
- config/ssh/sshd_config_hardening: Enhanced SSH security settings
- config/ssh/banner: Legal warning banner for SSH connections
- config/sysctl/99-security.conf: Kernel network and memory protection parameters
- Includes deployment instructions for system-level hardening
2025-09-12 20:38:21 +02:00
9fbc311c2d Add comprehensive fail2ban security configuration
- config/fail2ban/jail.local: Main jail configuration with SSH, web, and service protection
- config/fail2ban/filter.d/sshd-ddos.conf: SSH connection flooding protection
- config/fail2ban/filter.d/nginx-badbots.conf: Web scanner and bot detection
- config/fail2ban/filter.d/gitea-auth.conf: Gitea authentication failure detection
- Includes deployment instructions for automated IP banning
2025-09-12 20:38:07 +02:00
471659a95a Add SSH honeypot configuration files
- config/systemd/ssh-honeypot.service: Systemd service for port 22 honeypot
- config/honeypot/response.sh: Response script that logs connections and sends fake SSH banner
- Both files include deployment instructions and setup commands
2025-09-12 20:37:30 +02:00
3d2201bc40 docs: Add comprehensive security hardening guide
- Document critical security vulnerabilities found
- Provide step-by-step hardening procedures
- Include SSL certificate recovery from git history
- Add SSH hardening with Mosh compatibility
- Document VPN setup with WireGuard
- Create implementation checklists and status tracking
2025-09-12 19:21:47 +02:00
6980c36ae9 Harden Docker container configurations
- Gitea: Bind ports to localhost, add security options, resource limits, health checks
- Jellyfin: Add security options, enhanced resource limits, health checks (kept host networking for GPU)
- qBittorrent: Bind torrent ports to localhost, add security options, health checks
- All configs: Non-root users, capability drops, no-new-privileges, tmpfs hardening

Security improvements:
- Ports no longer exposed to all interfaces (0.0.0.0)
- Added security options (no-new-privileges, cap_drop)
- Resource limits and health checks implemented
- Read-only filesystems where possible
- Temporary filesystems with restrictions
2025-09-12 19:14:59 +02:00
ff2aedacf6 Add qBittorrent Docker configuration
- Add docker-compose.yml for qBittorrent container setup
- Add config directory with qBittorrent configuration files
- Include GeoDB, RSS feeds, categories, and watched folders config
2025-09-12 19:00:56 +02:00
ee5f2a4c18 Add service configurations and documentation
- Update Jellyfin Docker Compose configuration
- Add qBittorrent manual setup documentation
2025-09-12 18:53:58 +02:00
3dfe146297 Update core configuration files
- Update CLAUDE.md with comprehensive homelab documentation
- Update TODO.md with current task status
- Configure copyparty file server settings
- Update nginx reverse proxy configuration
- Refresh homelab homepage
- Update services documentation
2025-09-12 18:53:49 +02:00
e1a020163f Add admin services and VNC security tasks to TODO
- Document completed admin interface and monitoring setup
- Add security enhancement task for VNC connections
- Add self-hosted chat server setup for future consideration
- Track progress on monitoring and management implementation
2025-09-09 21:14:46 +02:00
914e8a0ba7 Update documentation for admin services implementation
- Document complete admin services setup in admin-services-setup.md
- Update services.md with Netdata replacing Cockpit configuration
- Include troubleshooting steps and security implementation details
- Document tabbed landing page architecture and service organization
- Add privacy-focused Netdata configuration details
2025-09-09 21:14:28 +02:00
2fa9ec3a20 Add clean deployment scripts for monitoring services
- setup-glances.sh: Install Glances with web interface and systemd service
- setup-netdata.sh: Install Netdata without nginx configuration changes
- deploy-netdata-config.sh: Complete Netdata deployment with privacy config
- Remove redundant iterative scripts from troubleshooting process
- Each script handles one specific deployment task cleanly
2025-09-09 21:14:14 +02:00
c5849679f9 Add privacy-focused Netdata configuration and Glances service
- Add Netdata config with cloud features disabled
- Configure localhost-only binding for security
- Disable telemetry and registry features
- Add systemd service configuration for Glances web server
- Ensure monitoring services run with proper isolation
2025-09-09 21:12:27 +02:00
fe9651f2fa Replace Cockpit with Netdata in nginx reverse proxy config
- Remove Cockpit reverse proxy configuration
- Add Netdata reverse proxy with basic auth protection
- Configure same authentication as Glances for consistency
- Maintain security headers and WebSocket support
- Use port 19999 for Netdata service
2025-09-09 21:12:13 +02:00
5c4d959ed8 Add tabbed admin interface with organized service sections
- Add tabbed navigation with Home and Admin tabs
- Organize Admin tab into Server Administration and Local Network sections
- Update service names to actual application names (Copyparty, Jellyfin)
- Add NAS Storage link for network management
- Improve service descriptions and icons
- Implement responsive design with Font Awesome icons
2025-09-09 21:11:57 +02:00
3d607d2f80 Update services documentation with WebDAV and permissions details
- Add comprehensive copyparty feature list and status
- Document WebDAV client setup (X-plore, rclone)
- Update permission structure with rwmd flags
- Add troubleshooting references for WebDAV issues
- Include working client configuration examples

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-19 22:05:44 +02:00
500f0afe29 Update README with current homelab service status
- Mark all major services as completed (file server, media server, SSL)
- Update repository structure with new config directories
- Fix troubleshooting documentation references
- Reflect current working state of homelab setup

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-19 22:05:35 +02:00
bb8d9a15c2 Update WebDAV troubleshooting guide with URL encoding fix
- Add section on URL encoding issues causing HTTP 400 errors
- Document nginx proxy_pass solution for preserving request URI
- Update final working configuration with HTTP/1.1 fixes
- Include Connection header and proxy_http_version settings

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-19 22:05:12 +02:00
4aaabdfb8e Fix nginx WebDAV URL encoding and HTTP protocol issues
- Change proxy_pass to preserve original request URI for URL encoding
- Add HTTP/1.1 and Connection header fixes for copyparty compatibility
- Remove path manipulation that broke files with spaces/special characters

Fixes HTTP 400 "bad headers" errors when uploading files with spaces
in filenames via WebDAV clients like X-plore.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-19 22:05:03 +02:00
58 changed files with 4398 additions and 143 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
scripts/tmp/*

76
AGENTS.md Normal file
View 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.**

View File

@@ -183,4 +183,57 @@ rclone mount homelab-webdav: ~/homelab-files --daemon
# Test WebDAV
curl -X PROPFIND https://hoborg:AdminPass2024!@ak-homelab.duckdns.org/files/ \
-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

View File

@@ -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] SSH remote access (hostname: homelab)
- [x] Dotfiles and development environment setup
- [x] Network domain setup
- [ ] Self-hosted cloud storage (ownCloud/Nextcloud)
- [x] Self-hosted git repository (Gitea/Forgejo/GitLab)
- [x] Network domain setup with SSL certificates
- [x] Self-hosted file server with WebDAV (copyparty)
- [x] Self-hosted git repository (Gitea)
- [x] Self-hosted media server (Jellyfin)
- [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
## 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
- 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
- PewDiePie homelab setup video: https://www.youtube.com/watch?v=u_Lxkt50xOg
- 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`)
- ✅ Dotfiles management with yadm configured and merged
- ✅ 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)
- ✅ Copyparty file server with working WebDAV support
- ✅ Jellyfin media server (Docker container)
- ✅ 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
@@ -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)
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)
6. **Issues**: Find solutions in [docs/troubleshooting.md](docs/troubleshooting.md)
6. **Issues**: Find solutions in [docs/troubleshooting/](docs/troubleshooting/)
### Repository Structure
```
@@ -51,12 +62,17 @@ homelab/
├── docs/ # Detailed documentation
│ ├── system-setup.md # Arch Linux installation & config
│ ├── network-security.md # SSH, DNS, VPN, firewall
│ ├── geoip-blocking.md # Geographic IP blocking setup
│ ├── services.md # Self-hosted services
│ ├── voice-assistant.md # AI voice setup with Piper TTS
│ └── troubleshooting.md # Solutions & fixes
│ └── troubleshooting/ # Solutions & troubleshooting guides
├── config/ # Configurations & scripts
│ ├── 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
│ ├── www/ # Landing page HTML
│ └── scripts/ # Utility scripts
├── voice-server/ # AI voice assistant server
│ ├── src/voice_server/ # FastAPI application
@@ -70,14 +86,16 @@ homelab/
### Documentation Files
- **[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/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/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
- **[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
- **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)
- **Services**: Nginx reverse proxy, Gitea Git server (Docker), AI voice assistant (Piper TTS)
- **Security**: SSH hardened, firewall planned, SSL certificates pending
- **Services**: Nginx reverse proxy, Gitea Git server, Copyparty file server with WebDAV, Jellyfin media server, AI voice assistant
- **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

41
TODO.md
View File

@@ -5,9 +5,25 @@
- [x] SSH security hardening *(documented in network-security.md)*
- [x] Figure out why laptop IP changes: Different eth ports have different MAC?
- [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
- [ ] UFW firewall setup and rules
- [ ] fail2ban for intrusion prevention
- [x] VNC setup for remote desktop access *(TigerVNC installed, enabled temporarily via SSH when needed)*
## Git & Development
- [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
## 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
- Tools to consider: ytmusicapi (YouTube Music), scdl (SoundCloud), Google Takeout
- 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] Nginx reverse proxy integration at /media/ path
- [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
- 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)*
- [ ] 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
- Jellyfin: ✅ Docker
- Copyparty: ❌ systemd service (consider dockerizing)
- Nginx: ❌ system package (fine as-is for reverse proxy)
- Jellyfin: ✅ Docker
- qBittorrent: ✅ Docker
- 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
- [ ] Fix bluetooth audio connectivity issues

View File

@@ -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

View 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

View File

@@ -0,0 +1,7 @@
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}

View File

@@ -3,6 +3,11 @@
# Create data directory: sudo mkdir -p /opt/docker/gitea/data
# 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:
gitea:
external: false
@@ -14,13 +19,35 @@ services:
environment:
- USER_UID=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:
- gitea
volumes:
- ./data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
# Bind ports to localhost only for reverse proxy
ports:
- "3000:3000"
- "2223:22"
- "127.0.0.1:3000:3000"
- "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

View File

@@ -2,6 +2,13 @@
# 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
# 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:
jellyfin:
image: jellyfin/jellyfin:latest
@@ -16,8 +23,24 @@ services:
- JELLYFIN_PublishedServerUrl=https://ak-homelab.duckdns.org/media
# 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
# 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
volumes:
# Jellyfin configuration and data
@@ -25,11 +48,11 @@ services:
- /opt/docker/jellyfin/cache:/cache
# Media folders (shared with Copyparty)
- /home/hoborg/Music:/media/music:ro
- /home/hoborg/Videos:/media/videos:ro
- /home/hoborg/Pictures:/media/pictures:ro
- /home/hoborg/shared:/media/shared:ro
- /home/hoborg/private:/media/private:ro
- /mnt/nas/music:/media/music:ro
- /mnt/nas/videos:/media/videos:ro
- /mnt/nas/pictures:/media/pictures:ro
- /mnt/nas/shared:/media/shared:ro
- /mnt/nas/private:/media/private:ro
# Additional media folders if they exist
# - /home/hoborg/Movies:/media/movies:ro
@@ -39,15 +62,20 @@ services:
devices:
- /dev/dri:/dev/dri
# Memory limits for container stability
# Enhanced resource limits
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
# Optional: Create a custom network if not using host networking
# networks:
# jellyfin:
# driver: bridge
# Health check
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8096/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s

View 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:

View File

@@ -0,0 +1,2 @@
{
}

View File

@@ -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"

View File

@@ -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#()

View File

@@ -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})"

View File

@@ -0,0 +1,2 @@
{
}

View File

@@ -0,0 +1,2 @@
{
}

View 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

View 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 =

View 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 =

View 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 =

View 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

View 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

View 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

View File

@@ -4,9 +4,16 @@
server {
listen 80 default_server;
server_name ak-homelab.duckdns.org _;
# Redirect HTTP to HTTPS
return 301 https://$host$request_uri;
# Allow Let's Encrypt ACME challenges
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# Redirect all other HTTP to HTTPS
location / {
return 301 https://$host$request_uri;
}
}
server {
@@ -21,6 +28,25 @@ server {
add_header X-Content-Type-Options "nosniff" 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
location / {
root /var/www/homelab;
@@ -58,7 +84,8 @@ server {
# Explicitly allow WebDAV methods
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 X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@@ -84,6 +111,10 @@ server {
proxy_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
proxy_redirect off;
}
@@ -115,6 +146,70 @@ server {
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_key /etc/letsencrypt/live/ak-homelab.duckdns.org/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot

14
config/ssh/banner Normal file
View 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.
================================================================================

View 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

View 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

View 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

View 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

View 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

View File

@@ -15,17 +15,47 @@
}
.container {
max-width: 900px; margin: 0 auto;
background: white; padding: 40px;
border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.2);
background: white; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.2);
overflow: hidden;
}
.header {
padding: 40px 40px 0 40px;
}
h1 {
color: #333; text-align: center; margin-bottom: 10px;
font-size: 2.5em; font-weight: 300;
}
.subtitle {
text-align: center; color: #666; margin-bottom: 40px;
text-align: center; color: #666; margin-bottom: 30px;
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 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
@@ -36,7 +66,7 @@
color: white; padding: 30px; border-radius: 8px;
text-decoration: none; text-center;
transition: transform 0.3s ease, box-shadow 0.3s ease;
border: none;
border: none; display: block;
}
.service:hover {
transform: translateY(-5px);
@@ -54,6 +84,9 @@
.service.cloud {
background: linear-gradient(135deg, #0082c9 0%, #30b455 100%);
}
.service.admin {
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
}
.service i {
font-size: 3em; margin-bottom: 15px; display: block;
}
@@ -67,42 +100,123 @@
opacity: 0.7;
}
.footer {
text-align: center; margin-top: 40px; color: #888;
text-align: center; padding: 0 40px 40px 40px; color: #888;
font-size: 0.9em;
}
.admin-services {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 25px;
margin-top: 20px;
}
</style>
</head>
<body>
<div class="container">
<h1>Homelab Services</h1>
<p class="subtitle">Self-hosted applications and services</p>
<div class="header">
<h1>Homelab Services</h1>
<p class="subtitle">Self-hosted applications and services</p>
</div>
<div class="services">
<a href="/gitea/" class="service">
<i class="fas fa-code-branch"></i>
<h3>Gitea</h3>
<p>Git Repository Server</p>
</a>
<a href="/files/" class="service">
<i class="fas fa-folder-open"></i>
<h3>File Server</h3>
<p>Upload & Download Files</p>
</a>
<a href="#" class="service disabled" onclick="return false;">
<i class="fas fa-cloud"></i>
<h3>Nextcloud</h3>
<p>Temporarily Disabled</p>
</a>
<a href="/media/" class="service">
<i class="fas fa-play-circle"></i>
<h3>Media Server</h3>
<p>Movies, Music & TV Shows</p>
</a>
<div class="tab-nav">
<button class="tab-button active" onclick="showTab('home', this)">
<i class="fas fa-home"></i> Home
</button>
<button class="tab-button" onclick="showTab('admin', this)">
<i class="fas fa-cogs"></i> Admin
</button>
</div>
<div id="home-tab" class="tab-content active">
<div class="services">
<a href="/gitea/" class="service">
<i class="fas fa-code-branch"></i>
<h3>Gitea</h3>
<p>Git Repository Server</p>
</a>
<a href="/files/" class="service">
<i class="fas fa-folder-open"></i>
<h3>Copyparty</h3>
<p>File Server & WebDAV</p>
</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 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>
<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>
</html>

View 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
View 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

View File

@@ -366,6 +366,245 @@ sudo fail2ban-client status sshd
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
### Port Forwarding

121
docs/qbittorrent-setup.md Normal file
View 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

View 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
View 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

View File

@@ -4,12 +4,12 @@ Planning and configuration for self-hosted services and applications.
## Nginx Reverse Proxy
**Status**: ✅ Active
**Port**: 80 (HTTP), 443 (HTTPS)
**Status**: ✅ Active
**Port**: 80 (HTTP), 443 (HTTPS)
**Configuration**: `/etc/nginx/sites-available/homelab`
### Features
- Path-based routing to services (`/gitea/`, `/files/`, `/media/`)
- Path-based routing to services (`/gitea/`, `/files/`, `/media/`)
- SSL termination with Let's Encrypt certificates
- Security headers (XSS protection, content type sniffing prevention)
- WebDAV support for Copyparty file server
@@ -160,7 +160,7 @@ docker-compose up -d
**Port Assignments:**
- **System SSH**: 2222 (for server administration)
- **Gitea SSH**: 2223 (for Git operations)
- **Gitea SSH**: 2223 (for Git operations)
- **Gitea Web**: 3000 (web interface)
**Database**: SQLite (default, stored in container volume)
@@ -264,16 +264,27 @@ sudo pacman -S copyparty
- **SSL**: Let's Encrypt certificates with automatic renewal
**User Accounts:**
- **guest**: Standard user with read/write access to shared areas
- **hoborg**: Admin user with access to all areas including private folder
- **guest**: Standard user with read/write (`rw`) access to shared areas
- **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:**
```
/shared → /home/hoborg/shared (guest, hoborg: rw)
/documents → /home/hoborg/Documents (guest, hoborg: rw)
/music → /home/hoborg/Music (guest, hoborg: rw)
/videos → /home/hoborg/Videos (guest, hoborg: rw)
/private → /home/hoborg/private (hoborg only: rw)
/shared → /home/hoborg/shared (guest: rw, hoborg: rwmd)
/documents → /home/hoborg/Documents (hoborg: rwmd)
/music → /home/hoborg/Music (guest: rw, hoborg: rwmd)
/videos → /home/hoborg/Videos (guest: rw, hoborg: rwmd)
/pictures → /home/hoborg/Pictures (guest: rw, hoborg: rwmd)
/private → /home/hoborg/private (hoborg only: rwmd)
```
**Features Enabled:**
@@ -311,6 +322,29 @@ sudo systemctl disable copyparty
- **Systemd service**: `/home/hoborg/homelab/config/systemd/copyparty.service`
- **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:**
- ✅ File uploads working (including video files)
- ✅ WebDAV folder uploads from Android (X-plore File Manager)
@@ -442,6 +476,17 @@ sudo docker-compose logs -f jellyfin
- **Immich**: Modern photo backup solution
- **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
### System Monitoring
@@ -498,7 +543,7 @@ sudo usermod -aG docker hoborg
- No auto-discovery of services
- Requires config reload for changes
#### Traefik
#### Traefik
**Pros:**
- Docker-native auto-discovery via labels
- Automatic HTTPS with Let's Encrypt
@@ -636,7 +681,7 @@ server {
### Router Port Forwarding Requirements
- **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)
### SSL Certificates
@@ -715,3 +760,187 @@ sudo -u postgres pg_dump gitea > /backup/gitea-db-$DATE.sql
- **Bookstack**: Documentation wiki
- What is this for? How does it compare to Logseq?
- **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
View 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

View 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
View 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)

View 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

View File

@@ -39,6 +39,27 @@ server {
- Error: `HTTP/1.1 403 Forbidden`
**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
[/shared]
/home/hoborg/shared
@@ -115,7 +136,8 @@ server {
# Explicitly allow WebDAV methods
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 X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@@ -140,6 +162,10 @@ server {
# Critical: Streaming uploads for WebDAV
proxy_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
proxy_redirect off;

View File

@@ -1,5 +1,7 @@
# 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.
## Overview
@@ -8,6 +10,7 @@ The voice assistant setup uses:
- **Piper TTS**: Local neural text-to-speech engine for generating natural-sounding speech
- **FastAPI**: HTTP server wrapper to make Piper compatible with voice-mode
- **Ryan voice model**: Professional male US English voice for AI assistant personality
- **onnxruntime**: ML inference library (removed - required for TTS)
## Prerequisites

49
opencode.json Normal file
View 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
View 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
View 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
View 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)"

View 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
View 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
View 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
View 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"

View 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"