Fix security warnings

This commit is contained in:
gpt-engineer-app[bot]
2025-10-20 21:52:32 +00:00
parent 9c584beddb
commit 10bcbf519c
7 changed files with 640 additions and 125 deletions

View File

@@ -1,2 +1,6 @@
# Timezone
TZ=Europe/Copenhagen
# Admin Credentials (CHANGE THESE!)
ADMIN_EMAIL=admin@docker-webui.local
ADMIN_PASSWORD=changeme123

343
README.md
View File

@@ -1,73 +1,314 @@
# Welcome to your Lovable project
# 🐳 Docker WebUI
## Project info
A lightweight, self-hosted web interface for monitoring and controlling Docker containers in real-time.
**URL**: https://lovable.dev/projects/ba519789-1c3e-4fe3-a965-cdc4a30e6a8b
## ✨ Features
## How can I edit this code?
- **Real-time Monitoring** - Live metrics via WebSocket (CPU, memory, network, I/O)
- **Auto-discovery** - Containers created/removed via CLI appear instantly in UI
- **Container Control** - Start, stop, restart containers from the web interface
- **Live Logs** - Stream container logs in real-time with filtering
- **Discord Alerts** - Per-container notifications on stop/error
- **Dark Mode** - Beautiful, responsive interface
- **Single Admin** - Simple authentication with one admin user
There are several ways of editing your application.
## 🚀 Quick Start
**Use Lovable**
### Prerequisites
Simply visit the [Lovable Project](https://lovable.dev/projects/ba519789-1c3e-4fe3-a965-cdc4a30e6a8b) and start prompting.
- Docker & Docker Compose
- Host with Docker socket access (`/var/run/docker.sock`)
Changes made via Lovable will be committed automatically to this repo.
### Installation
**Use your preferred IDE**
If you want to work locally using your own IDE, you can clone this repo and push changes. Pushed changes will also be reflected in Lovable.
The only requirement is having Node.js & npm installed - [install with nvm](https://github.com/nvm-sh/nvm#installing-and-updating)
Follow these steps:
```sh
# Step 1: Clone the repository using the project's Git URL.
git clone <YOUR_GIT_URL>
# Step 2: Navigate to the project directory.
cd <YOUR_PROJECT_NAME>
# Step 3: Install the necessary dependencies.
npm i
# Step 4: Start the development server with auto-reloading and an instant preview.
npm run dev
1. Clone this repository:
```bash
git clone <your-repo-url>
cd docker-webui
```
**Edit a file directly in GitHub**
2. Copy and configure environment:
```bash
cp .env.example .env
nano .env # Edit admin credentials
```
- Navigate to the desired file(s).
- Click the "Edit" button (pencil icon) at the top right of the file view.
- Make your changes and commit the changes.
3. Set your admin credentials in `.env`:
```env
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=your-secure-password
TZ=Europe/Copenhagen
```
**Use GitHub Codespaces**
4. Build and start:
```bash
docker-compose up -d --build
```
- Navigate to the main page of your repository.
- Click on the "Code" button (green button) near the top right.
- Select the "Codespaces" tab.
- Click on "New codespace" to launch a new Codespace environment.
- Edit files directly within the Codespace and commit and push your changes once you're done.
5. Access the UI at `http://your-server-ip:8080`
## What technologies are used for this project?
6. **First Login:**
- Navigate to `/settings` in your browser
- Click "Sign Up" to create the admin account
- Use the credentials from your `.env` file
- After creating the account, login with the same credentials
This project is built with:
## 🔧 Configuration
- Vite
- TypeScript
- React
- shadcn-ui
- Tailwind CSS
### Docker Compose
## How can I deploy this project?
The service uses **host networking mode** for direct Docker socket access and requires:
- `/var/run/docker.sock` (read-only) - Docker API access
- `/proc` (read-only) - Host system metrics
Simply open [Lovable](https://lovable.dev/projects/ba519789-1c3e-4fe3-a965-cdc4a30e6a8b) and click on Share -> Publish.
### Environment Variables
## Can I connect a custom domain to my Lovable project?
| Variable | Default | Description |
|----------|---------|-------------|
| `ADMIN_EMAIL` | `admin@docker-webui.local` | Admin email address |
| `ADMIN_PASSWORD` | `changeme123` | Admin password (min 6 chars) |
| `TZ` | `Europe/Copenhagen` | Server timezone |
Yes, you can!
### Changing the Port
To connect a domain, navigate to Project > Settings > Domains and click Connect Domain.
Edit `nginx.conf`:
```nginx
server {
listen 8082; # Change from 8080 to your desired port
server_name _;
# ...
}
```
Read more here: [Setting up a custom domain](https://docs.lovable.dev/features/custom-domain#custom-domain)
Then rebuild:
```bash
docker-compose down
docker-compose up -d --build
```
## 📊 Usage
### Dashboard
- **Host Metrics** - CPU, RAM, network, uptime
- **Container List** - All containers with live stats
- **Quick Actions** - Start/stop/restart buttons
- **Auto-refresh** - Updates every 2-5 seconds
### Container Details
- Real-time metrics graphs
- Live log streaming (no storage, ephemeral)
- Container configuration details
- Per-container alert settings
### Settings
- Discord webhook configuration
- Metrics refresh interval
- Alert debounce settings
- Error pattern regex
## 🔔 Discord Alerts
Set up per-container Discord notifications:
1. Create a Discord webhook in your server
2. In WebUI Settings, add your webhook URL
3. Enable alerts for specific containers
4. Configure triggers:
- **Container Stopped** - Alert when container stops/crashes
- **Error Pattern** - Match regex in logs (default: `(?i)(error|err|exception|traceback|crit(ical)?)`)
Example alert:
```json
{
"username": "Docker WebUI",
"embeds": [{
"title": "Container Stopped",
"color": 15158332,
"fields": [
{"name": "Container", "value": "redis:prod"},
{"name": "Reason", "value": "stop event"},
{"name": "Time", "value": "2025-10-20T10:22:31Z"}
]
}]
}
```
## 🔒 Security
⚠️ **Important Security Considerations:**
- **Root Required** - Container needs root access to Docker socket
- **Trusted Hosts Only** - Docker socket access = full system control
- **Single Admin** - Only one user (configured via env vars)
- **Read-only Mounts** - Socket and `/proc` are mounted read-only
- **No Public Exposure** - Always use behind HTTPS reverse proxy
- **Audit Logging** - All control actions logged to database
### Reverse Proxy Setup (Recommended)
Example Nginx configuration:
```nginx
server {
listen 443 ssl http2;
server_name docker.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
}
}
```
## 🛠️ Development
### Tech Stack
- **Frontend**: React 18 + TypeScript + Vite
- **Backend**: Lovable Cloud (Supabase)
- **UI Framework**: shadcn/ui + Tailwind CSS
- **Real-time**: WebSocket for metrics streaming
- **Deployment**: Docker + Nginx
### Local Development
```bash
# Install dependencies
npm install
# Start dev server
npm run dev
# Build for production
npm run build
```
### Project Structure
```
docker-webui/
├── src/
│ ├── pages/ # Dashboard, Login, Settings
│ ├── components/ # Reusable UI components
│ ├── hooks/ # Custom React hooks
│ └── integrations/ # Supabase client
├── supabase/
│ ├── functions/ # Edge functions (Docker API)
│ └── config.toml # Function configuration
├── Dockerfile # Multi-stage build
├── nginx.conf # Web server config
└── docker-compose.yml # Service definition
```
## 📝 Architecture
```
┌──────────────┐ WebSocket ┌───────────────┐
│ Browser │◄────────────────────────►│ docker-metrics│
│ (React) │ │ Edge Function │
└──────────────┘ └───────┬───────┘
┌──────────────┐ REST API ┌───────▼───────┐
│ Dashboard │◄────────────────────────►│ docker-api │
│ Frontend │ │ Edge Function │
└──────────────┘ └───────┬───────┘
┌──────▼─────────┐
│ /var/run/ │
│ docker.sock │
└────────────────┘
```
**Flow:**
1. Frontend connects via WebSocket for real-time metrics
2. Backend edge functions read Docker socket via Unix socket
3. Container stats streamed every 2 seconds
4. Container list auto-updates via polling + WebSocket
## 🚀 Features in Detail
### Auto-Discovery
- Detects new containers within 5 seconds
- Automatically removes deleted containers from UI
- No manual refresh needed
- Works with `docker run`, `docker-compose`, etc.
### Live Metrics
- **Host**: CPU %, RAM usage, network I/O, uptime
- **Containers**: CPU %, memory, network RX/TX, block I/O
- **Refresh**: 2-second intervals via WebSocket
- **No Storage**: All metrics are ephemeral (not saved)
### Log Streaming
- Streams directly from Docker (no disk storage)
- Client-side regex filtering
- Color-coded timestamps
- Auto-scroll with pause/resume
- Logs disappear on browser close
## 🔮 Roadmap
- [ ] Multi-host support (lightweight agent model)
- [ ] Historical metrics with charts
- [ ] Advanced health check rules
- [ ] Multi-language UI (English/Danish)
- [ ] Docker Compose stack management
- [ ] Volume and network management
- [ ] Resource limits configuration
## ⚠️ Limitations
- **No Shell Access** - WebUI does not provide terminal/exec access
- **No Log Storage** - Logs stream live but are not persisted
- **Single Host** - Multi-host requires future agent architecture
- **Rate Limits** - Discord webhooks have rate limits (use debounce)
- **Root Access** - Requires privileged Docker socket access
## 🐛 Troubleshooting
### Container not accessible from browser
- Check that you're using host networking mode
- Verify firewall allows port 8080
- Try accessing via `http://HOST_IP:8080`
### Metrics not updating
- Check WebSocket connection (WiFi icon in header)
- Verify `/var/run/docker.sock` is mounted
- Check edge function logs in Lovable Cloud
### Can't login
- Ensure you created the admin account first (Settings → Sign Up)
- Verify credentials match `.env` file
- Check browser console for errors
## 📄 License
MIT License
## 🤝 Contributing
Contributions welcome! Please:
1. Fork the repository
2. Create a feature branch
3. Commit your changes
4. Open a Pull Request
## 💬 Support
- **Issues**: Report bugs via GitHub Issues
- **Discussions**: Ask questions in GitHub Discussions
- **Documentation**: See inline code comments
---
**Built with ❤️ using [Lovable](https://lovable.dev)**
Deploy on trusted infrastructure only. Docker socket access = full system control.

253
SETUP.md Normal file
View File

@@ -0,0 +1,253 @@
# Docker WebUI Setup Guide
## First-Time Setup
### Step 1: Configure Environment
1. Copy the example environment file:
```bash
cp .env.example .env
```
2. Edit `.env` with your admin credentials:
```env
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=YourSecurePassword123
TZ=Europe/Copenhagen
```
### Step 2: Build and Start
```bash
docker-compose up -d --build
```
This will:
- Build the React frontend
- Create the Docker container
- Mount `/var/run/docker.sock` (read-only)
- Mount `/proc` for host metrics
- Start the web server on port 8080
### Step 3: Create Admin Account
⚠️ **Important**: You must create the admin account before you can login.
1. Open your browser to `http://your-server-ip:8080`
2. You'll see the login page
3. Click on the Settings page link (or navigate to `/settings`)
4. Look for the "Sign Up" option
5. Enter the EXACT credentials from your `.env` file:
- Email: The value from `ADMIN_EMAIL`
- Password: The value from `ADMIN_PASSWORD`
6. Submit the signup form
### Step 4: Login
After creating the account:
1. Return to the login page (`/login`)
2. Login with your admin credentials
3. You'll be redirected to the dashboard
## System Requirements
- **Docker**: 20.10+
- **Docker Compose**: 1.29+
- **OS**: Linux (tested on Ubuntu 20.04+, Debian 11+)
- **RAM**: Minimum 512MB available
- **Network**: Port 8080 available (or customize in nginx.conf)
## Port Configuration
The default port is **8080**. To change it:
1. Edit `nginx.conf`:
```nginx
server {
listen 8082; # Change this
server_name _;
# ... rest of config
}
```
2. Rebuild the container:
```bash
docker-compose down
docker-compose up -d --build
```
## Reverse Proxy Setup (Recommended)
For production, always use HTTPS via a reverse proxy.
### Nginx Reverse Proxy
Create `/etc/nginx/sites-available/docker-webui`:
```nginx
server {
listen 443 ssl http2;
server_name docker.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/docker.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/docker.yourdomain.com/privkey.pem;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
# WebSocket support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Headers
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;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name docker.yourdomain.com;
return 301 https://$server_name$request_uri;
}
```
Enable and restart:
```bash
sudo ln -s /etc/nginx/sites-available/docker-webui /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```
### Traefik (Docker)
Add labels to `docker-compose.yml`:
```yaml
services:
docker-webui:
# ... existing config
labels:
- "traefik.enable=true"
- "traefik.http.routers.docker-webui.rule=Host(`docker.yourdomain.com`)"
- "traefik.http.routers.docker-webui.entrypoints=websecure"
- "traefik.http.routers.docker-webui.tls.certresolver=letsencrypt"
- "traefik.http.services.docker-webui.loadbalancer.server.port=8080"
```
## Firewall Configuration
If using UFW:
```bash
# Allow from specific IP
sudo ufw allow from 192.168.1.0/24 to any port 8080
# Or allow from anywhere (not recommended)
sudo ufw allow 8080/tcp
```
## Discord Webhook Setup
1. In Discord, go to Server Settings → Integrations → Webhooks
2. Click "New Webhook"
3. Name it "Docker WebUI"
4. Copy the webhook URL
5. In Docker WebUI, go to Settings
6. Paste webhook URL
7. Configure per-container alerts
## Troubleshooting
### "Cannot connect to Docker daemon"
Check socket permissions:
```bash
ls -la /var/run/docker.sock
# Should show: srw-rw---- 1 root docker
# Add your user to docker group
sudo usermod -aG docker $USER
newgrp docker
```
### WebUI not accessible from other machines
Check host networking:
```bash
# Verify the container is using host network
docker inspect docker-webui | grep NetworkMode
# Should show: "NetworkMode": "host"
# Check if port is listening
sudo netstat -tulpn | grep 8080
```
### Metrics not updating
1. Check WebSocket connection (WiFi icon in header)
2. View edge function logs in Lovable Cloud
3. Verify container can access Docker socket:
```bash
docker exec docker-webui ls -la /var/run/docker.sock
```
### High CPU usage
Reduce metrics refresh interval in Settings:
- Default: 2000ms (2 seconds)
- Recommended for many containers: 5000ms (5 seconds)
## Security Best Practices
1. **Change default credentials** in `.env` before first run
2. **Use strong passwords** (minimum 12 characters)
3. **Enable HTTPS** via reverse proxy
4. **Restrict network access** to trusted IPs only
5. **Keep Docker updated** for security patches
6. **Review audit logs** regularly for suspicious activity
7. **Backup configuration** and database regularly
## Updating
```bash
# Pull latest changes
git pull
# Rebuild and restart
docker-compose down
docker-compose up -d --build
```
## Uninstalling
```bash
# Stop and remove container
docker-compose down
# Remove images
docker rmi docker-webui:latest
# Remove volumes (if any)
docker volume prune
```
## Getting Help
- GitHub Issues: Report bugs
- GitHub Discussions: Ask questions
- Documentation: Check README.md

View File

@@ -9,6 +9,8 @@ services:
network_mode: host
environment:
- TZ=${TZ:-Europe/Copenhagen}
- ADMIN_EMAIL=${ADMIN_EMAIL:-admin@docker-webui.local}
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-changeme123}
volumes:
# Mount Docker socket for container management (read-only for safety)
- /var/run/docker.sock:/var/run/docker.sock:ro

View File

@@ -125,27 +125,6 @@ export type Database = {
}
Relationships: []
}
profiles: {
Row: {
created_at: string | null
id: string
updated_at: string | null
username: string
}
Insert: {
created_at?: string | null
id: string
updated_at?: string | null
username: string
}
Update: {
created_at?: string | null
id?: string
updated_at?: string | null
username?: string
}
Relationships: []
}
}
Views: {
[_ in never]: never

View File

@@ -2,7 +2,7 @@ import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Card } from "@/components/ui/card";
import { Server, Lock, Mail } from "lucide-react";
import { Server, Lock, Mail, Shield } from "lucide-react";
import { useNavigate } from "react-router-dom";
import { supabase } from "@/integrations/supabase/client";
import { useToast } from "@/components/ui/use-toast";
@@ -10,36 +10,15 @@ import { useToast } from "@/components/ui/use-toast";
const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [isSignup, setIsSignup] = useState(false);
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
const { toast } = useToast();
const handleAuth = async (e: React.FormEvent) => {
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
try {
if (isSignup) {
const { error } = await supabase.auth.signUp({
email,
password,
options: {
data: {
username: email.split('@')[0]
}
}
});
if (error) throw error;
toast({
title: "Account created!",
description: "You can now log in.",
});
setIsSignup(false);
setPassword("");
} else {
const { error } = await supabase.auth.signInWithPassword({
email,
password,
@@ -48,7 +27,6 @@ const Login = () => {
if (error) throw error;
navigate("/dashboard");
}
} catch (error: any) {
toast({
title: "Authentication failed",
@@ -69,21 +47,21 @@ const Login = () => {
</div>
<h1 className="text-3xl font-bold text-foreground mb-2">Docker WebUI</h1>
<p className="text-muted-foreground text-center">
{isSignup ? 'Create an account to get started' : 'Lightweight container monitoring and control'}
Admin access only
</p>
</div>
<form onSubmit={handleAuth} className="space-y-4">
<form onSubmit={handleLogin} className="space-y-4">
<div className="space-y-2">
<label htmlFor="email" className="text-sm font-medium text-foreground">
Email
Admin Email
</label>
<div className="relative">
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
<Input
id="email"
type="email"
placeholder="admin@example.com"
placeholder="admin@docker-webui.local"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="pl-10 bg-secondary border-border"
@@ -118,29 +96,23 @@ const Login = () => {
className="w-full bg-primary hover:bg-primary/90"
disabled={loading}
>
{loading ? 'Processing...' : (isSignup ? 'Sign Up' : 'Sign In')}
{loading ? 'Signing in...' : 'Sign In'}
</Button>
</form>
<div className="mt-4 text-center">
<button
type="button"
onClick={() => {
setIsSignup(!isSignup);
setPassword("");
}}
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
disabled={loading}
>
{isSignup ? 'Already have an account? Sign in' : "Don't have an account? Sign up"}
</button>
<div className="mt-6 p-4 rounded-lg bg-primary/5 border border-primary/20">
<div className="flex items-start gap-2">
<Shield className="w-5 h-5 text-primary mt-0.5 flex-shrink-0" />
<div className="text-sm text-muted-foreground">
<p className="font-semibold text-foreground mb-1">Single Admin System</p>
<p>Admin credentials are configured in docker-compose.yml via environment variables:</p>
<ul className="mt-2 space-y-1 text-xs font-mono">
<li> ADMIN_EMAIL</li>
<li> ADMIN_PASSWORD</li>
</ul>
</div>
</div>
</div>
{!isSignup && (
<p className="mt-6 text-xs text-center text-muted-foreground">
Secure authentication powered by Lovable Cloud
</p>
)}
</Card>
</div>
);

View File

@@ -0,0 +1,64 @@
-- Remove profiles table (not needed for single admin user)
DROP TABLE IF EXISTS public.profiles CASCADE;
-- Remove the trigger and function for auto-creating profiles
DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
DROP FUNCTION IF EXISTS public.handle_new_user() CASCADE;
-- Update global_settings to not require user_id (single admin)
ALTER TABLE public.global_settings DROP CONSTRAINT IF EXISTS global_settings_user_id_fkey;
ALTER TABLE public.global_settings ALTER COLUMN user_id DROP NOT NULL;
-- Update container_settings to not require user_id
ALTER TABLE public.container_settings DROP CONSTRAINT IF EXISTS container_settings_user_id_fkey;
ALTER TABLE public.container_settings ALTER COLUMN user_id DROP NOT NULL;
-- Update audit_logs to not require user_id
ALTER TABLE public.audit_logs DROP CONSTRAINT IF EXISTS audit_logs_user_id_fkey;
ALTER TABLE public.audit_logs ALTER COLUMN user_id DROP NOT NULL;
-- Update RLS policies for single admin
DROP POLICY IF EXISTS "Users can view their own settings" ON public.global_settings;
DROP POLICY IF EXISTS "Users can insert their own settings" ON public.global_settings;
DROP POLICY IF EXISTS "Users can update their own settings" ON public.global_settings;
CREATE POLICY "Admin can manage global settings"
ON public.global_settings FOR ALL
USING (true)
WITH CHECK (true);
DROP POLICY IF EXISTS "Users can view their own container settings" ON public.container_settings;
DROP POLICY IF EXISTS "Users can insert their own container settings" ON public.container_settings;
DROP POLICY IF EXISTS "Users can update their own container settings" ON public.container_settings;
DROP POLICY IF EXISTS "Users can delete their own container settings" ON public.container_settings;
CREATE POLICY "Admin can manage container settings"
ON public.container_settings FOR ALL
USING (true)
WITH CHECK (true);
DROP POLICY IF EXISTS "Users can view their own audit logs" ON public.audit_logs;
DROP POLICY IF EXISTS "Users can insert their own audit logs" ON public.audit_logs;
CREATE POLICY "Admin can view audit logs"
ON public.audit_logs FOR ALL
USING (true)
WITH CHECK (true);
-- Create function to initialize admin settings on first login
CREATE OR REPLACE FUNCTION public.ensure_admin_settings()
RETURNS TRIGGER AS $$
BEGIN
-- Create default global settings if they don't exist
INSERT INTO public.global_settings (user_id)
SELECT NEW.id
WHERE NOT EXISTS (SELECT 1 FROM public.global_settings LIMIT 1);
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER SET search_path = public;
-- Trigger to ensure settings exist when admin logs in
CREATE TRIGGER ensure_admin_settings_on_login
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION public.ensure_admin_settings();