Skip to main content
9 min read

Raspberry Pi Security Projects That Actually Solve Problems

From network monitoring to physical security – practical Raspberry Pi projects that enhance your home security without breaking the bank

After collecting a drawer full of Raspberry Pis over the years (we all have that drawer, right?), I decided it was time to put them to work. Here are five security projects that actually solve real problems, complete with implementation guides and lessons learned.

Why Raspberry Pi for Security?

Before diving into projects, let's address the elephant in the room: Why use a $35 computer for security when enterprise solutions exist?

  1. Low Power Consumption: Running 24/7 costs pennies
  2. Silent Operation: No fans = no noise in your home
  3. Versatility: From network monitoring to physical security
  4. Learning Platform: Mistakes are cheap and educational
  5. Real Solutions: These aren't toys – they solve actual problems

Project 1: The Network Sentinel – DNS Sinkhole & Monitor

Problem Solved: Ads, tracking, and malicious domains accessing your network

Hardware: Raspberry Pi 4 (2GB), microSD card, Ethernet cable

Implementation

# Install Pi-hole
curl -sSL https://install.pi-hole.net | bash

# Add custom blocklists for security
cd /etc/pihole
sudo wget https://raw.githubusercontent.com/RPiList/specials/master/Blocklists/malware.txt
sudo wget https://raw.githubusercontent.com/RPiList/specials/master/Blocklists/ransomware.txt
sudo wget https://raw.githubusercontent.com/RPiList/specials/master/Blocklists/phishing.txt

# Update gravity database
pihole -g

Enhancements I Added

#!/usr/bin/env python3
# dns_alert.py - Alert on suspicious DNS queries

import tailer
import re
from datetime import datetime
import smtplib

# Suspicious TLDs often used in malware
SUSPICIOUS_TLDS = ['.tk', '.ml', '.ga', '.cf', '.click', '.download']

def check_dns_query(query):
    for tld in SUSPICIOUS_TLDS:
        if query.endswith(tld):
            return True
    # Check for DGA-like domains (random looking)
    if len(re.findall(r'[0-9]', query)) > 5:
        return True
    return False

def monitor_pihole_log():
    for line in tailer.follow(open('/var/log/pihole.log')):
        if 'query[A]' in line:
            domain = line.split('query[A]')[1].split('from')[0].strip()
            if check_dns_query(domain):
                alert(f"Suspicious DNS query: {domain}")

def alert(message):
    print(f"[{datetime.now()}] ALERT: {message}")
    # Add email/Discord notification here

if __name__ == "__main__":
    monitor_pihole_log()

Results: Blocking 30-40% of DNS queries (mostly ads/tracking), caught 3 malware callbacks in 6 months, kids' devices are significantly faster.

Project 2: The Silent Guardian – Motion Detection Security Camera

Problem Solved: Package theft, wanting to monitor specific areas without cloud dependencies

Hardware: Raspberry Pi Zero W, Pi Camera v2, PIR motion sensor, 3D printed case

Implementation

#!/usr/bin/env python3
# motion_security.py - Smart motion detection with AI

import cv2
import numpy as np
from picamera2 import Picamera2
import time
from datetime import datetime
import os

class SecurityCamera:
    def __init__(self):
        self.camera = Picamera2()
        self.camera.configure(self.camera.create_preview_configuration())
        self.camera.start()
        
        # YOLO for person detection
        self.net = cv2.dnn.readNet("yolov4-tiny.weights", "yolov4-tiny.cfg")
        self.classes = open("coco.names").read().strip().split("\n")
        
    def detect_person(self, frame):
        height, width = frame.shape[:2]
        
        # Create blob from image
        blob = cv2.dnn.blobFromImage(frame, 1/255.0, (416, 416), 
                                     swapRB=True, crop=False)
        self.net.setInput(blob)
        outputs = self.net.forward(self.net.getUnconnectedOutLayersNames())
        
        # Parse detections
        for output in outputs:
            for detection in output:
                scores = detection[5:]
                class_id = np.argmax(scores)
                confidence = scores[class_id]
                
                if confidence > 0.5 and self.classes[class_id] == "person":
                    return True
        return False
    
    def capture_alert(self):
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"alert_{timestamp}.jpg"
        
        frame = self.camera.capture_array()
        
        if self.detect_person(frame):
            cv2.imwrite(f"/home/pi/alerts/{filename}", frame)
            self.send_notification(filename)
            return True
        return False
    
    def send_notification(self, filename):
        # Send to Discord webhook
        import requests
        webhook_url = "YOUR_DISCORD_WEBHOOK"
        
        with open(f"/home/pi/alerts/{filename}", "rb") as f:
            files = {"file": (filename, f)}
            data = {"content": f"🚨 Person detected at {datetime.now()}"}
            requests.post(webhook_url, data=data, files=files)

if __name__ == "__main__":
    camera = SecurityCamera()
    
    while True:
        if GPIO.input(PIR_PIN):
            camera.capture_alert()
            time.sleep(10)  # Cooldown period
        time.sleep(0.1)

Results: Caught multiple package delivery attempts, identified a raccoon problem (AI thought it was a person at first), zero false alerts after tuning.

Project 3: The Honeypot – Early Warning System

Problem Solved: Detecting network intrusion attempts before they reach real systems

Hardware: Raspberry Pi 3B+, Ethernet connection

Implementation

#!/usr/bin/env python3
# honeypot.py - Lightweight SSH/HTTP honeypot

import socket
import threading
import json
from datetime import datetime
import sqlite3

class Honeypot:
    def __init__(self):
        self.setup_database()
        
    def setup_database(self):
        self.conn = sqlite3.connect('honeypot.db', check_same_thread=False)
        self.conn.execute('''
            CREATE TABLE IF NOT EXISTS attempts (
                timestamp TEXT,
                service TEXT,
                source_ip TEXT,
                username TEXT,
                password TEXT,
                user_agent TEXT
            )
        ''')
        self.conn.commit()
    
    def log_attempt(self, service, source_ip, **kwargs):
        self.conn.execute('''
            INSERT INTO attempts VALUES (?, ?, ?, ?, ?, ?)
        ''', (
            datetime.now().isoformat(),
            service,
            source_ip,
            kwargs.get('username', ''),
            kwargs.get('password', ''),
            kwargs.get('user_agent', '')
        ))
        self.conn.commit()
        
        # Alert on suspicious patterns
        if self.check_threat_level(source_ip):
            self.send_alert(source_ip)
    
    def fake_ssh_server(self, port=2222):
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.bind(('0.0.0.0', port))
        server.listen(5)
        
        while True:
            client, addr = server.accept()
            # Send fake SSH banner
            client.send(b"SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.1\r\n")
            
            try:
                data = client.recv(1024).decode('utf-8')
                # Parse SSH handshake for username
                self.log_attempt('ssh', addr[0])
            except:
                pass
            finally:
                client.close()
    
    def fake_http_server(self, port=8080):
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.bind(('0.0.0.0', port))
        server.listen(5)
        
        while True:
            client, addr = server.accept()
            
            try:
                request = client.recv(1024).decode('utf-8')
                lines = request.split('\n')
                
                # Extract user agent
                user_agent = ""
                for line in lines:
                    if line.startswith("User-Agent:"):
                        user_agent = line.split(":", 1)[1].strip()
                
                # Send fake response
                response = """HTTP/1.1 200 OK
Content-Type: text/html

<html><body><h1>Router Configuration</h1></body></html>
"""
                client.send(response.encode())
                
                self.log_attempt('http', addr[0], user_agent=user_agent)
                
            except:
                pass
            finally:
                client.close()
    
    def check_threat_level(self, ip):
        # Check if IP has made multiple attempts
        cursor = self.conn.execute('''
            SELECT COUNT(*) FROM attempts 
            WHERE source_ip = ? 
            AND timestamp > datetime('now', '-1 hour')
        ''', (ip,))
        
        count = cursor.fetchone()[0]
        return count > 5

honeypot = Honeypot()

# Run services in threads
threading.Thread(target=honeypot.fake_ssh_server, daemon=True).start()
threading.Thread(target=honeypot.fake_http_server, daemon=True).start()

print("Honeypot running... Check honeypot.db for attempts")

# Keep main thread alive
while True:
    time.sleep(60)

Results: Detected 3 targeted scans of my network, identified compromised IoT device attempting lateral movement, fascinating data on bot behavior.

Project 4: The Vault Guardian – Hardware Security Key Backup

Problem Solved: Secure backup for 2FA recovery codes and emergency access

Hardware: Raspberry Pi Zero, OLED display, button, encrypted SD card

Implementation

#!/usr/bin/env python3
# vault_guardian.py - Offline 2FA backup vault

import os
import json
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import base64
import getpass
from pathlib import Path

class SecureVault:
    def __init__(self):
        self.vault_file = Path("/home/pi/vault/backup.enc")
        self.vault_file.parent.mkdir(exist_ok=True)
        
    def derive_key(self, password: str, salt: bytes) -> bytes:
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=salt,
            iterations=100000,
        )
        key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
        return key
    
    def create_backup(self):
        print("=== 2FA Recovery Code Vault ===")
        
        # Generate new salt
        salt = os.urandom(16)
        
        # Get master password
        password = getpass.getpass("Create master password: ")
        confirm = getpass.getpass("Confirm password: ")
        
        if password != confirm:
            print("Passwords don't match!")
            return
        
        # Derive encryption key
        key = self.derive_key(password, salt)
        f = Fernet(key)
        
        # Collect recovery codes
        codes = {}
        print("\nEnter recovery codes (empty service name to finish):")
        
        while True:
            service = input("Service name: ").strip()
            if not service:
                break
                
            recovery_codes = []
            print(f"Enter recovery codes for {service} (empty to finish):")
            
            while True:
                code = input("Code: ").strip()
                if not code:
                    break
                recovery_codes.append(code)
            
            if recovery_codes:
                codes[service] = recovery_codes
        
        # Add metadata
        vault_data = {
            "created": datetime.now().isoformat(),
            "services": codes,
            "notes": input("\nAny additional notes: ")
        }
        
        # Encrypt and save
        encrypted = f.encrypt(json.dumps(vault_data).encode())
        
        with open(self.vault_file, 'wb') as file:
            file.write(salt + encrypted)
        
        print(f"\n✓ Vault created with {len(codes)} services")
        print("⚠️  This Pi is now your 2FA recovery device. Keep it secure!")
        
    def unlock_vault(self):
        if not self.vault_file.exists():
            print("No vault found!")
            return
        
        with open(self.vault_file, 'rb') as file:
            data = file.read()
            
        salt = data[:16]
        encrypted = data[16:]
        
        # Get password
        password = getpass.getpass("Enter vault password: ")
        
        try:
            key = self.derive_key(password, salt)
            f = Fernet(key)
            decrypted = f.decrypt(encrypted)
            
            vault_data = json.loads(decrypted)
            
            print(f"\n=== Vault Contents ===")
            print(f"Created: {vault_data['created']}")
            print(f"Services: {len(vault_data['services'])}")
            
            for service, codes in vault_data['services'].items():
                print(f"\n{service}:")
                for code in codes:
                    print(f"  - {code}")
            
            if vault_data.get('notes'):
                print(f"\nNotes: {vault_data['notes']}")
                
        except Exception as e:
            print("Failed to decrypt vault. Wrong password?")

# Physical button interface
import RPi.GPIO as GPIO

BUTTON_PIN = 17
GPIO.setmode(GPIO.BCM)
GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

vault = SecureVault()

print("Press button to unlock vault...")

while True:
    if GPIO.input(BUTTON_PIN) == GPIO.LOW:
        vault.unlock_vault()
        time.sleep(5)  # Debounce

Results: Peace of mind knowing recovery codes are offline but accessible, survived a YubiKey failure gracefully, spouse approved the "break glass" simplicity.

Project 5: The Compliance Scanner – Automated Security Auditing

Problem Solved: Keeping track of security posture across multiple devices

Hardware: Raspberry Pi 4 (4GB), good network connection

Implementation

#!/usr/bin/env python3
# compliance_scanner.py - Automated security baseline checker

import nmap
import paramiko
import json
from datetime import datetime
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

class ComplianceScanner:
    def __init__(self):
        self.nm = nmap.PortScanner()
        self.results = []
        
    def scan_network(self, network="192.168.1.0/24"):
        print(f"Scanning network: {network}")
        
        # Find all hosts
        self.nm.scan(hosts=network, arguments='-sn')
        
        hosts = []
        for host in self.nm.all_hosts():
            if self.nm[host].state() == 'up':
                hosts.append(host)
                
        print(f"Found {len(hosts)} hosts")
        
        # Detailed scan of each host
        for host in hosts:
            self.scan_host(host)
            
        return self.results
    
    def scan_host(self, host):
        print(f"\nScanning {host}...")
        
        result = {
            'host': host,
            'timestamp': datetime.now().isoformat(),
            'issues': [],
            'score': 100
        }
        
        # Port scan
        self.nm.scan(host, '1-65535', '-sV')
        
        if host in self.nm.all_hosts():
            # Check for risky services
            risky_ports = {
                23: 'Telnet',
                21: 'FTP', 
                445: 'SMB',
                3389: 'RDP',
                22: 'SSH'  # Not risky, but worth checking config
            }
            
            for port, service in risky_ports.items():
                if port in self.nm[host]['tcp']:
                    state = self.nm[host]['tcp'][port]['state']
                    if state == 'open':
                        issue = f"{service} port {port} is open"
                        result['issues'].append(issue)
                        result['score'] -= 10
            
            # Check SSH configuration if available
            if 22 in self.nm[host]['tcp'] and self.nm[host]['tcp'][22]['state'] == 'open':
                ssh_issues = self.check_ssh_config(host)
                result['issues'].extend(ssh_issues)
                result['score'] -= len(ssh_issues) * 5
        
        self.results.append(result)
        
    def check_ssh_config(self, host):
        issues = []
        
        try:
            # Try to connect with weak credentials
            weak_creds = [
                ('pi', 'raspberry'),
                ('admin', 'admin'),
                ('root', 'root')
            ]
            
            for username, password in weak_creds:
                try:
                    ssh = paramiko.SSHClient()
                    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
                    ssh.connect(host, username=username, password=password, timeout=3)
                    issues.append(f"Weak credentials work: {username}:{password}")
                    ssh.close()
                except:
                    pass  # Good, credentials didn't work
                    
        except Exception as e:
            pass
            
        return issues
    
    def generate_report(self):
        report = "# Network Security Compliance Report\n\n"
        report += f"Scan Date: {datetime.now()}\n\n"
        
        critical_hosts = [r for r in self.results if r['score'] < 70]
        warning_hosts = [r for r in self.results if 70 <= r['score'] < 90]
        
        if critical_hosts:
            report += "## 🔴 Critical Issues\n\n"
            for host in critical_hosts:
                report += f"### {host['host']} (Score: {host['score']})\n"
                for issue in host['issues']:
                    report += f"- {issue}\n"
                report += "\n"
        
        if warning_hosts:
            report += "## 🟡 Warnings\n\n"
            for host in warning_hosts:
                report += f"### {host['host']} (Score: {host['score']})\n"
                for issue in host['issues']:
                    report += f"- {issue}\n"
                report += "\n"
        
        # Summary statistics
        avg_score = sum(r['score'] for r in self.results) / len(self.results)
        report += f"## Summary\n\n"
        report += f"- Hosts scanned: {len(self.results)}\n"
        report += f"- Average security score: {avg_score:.1f}/100\n"
        report += f"- Critical issues: {len(critical_hosts)}\n"
        report += f"- Warnings: {len(warning_hosts)}\n"
        
        return report

# Run weekly scan
scanner = ComplianceScanner()
scanner.scan_network()
report = scanner.generate_report()

print("\n" + report)

# Save report
with open(f"/home/pi/reports/scan_{datetime.now().strftime('%Y%m%d')}.md", "w") as f:
    f.write(report)

Results: Found 2 IoT devices with default passwords, discovered forgotten test VM with open services, maintains security baseline visibility.

Lessons Learned

After implementing these projects, here are my key takeaways:

1. Start Simple, Iterate Often

My first Pi-hole setup was basic. Now it has custom blocklists, monitoring scripts, and integration with my SIEM. Evolution is normal.

2. Physical Security Matters

That expensive Arlo camera? My Pi Zero setup catches more relevant events because I positioned it better and tuned it myself.

3. False Positives Will Happen

My honeypot initially alerted on my own port scans. My motion detector thought shadows were intruders. Tuning is crucial.

4. Power and Network Reliability

Invest in good power supplies and consider PoE HATs. Nothing worse than your security system going down during a storm.

5. Documentation Is Security

Every project has a README with recovery procedures. When something breaks at 2 AM, you'll thank yourself.

Cost Analysis

Total investment for all five projects:

  • Raspberry Pis: ~$200
  • Accessories (cases, cards, sensors): ~$100
  • Time invested: ~40 hours
  • Knowledge gained: Priceless

Compare that to:

  • Enterprise DNS filter: $500+/year
  • Cloud security camera: $10+/month
  • SIEM subscription: $100+/month
  • Vulnerability scanner: $2000+/year

What's Next?

I'm currently working on:

  1. AI-Powered Threat Detection: Using Coral TPU for real-time network traffic analysis
  2. Mesh Security Network: Multiple Pis creating a distributed security sensor network
  3. Incident Response Bot: Automated playbook execution via Discord commands

Your Turn

These projects solve my specific problems, but the beauty of Raspberry Pi is customization. Maybe you need:

  • A secure password manager display
  • Network usage monitor for kids' devices
  • Automated backup verification system
  • Physical security for a specific door/window

The key is identifying a real problem and building a focused solution.

Resources

  • Hardware: CanaKit for reliable Pi bundles
  • Cases: Check Thingiverse for security-specific Pi cases
  • Community: r/raspberry_pi and r/homelab are goldmines
  • My Scripts: [GitHub - Coming Soon] (sanitizing for public release)

Remember: The best security system is one that actually gets used. These Raspberry Pi projects work because they're maintainable, understandable, and solve real problems.

What security problem will you solve with your next Pi?


Have questions about any of these projects? Found a cool use for your Pi? Let me know! I'm always looking for the next practical security project.

Related Posts