Skip to content

09 - Scanopy + Vaultwarden

Date: 2026-01-04
System: Proxmox VE 9.1.2


πŸ“‹ Overview

Installed Services

Service LXC ID IP Address Tailscale IP Port Status
Vaultwarden 103 192.168.0.219 YOUR_TAILSCALE_IP 8000 βœ… Working
Scanopy 104 192.168.0.122 YOUR_TAILSCALE_IP 60072 βœ… Working

πŸ” Vaultwarden (Password Manager)

Basic Information

  • Platform: Alpine Linux LXC
  • Installation: Proxmox Community Scripts
  • Version: Latest stable

Installation Command

bash -c "$(wget -qO - https://github.com/community-scripts/ProxmoxVE/raw/main/ct/alpine-vaultwarden.sh)"

LXC Specifications

  • CPU: 1 core
  • RAM: 1GB
  • Disk: 4GB
  • Network: vmbr0, DHCP

Configuration

  • Config file: /etc/vaultwarden/config.json or /opt/vaultwarden/.env
  • Web admin: http://<IP>:8000/admin

πŸ—ΊοΈ Scanopy (Network Scanner & Topology Visualizer)

Basic Information

  • Platform: Debian 13 LXC (Unprivileged)
  • Installation: Proxmox Community Scripts
  • Version: 0.12.9

Installation Command

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/scanopy.sh)"

LXC Specifications

  • Container ID: 104
  • Hostname: scanopy
  • CPU: 2 cores
  • RAM: 3GB (3072 MiB)
  • Disk: 6GB
  • Network: vmbr0, DHCP (192.168.0.122)
  • Features: nesting=1, keyctl=1

Access

  • Local IP: http://192.168.0.122:60072
  • Tailscale IP: http://YOUR_TAILSCALE_IP:60072

Daemon Configuration

Manual daemon setup:

# The daemon is already configured and running
systemctl status scanopy-daemon

Daemon details: - Name: local-daemon - Mode: Push - Version: 0.12.9 - Network: My Network (YOUR_NETWORK_UUID)

Config Files

  • Server config: /opt/scanopy/.env
  • Daemon config: /root/.config/daemon/config.json
  • Server service: /etc/systemd/system/scanopy-server.service
  • Daemon service: /etc/systemd/system/scanopy-daemon.service

πŸ”— Tailscale Integration

Network Architecture

  • Home network (Proxmox): 192.168.0.0/24
  • Remote K3s cluster: 192.168.2.0/24 (different location)
  • Tailscale peers: Directly reachable devices (on 100.x.x.x IPs)

LXC 104 (Scanopy) - Tailscale Setup

Installation

curl -fsSL https://tailscale.com/install.sh | sh

Proxmox LXC Config Modification

File: /etc/pve/lxc/104.conf

Added lines (TUN/TAP support):

lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net dev/net none bind,create=dir

Proxmox Host - Disable Subnet Advertise

IMPORTANT: The Proxmox host must NOT advertise the local subnet (192.168.0.0/24)

In Proxmox shell:

# Reset and allow SSH WITHOUT advertise
tailscale up --ssh --reset

Why? - Avoids routing conflict in the Scanopy LXC - Remote K3s devices are directly reachable as Tailscale peers (no advertise needed)

Scanopy LXC - Start Tailscale

# Accept routes ENABLED (in case there are advertised subnets later)
tailscale up --reset --accept-routes

# AND iptables rule for local network access
iptables -I ts-input 1 -i eth0 -j ACCEPT

Why this configuration? - --accept-routes: Accepts any advertised subnets (e.g. if the K3s site advertises other subnets later) - iptables -I ts-input 1 -i eth0 -j ACCEPT: Prevents the Tailscale firewall from blocking the local network (eth0 interface)

Persistent Configuration (systemd override)

File: /etc/systemd/system/tailscaled.service.d/override.conf

[Service]
ExecStartPost=/bin/sh -c 'sleep 5 && /usr/bin/tailscale up --accept-routes && /usr/sbin/iptables -I ts-input 1 -i eth0 -j ACCEPT'

Activate:

systemctl daemon-reload

Tailscale Network Status

tailscale status

Peers: - pve (YOUR_TAILSCALE_IP_PVE) - Proxmox host (home, 192.168.0.0/24) - nex-pc (YOUR_TAILSCALE_IP_PC) - Windows desktop - orangepione (YOUR_TAILSCALE_IP_ORANGEPI) - Orange Pi (K3s site, 192.168.2.0/24) - opt3050-i5 (YOUR_TAILSCALE_IP_OPT1) - K3s cluster node 1 (remote location) - opt3060-i3 (YOUR_TAILSCALE_IP_OPT2) - K3s cluster node 2 (remote location) - opt5060-i5 (YOUR_TAILSCALE_IP_OPT3) - K3s cluster node 3 (remote location)

K3s Cluster Info: - Physical location: Remote (different location, not home) - Local subnet: 192.168.2.0/24 - Tailscale access: Directly as peers (on 100.x.x.x IPs) - Advertise: NOT needed (all important devices are direct Tailscale peers)


πŸ“‘ Network Discovery

Configured Networks (Subnets)

  1. 192.168.0.0/24 (LAN - Local Network)
  2. βœ… Fully mapped
  3. Discovered devices: 14+
  4. Type: Local (eth0 interface)

  5. MamaNet - 100.64.0.0/10 (Tailscale CGNAT Range)

  6. Covers ALL Tailscale peers
  7. Includes: K3s cluster (OptiPlexes), orangepione, pve, nex-pc etc.
  8. Type: VPN (Tailscale)

  9. Internet (0.0.0.0/0) - Public DNS, cloud services

  10. Type: Internet

  11. Remote Network (0.0.0.0/0) - Organizational container for remote hosts

  12. Type: Remote

Starting a Scan

From Web UI (Subnets menu):

  1. Subnets menu
  2. View the configured subnets
  3. Click a subnet and start a scan

From Sessions menu:

  1. Sessions menu β†’ Start Discovery
  2. Select which subnet to scan:
  3. 192.168.0.0/24 - Local LAN
  4. 100.64.0.0/10 - Tailscale network (K3s cluster + all peers)
  5. Or both at once

Scanning Tailscale Devices

Scanopy automatically sees: - βœ… Tailscale interface (tailscale0) - βœ… All peers (on 100.x.x.x IPs) - βœ… K3s cluster nodes (opt3050-i5, opt3060-i3, opt5060-i5) - βœ… orangepione

Scanning: - The daemon automatically discovers Tailscale peers - Port scanning and service discovery work through Tailscale - Encryption: provided by Tailscale (WireGuard-based)


πŸ”§ Troubleshooting

Scanopy local IP not reachable

Problem: 192.168.0.122:60072 not accessible from browser

Checks:

# Service status
systemctl status scanopy-server
systemctl status scanopy-daemon

# Port listening
ss -tulpn | grep 60072

# Check Tailscale iptables rule
iptables -L ts-input -n -v --line-numbers

# The first rule must be eth0 ACCEPT
# If missing:
iptables -I ts-input 1 -i eth0 -j ACCEPT

Tailscale not working after restart

Solution:

# Check override file
cat /etc/systemd/system/tailscaled.service.d/override.conf

# If missing or wrong, recreate it:
mkdir -p /etc/systemd/system/tailscaled.service.d/
cat > /etc/systemd/system/tailscaled.service.d/override.conf << 'EOF'
[Service]
ExecStartPost=/bin/sh -c 'sleep 5 && /usr/bin/tailscale up --accept-routes && /usr/sbin/iptables -I ts-input 1 -i eth0 -j ACCEPT'
EOF

systemctl daemon-reload
systemctl restart tailscaled

Proxmox host advertising subnet (conflict)

Problem: The Proxmox host is advertising 192.168.0.0/24 and this causes a conflict

Check:

# On Proxmox host
tailscale status | grep advertise

# In Scanopy LXC
ip route show table all | grep "192.168.0.0/24 dev tailscale0"

Solution - on Proxmox host:

# Disable advertise
tailscale up --ssh --reset

Solution - in Scanopy LXC (if the above is not enough):

# Delete the conflicting route
ip route del 192.168.0.0/24 dev tailscale0 table 52

K3s cluster not reachable

Problem: OptiPlex nodes cannot be scanned

Checks:

# Tailscale peer status
tailscale status | grep opt

# Ping test to Tailscale IP
ping YOUR_TAILSCALE_IP_OPT1
ping YOUR_TAILSCALE_IP_OPT2
ping YOUR_TAILSCALE_IP_OPT3

# Tailscale routes
ip route show table 52 | grep "100\."

Solution:

# If the OptiPlexes are offline in Tailscale:
# SSH to the OptiPlexes and restart Tailscale
systemctl restart tailscaled

# Or on orangepione (if that is reachable)

Daemon not visible in Web UI

Solution:

# Restart daemon
systemctl restart scanopy-daemon

# Check logs
journalctl -u scanopy-daemon -f

# Check server connection
curl http://127.0.0.1:60072


πŸ“ Useful Commands

Scanopy

# Service management
systemctl status scanopy-server
systemctl status scanopy-daemon
systemctl restart scanopy-server
systemctl restart scanopy-daemon

# Logs
journalctl -u scanopy-server -f
journalctl -u scanopy-daemon -f

# Config
cat /opt/scanopy/.env
cat /root/.config/daemon/config.json

Tailscale

# Status
tailscale status

# Ping peer
tailscale ping <hostname-or-ip>

# Routes
ip route show table 52

# Restart with correct flags
tailscale down
tailscale up --accept-routes=false --netfilter-mode=off

Network Debugging

# Port check
ss -tulpn | grep 60072

# Ping test
ping 192.168.0.122

# HTTP test
curl -I http://192.168.0.122:60072

# Packet capture
tcpdump -i eth0 -n host 192.168.0.122

# Routing
ip route show
ip route show table all
ip rule show

⚠️ Important Notes

Tailscale Routing Solution

Final configuration: 1. Proxmox host: Must NOT advertise the local subnet (192.168.0.0/24) - tailscale up --ssh --reset 2. Scanopy LXC: Accept routes + iptables fix - tailscale up --accept-routes - iptables -I ts-input 1 -i eth0 -j ACCEPT

Why this solution works: - βœ… Avoids routing conflict (192.168.0.0/24 is not in the Tailscale routing table) - βœ… Scanopy can see the local network (eth0 interface) - βœ… Scanopy can see ALL Tailscale peers - βœ… If an advertised subnet is added later (e.g. 192.168.2.0/24), it will be visible too

K3s Cluster Access

  • Physical location: Remote site (not home)
  • Local subnet: 192.168.2.0/24
  • Tailscale access: Directly as peers (no advertise needed)
  • Devices: 3x OptiPlex + 1x orangepione

Advertise is NOT needed because: - All important devices are direct Tailscale peers - There are no other devices on 192.168.2.0/24 that need to be scanned

Firewall & iptables

  • Tailscale was blocking the local network by default (ts-input chain)
  • Solution: iptables -I ts-input 1 -i eth0 -j ACCEPT
  • This rule is permanent (systemd override)
  • The LXC is reachable from all interfaces (local + Tailscale)

rp_filter Setting

  • No longer needed (resolved along with the routing conflict)
  • Default setting: rp_filter=2 (strict mode) - kept as-is

πŸš€ Next Steps

Ready to use immediately:

  1. Network Scanning
  2. βœ… Local LAN (192.168.0.0/24) scanning
  3. βœ… Tailscale network (100.64.0.0/10) scanning
  4. βœ… K3s cluster discovery (OptiPlex nodes)

  5. Scheduled Scans

  6. Configurable from the Scanopy UI
  7. Recommended: Daily scan on local LAN, weekly on Tailscale network

Optional configurations:

  1. HTTPS Setup (Nginx Proxy Manager)
  2. Subdomain: scanopy.yourdomain.com
  3. Forward to: 192.168.0.122:60072
  4. SSL certificate (Let's Encrypt)

  5. Vaultwarden HTTPS

  6. Subdomain: vault.yourdomain.com
  7. Forward to: Vaultwarden LXC IP:8000
  8. SSL certificate required (Vaultwarden requires HTTPS for client apps)

  9. MikroTik Router Upgrade

  10. VLAN segmentation (IoT, Guest, Management)
  11. Advanced routing
  12. Traffic monitoring and QoS
  13. Firewall rules optimization

  14. K3s Cluster Monitoring

  15. Prometheus + Grafana installation
  16. Node exporter on every OptiPlex
  17. Kubernetes metrics collection
  18. Scanopy integration with cluster services

  19. Backup Strategy

  20. Scanopy database (PostgreSQL) backup
  21. Vaultwarden data backup
  22. Automated backup to remote location via Tailscale

  23. Tailscale Exit Node (optional)

  24. If you want an exit node on the network
  25. VPN access for mobile devices

K3s Cluster Scan Fine-tuning:

When the OptiPlexes are online: - Scanopy automatically discovers them (YOUR_TAILSCALE_IP_OPT1, YOUR_TAILSCALE_IP_OPT2, YOUR_TAILSCALE_IP_OPT3) - Port scan and service discovery - Kubernetes API endpoint discovery - Container network mapping (if accessible)


πŸ” Routing Problem Resolution - Detailed Explanation

Origin of the problem

Initial configuration: - Proxmox host was advertising the 192.168.0.0/24 subnet via Tailscale - Scanopy LXC was started with the --accept-routes flag - Result: In the routing table, 192.168.0.0/24 pointed to the tailscale0 interface

Consequence:

# ip route show table 52
192.168.0.0/24 dev tailscale0 table 52  # WRONG!

This caused: - Scanopy LXC tried to reach a local IP (e.g. 192.168.0.122) - The kernel sent the packet out via the tailscale0 interface based on the routing table - Tailscale tried to forward it through the Proxmox host - BUT it was the LXC's own IP address, creating a loop - Result: Timeout, no response

Attempted solutions

Attempt 1 - netfilter-mode=off:

tailscale up --accept-routes=false --netfilter-mode=off
- βœ… Local IP worked - ❌ Tailscale network was not fully functional (firewall off) - ❌ Would not have seen advertised subnets

Attempt 2 - Manual route deletion:

ip route del 192.168.0.0/24 dev tailscale0 table 52
sysctl -w net.ipv4.conf.all.rp_filter=0
- βœ… Worked temporarily - ❌ Would have needed to be re-applied after every restart

Attempt 3 - PERMANENT SOLUTION - Source fix:

A. Proxmox host:

tailscale up --ssh --reset  # Do NOT advertise

B. Scanopy LXC:

tailscale up --accept-routes  # Accept any advertised subnets
iptables -I ts-input 1 -i eth0 -j ACCEPT  # Local network access

Why it works: - βœ… No 192.168.0.0/24 in the Tailscale routing table - βœ… Local network reachable via the eth0 interface - βœ… Tailscale peers reachable via the tailscale0 interface - βœ… If another subnet is advertised later (e.g. 192.168.2.0/24), it will be visible - βœ… Firewall properly configured (ts-input chain)

Routing Tables BEFORE and AFTER

BEFORE (WRONG):

# Main table
default via 192.168.0.1 dev eth0
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.122

# Table 52 (Tailscale)
192.168.0.0/24 dev tailscale0  # ← CONFLICT!
YOUR_TAILSCALE_IP_OPT1 dev tailscale0
YOUR_TAILSCALE_IP_PVE dev tailscale0

AFTER (CORRECT):

# Main table
default via 192.168.0.1 dev eth0
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.122

# Table 52 (Tailscale)
# NO 192.168.0.0/24 ← CORRECT!
YOUR_TAILSCALE_IP_OPT1 dev tailscale0
YOUR_TAILSCALE_IP_PVE dev tailscale0
YOUR_TAILSCALE_IP_OPT2 dev tailscale0

Lessons Learned

  1. Subnet advertise only when truly necessary
  2. If all important devices are direct Tailscale peers, it is not needed

  3. Routing priority matters

  4. The kernel always uses the most specific route
  5. If there is a conflict, policy routing or route metric is needed

  6. Debugging firewall rules

  7. iptables -L -n -v --line-numbers for every chain
  8. tcpdump for following packet flow

  9. Understanding Tailscale flags

  10. --accept-routes: Accepts advertised subnets
  11. --advertise-routes: Advertises a subnet
  12. --netfilter-mode=off: Disables the Tailscale firewall (not recommended in production)

πŸ“š Further Documentation


Version: 2.0
Last updated: 2026-01-04 (Final routing fix)
Author: Nex (Home Lab Setup Session)

Changes in v2.0: - βœ… Routing problem resolved (Proxmox advertise disabled) - βœ… Scanopy accept-routes + iptables fix - βœ… K3s cluster remote location documented - βœ… Subnet configuration (192.168.0.0/24 + 100.64.0.0/10) - βœ… Detailed troubleshooting and routing explanation