- name
- clicd-virtualization-panel
- description
- Use CLICD to manage LXC/KVM virtualization with web console, NAT/IPv6 networking, WebSSH/VNC, resource controls, and security monitoring.
- triggers
- ["set up CLICD virtualization panel","manage LXC containers with CLICD","configure CLICD NAT networking","use CLICD REST API","create KVM virtual machines in CLICD","configure CLICD security alerts","manage CLICD user permissions","integrate CLICD with billing system"]
# CLICD Virtualization Panel Skill
> Skill by [ara.so](https://ara.so) — Devtools Skills collection.
CLICD is a lightweight virtualization management panel for LXC and KVM that provides a web console, CLI tools, REST API, NAT/IPv6 networking, WebSSH/WebVNC access, resource quotas, traffic limits, snapshots, delegated sub-user access, and security monitoring. Built with Go backend and React frontend.
## Installation
### One-Click Install
```bash
# Install CLICD
curl -fsSL https://raw.githubusercontent.com/MengMengCode/CLICD/main/install.sh | sudo sh
# Uninstall CLICD
curl -fsSL https://raw.githubusercontent.com/MengMengCode/CLICD/main/install.sh | sudo sh -s -- uninstall
```
### Prerequisites
- Linux system with systemd
- LXC or KVM/QEMU installed
- Root or sudo access
- iptables and conntrack-tools
### Post-Installation
After installation, CLICD runs as a systemd service:
```bash
# Check service status
sudo systemctl status clicd
# View logs
sudo journalctl -u clicd -f
# Restart service
sudo systemctl restart clicd
```
Access the web interface at `http://your-server-ip:8080` (default port).
## Configuration
### Main Configuration File
Configuration is typically stored in `/etc/clicd/config.yaml` or similar location:
```yaml
server:
host: "0.0.0.0"
port: 8080
tls:
enabled: false
cert_file: ""
key_file: ""
letsencrypt: false
domain: ""
database:
type: "sqlite"
path: "/var/lib/clicd/clicd.db"
lxc:
enabled: true
default_storage_pool: "default"
default_network: "lxcbr0"
kvm:
enabled: true
default_storage_pool: "default"
default_network: "virbr0"
networking:
nat4:
enabled: true
public_ip: "1.2.3.4"
port_range_start: 10000
port_range_end: 60000
ipv6:
enabled: true
prefix: "2001:db8::/48"
auto_detect: true
security:
alerts_enabled: true
conntrack_monitoring: true
admin:
username: "admin"
# Set password via CLI or web interface
```
### Environment Variables
```bash
# Override config file location
export CLICD_CONFIG="/path/to/config.yaml"
# Set admin password (first run)
export CLICD_ADMIN_PASSWORD="your-secure-password"
# Database path
export CLICD_DB_PATH="/var/lib/clicd/clicd.db"
# Log level
export CLICD_LOG_LEVEL="info"
```
## CLI Usage
CLICD provides a CLI interface for common operations:
### Container Management
```bash
# List all containers
clicd container list
# Create a new LXC container
clicd container create \
--name web-server-01 \
--type lxc \
--template ubuntu-22.04 \
--cpu 2 \
--memory 2048 \
--disk 20 \
--ipv4 nat \
--ipv6 auto
# Start a container
clicd container start web-server-01
# Stop a container
clicd container stop web-server-01
# Delete a container
clicd container delete web-server-01 --force
# Reset container password
clicd container password web-server-01 --password "NewSecurePass123"
# Batch operations
clicd container batch-start --ids "1,2,3,4,5"
clicd container batch-stop --pattern "test-*"
```
### Image Management
```bash
# List available images
clicd image list
# Download an image
clicd image download ubuntu-22.04-lxc
# Enable/disable images
clicd image enable ubuntu-22.04-lxc
clicd image disable centos-7-lxc
# Clear image cache
clicd image cache-clear
```
### Networking
```bash
# List NAT port mappings
clicd nat list
# Add port mapping
clicd nat add \
--container web-server-01 \
--protocol tcp \
--public-port 8080 \
--private-port 80
# Remove port mapping
clicd nat remove --id 123
# Check IPv6 status
clicd ipv6 status
# Assign IPv6 to container
clicd ipv6 assign --container web-server-01
```
### User Management
```bash
# Create sub-user
clicd user create \
--username client01 \
--password "SecurePass123" \
--containers "web-server-01,db-server-01"
# Generate delegation link
clicd user link --username client01
# List users
clicd user list
# Update user permissions
clicd user update --username client01 --add-container api-server-01
```
## REST API
All API endpoints are versioned under `/api/v1`. Authentication uses API keys or session tokens.
### Authentication
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
type LoginResponse struct {
Token string `json:"token"`
Message string `json:"message"`
}
func login() (string, error) {
apiURL := os.Getenv("CLICD_API_URL") // e.g., http://localhost:8080
reqBody := LoginRequest{
Username: os.Getenv("CLICD_USERNAME"),
Password: os.Getenv("CLICD_PASSWORD"),
}
jsonData, _ := json.Marshal(reqBody)
resp, err := http.Post(
apiURL+"/api/v1/auth/login",
"application/json",
bytes.NewBuffer(jsonData),
)
if err != nil {
return "", err
}
defer resp.Body.Close()
var loginResp LoginResponse
body, _ := io.ReadAll(resp.Body)
json.Unmarshal(body, &loginResp)
return loginResp.Token, nil
}
```
### Container Operations
```go
type Container struct {
ID int `json:"id"`
Name string `json:"name"`
Type string `json:"type"` // "lxc" or "kvm"
Status string `json:"status"`
CPU int `json:"cpu"`
Memory int `json:"memory"` // MB
Disk int `json:"disk"` // GB
IPv4 string `json:"ipv4"`
IPv6 string `json:"ipv6"`
ExpiryDate string `json:"expiry_date"`
}
type CreateContainerRequest struct {
Name string `json:"name"`
Type string `json:"type"`
Template string `json:"template"`
CPU int `json:"cpu"`
Memory int `json:"memory"`
Disk int `json:"disk"`
Password string `json:"password"`
IPv4Type string `json:"ipv4_type"` // "nat" or "public"
IPv6Enable bool `json:"ipv6_enable"`
ExpiryDays int `json:"expiry_days"`
}
func createContainer(token string, req CreateContainerRequest) (*Container, error) {
apiURL := os.Getenv("CLICD_API_URL")
jsonData, _ := json.Marshal(req)
httpReq, _ := http.NewRequest(
"POST",
apiURL+"/api/v1/containers",
bytes.NewBuffer(jsonData),
)
httpReq.Header.Set("Authorization", "Bearer "+token)
httpReq.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var container Container
body, _ := io.ReadAll(resp.Body)
json.Unmarshal(body, &container)
return &container, nil
}
func listContainers(token string) ([]Container, error) {
apiURL := os.Getenv("CLICD_API_URL")
httpReq, _ := http.NewRequest("GET", apiURL+"/api/v1/containers", nil)
httpReq.Header.Set("Authorization", "Bearer "+token)
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var containers []Container
body, _ := io.ReadAll(resp.Body)
json.Unmarshal(body, &containers)
return containers, nil
}
func startContainer(token string, containerID int) error {
apiURL := os.Getenv("CLICD_API_URL")
httpReq, _ := http.NewRequest(
"POST",
fmt.Sprintf("%s/api/v1/containers/%d/start", apiURL, containerID),
nil,
)
httpReq.Header.Set("Authorization", "Bearer "+token)
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
func deleteContainer(token string, containerID int, force bool) error {
apiURL := os.Getenv("CLICD_API_URL")
url := fmt.Sprintf("%s/api/v1/containers/%d", apiURL, containerID)
if force {
url += "?force=true"
}
httpReq, _ := http.NewRequest("DELETE", url, nil)
httpReq.Header.Set("Authorization", "Bearer "+token)
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
```
### Batch Operations
```go
type BatchActionRequest struct {
IDs []int `json:"ids"`
Action string `json:"action"` // "start", "stop", "restart", "delete"
}
type BatchCreateRequest struct {
Count int `json:"count"`
NamePrefix string `json:"name_prefix"`
Template string `json:"template"`
CPU int `json:"cpu"`
Memory int `json:"memory"`
Disk int `json:"disk"`
}
func batchAction(token string, req BatchActionRequest) error {
apiURL := os.Getenv("CLICD_API_URL")
jsonData, _ := json.Marshal(req)
httpReq, _ := http.NewRequest(
"POST",
apiURL+"/api/v1/containers/batch",
bytes.NewBuffer(jsonData),
)
httpReq.Header.Set("Authorization", "Bearer "+token)
httpReq.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
func batchCreate(token string, req BatchCreateRequest) ([]Container, error) {
apiURL := os.Getenv("CLICD_API_URL")
jsonData, _ := json.Marshal(req)
httpReq, _ := http.NewRequest(
"POST",
apiURL+"/api/v1/containers/batch-create",
bytes.NewBuffer(jsonData),
)
httpReq.Header.Set("Authorization", "Bearer "+token)
httpReq.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var containers []Container
body, _ := io.ReadAll(resp.Body)
json.Unmarshal(body, &containers)
return containers, nil
}
```
### Networking API
```go
type NATMapping struct {
ID int `json:"id"`
ContainerID int `json:"container_id"`
Protocol string `json:"protocol"` // "tcp" or "udp"
PublicPort int `json:"public_port"`
PrivatePort int `json:"private_port"`
}
type CreateNATRequest struct {
ContainerID int `json:"container_id"`
Protocol string `json:"protocol"`
PrivatePort int `json:"private_port"`
PublicPort int `json:"public_port,omitempty"` // Auto-assign if 0
}
func createNATMapping(token string, req CreateNATRequest) (*NATMapping, error) {
apiURL := os.Getenv("CLICD_API_URL")
jsonData, _ := json.Marshal(req)
httpReq, _ := http.NewRequest(
"POST",
apiURL+"/api/v1/nat",
bytes.NewBuffer(jsonData),
)
httpReq.Header.Set("Authorization", "Bearer "+token)
httpReq.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var mapping NATMapping
body, _ := io.ReadAll(resp.Body)
json.Unmarshal(body, &mapping)
return &mapping, nil
}
func assignIPv6(token string, containerID int) (string, error) {
GitHub에서 보기