From 379fe8e4fe60b551273f8af17e2cc9c00227a1a4 Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Mon, 23 Feb 2026 16:14:56 +0100 Subject: [PATCH] feat(vm-builder): Add QCOW2 support for Proxmox/KVM - Add convert_to_qcow2() function using qemu-img - Add QCOW2_FILE output path variable - Create proxmox-import.sh helper script for easy VM import - Update distribution package to include QCOW2 and Proxmox script - Add Proxmox VE instructions to README - Update usage help with QCOW2 output Co-Authored-By: Claude Opus 4.5 --- .claude/settings.local.json | 4 +- .../files/usr/sbin/haproxyctl | 50 ++++++++++- secubox-tools/c3box-vm-builder.sh | 54 ++++++++++- secubox-tools/c3box-vm/proxmox-import.sh | 89 +++++++++++++++++++ 4 files changed, 190 insertions(+), 7 deletions(-) create mode 100755 secubox-tools/c3box-vm/proxmox-import.sh diff --git a/.claude/settings.local.json b/.claude/settings.local.json index d7a31997..d5d35682 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -395,7 +395,9 @@ "Bash(__NEW_LINE_722c25da6bf58fe1__ rm -f \"$COOKIES\" /tmp/login.html)", "WebFetch(domain:portal.nextcloud.com)", "WebFetch(domain:arnowelzel.de)", - "Bash(__NEW_LINE_5c2a7272ff3658b1__ ssh root@192.168.255.1 '\n# Test different sizes to find the limit\nfor size in 1000 5000 10000 20000 40000 60000; do\n CONTENT=$\\(head -c $size /tmp/test-upload.html | base64 -w0\\)\n CSIZE=$\\(echo -n \"\"$CONTENT\"\" | wc -c\\)\n RESULT=$\\(ubus call luci.metablogizer upload_and_create_site \"\"{\\\\\"\"name\\\\\"\":\\\\\"\"sizetest\\\\\"\",\\\\\"\"domain\\\\\"\":\\\\\"\"sizetest.gk2.secubox.in\\\\\"\",\\\\\"\"content\\\\\"\":\\\\\"\"$CONTENT\\\\\"\",\\\\\"\"is_zip\\\\\"\":\\\\\"\"0\\\\\"\"}\"\" 2>&1\\)\n \n if echo \"\"$RESULT\"\" | grep -q \"\"success.*true\"\"; then\n echo \"\"Size $size \\($CSIZE base64\\): OK\"\"\n ubus call luci.metablogizer delete_site \"\"{\\\\\"\"id\\\\\"\":\\\\\"\"site_sizetest\\\\\"\"}\"\" >/dev/null 2>&1\n else\n ERROR=$\\(echo \"\"$RESULT\"\" | head -1\\)\n echo \"\"Size $size \\($CSIZE base64\\): FAILED - $ERROR\"\"\n break\n fi\ndone\n')" + "Bash(__NEW_LINE_5c2a7272ff3658b1__ ssh root@192.168.255.1 '\n# Test different sizes to find the limit\nfor size in 1000 5000 10000 20000 40000 60000; do\n CONTENT=$\\(head -c $size /tmp/test-upload.html | base64 -w0\\)\n CSIZE=$\\(echo -n \"\"$CONTENT\"\" | wc -c\\)\n RESULT=$\\(ubus call luci.metablogizer upload_and_create_site \"\"{\\\\\"\"name\\\\\"\":\\\\\"\"sizetest\\\\\"\",\\\\\"\"domain\\\\\"\":\\\\\"\"sizetest.gk2.secubox.in\\\\\"\",\\\\\"\"content\\\\\"\":\\\\\"\"$CONTENT\\\\\"\",\\\\\"\"is_zip\\\\\"\":\\\\\"\"0\\\\\"\"}\"\" 2>&1\\)\n \n if echo \"\"$RESULT\"\" | grep -q \"\"success.*true\"\"; then\n echo \"\"Size $size \\($CSIZE base64\\): OK\"\"\n ubus call luci.metablogizer delete_site \"\"{\\\\\"\"id\\\\\"\":\\\\\"\"site_sizetest\\\\\"\"}\"\" >/dev/null 2>&1\n else\n ERROR=$\\(echo \"\"$RESULT\"\" | head -1\\)\n echo \"\"Size $size \\($CSIZE base64\\): FAILED - $ERROR\"\"\n break\n fi\ndone\n')", + "WebFetch(domain:admin.gk2.secubox.in)", + "WebFetch(domain:tdah.gk2.secubox.in)" ] } } diff --git a/package/secubox/secubox-app-haproxy/files/usr/sbin/haproxyctl b/package/secubox/secubox-app-haproxy/files/usr/sbin/haproxyctl index e62d8c08..684a70f0 100644 --- a/package/secubox/secubox-app-haproxy/files/usr/sbin/haproxyctl +++ b/package/secubox/secubox-app-haproxy/files/usr/sbin/haproxyctl @@ -486,6 +486,31 @@ generate_certs_list() { # Configuration Generation # =========================================== +# Helper to print a single userlist from UCI +_print_uci_userlist() { + local section="$1" + local name user + + config_get name "$section" name + config_get user "$section" user + + [ -z "$name" ] && return + [ -z "$user" ] && return + + echo "userlist $name" + # user format in UCI: username:password_hash + local username="${user%%:*}" + local password="${user#*:}" + echo " user $username password $password" + echo "" +} + +# Generate all UCI-defined userlists +_generate_uci_userlists() { + config_load haproxy + config_foreach _print_uci_userlist userlist +} + generate_config() { load_config @@ -541,6 +566,9 @@ EOF echo "" >> "$cfg_file" fi + # Generate UCI-defined userlists + _generate_uci_userlists >> "$cfg_file" + # Stats frontend if [ "$stats_enabled" = "1" ]; then cat >> "$cfg_file" << EOF @@ -609,6 +637,14 @@ frontend https-in mode http http-request set-header X-Forwarded-Proto https http-request set-header X-Real-IP %[src] + + # Security: Block access to sensitive paths + acl is_sensitive_path path_beg /.git + acl is_sensitive_path path_beg /.svn + acl is_sensitive_path path_beg /.env + acl is_sensitive_path path_beg /.htaccess + acl is_sensitive_path path_beg /.htpasswd + http-request deny if is_sensitive_path EOF else # Fallback to directory mode if no certs.list @@ -618,6 +654,14 @@ frontend https-in mode http http-request set-header X-Forwarded-Proto https http-request set-header X-Real-IP %[src] + + # Security: Block access to sensitive paths + acl is_sensitive_path path_beg /.git + acl is_sensitive_path path_beg /.svn + acl is_sensitive_path path_beg /.env + acl is_sensitive_path path_beg /.htaccess + acl is_sensitive_path path_beg /.htpasswd + http-request deny if is_sensitive_path EOF fi # Add path-based ACLs BEFORE vhost ACLs (path rules take precedence) @@ -800,10 +844,12 @@ _add_vhost_acl() { esac # Add auth requirement for private vhosts - local auth_enabled + local auth_enabled auth_realm auth_userlist config_get auth_enabled "$section" auth_enabled "0" if [ "$auth_enabled" = "1" ]; then - echo " http-request auth realm \"SecuBox\" if host_${acl_name} !{ http_auth(secubox_users) }" + config_get auth_realm "$section" auth_realm "SecuBox" + config_get auth_userlist "$section" auth_userlist "secubox_users" + echo " http-request auth realm \"$auth_realm\" if host_${acl_name} !{ http_auth($auth_userlist) }" # Log auth user in header for backend (http_auth_user only works after successful auth) echo " http-request set-header X-Auth-User %[http_auth_user] if host_${acl_name}" fi diff --git a/secubox-tools/c3box-vm-builder.sh b/secubox-tools/c3box-vm-builder.sh index 2e2d9451..ddd87961 100755 --- a/secubox-tools/c3box-vm-builder.sh +++ b/secubox-tools/c3box-vm-builder.sh @@ -41,6 +41,7 @@ IMG_FILE="$OUTPUT_DIR/c3box-combined-ext4.img" VMDK_FILE="$OUTPUT_DIR/$VM_NAME.vmdk" OVA_FILE="$OUTPUT_DIR/$VM_NAME.ova" VDI_FILE="$OUTPUT_DIR/$VM_NAME.vdi" +QCOW2_FILE="$OUTPUT_DIR/$VM_NAME.qcow2" print_header() { echo "" @@ -365,6 +366,23 @@ convert_to_vdi() { fi } +convert_to_qcow2() { + print_header "Converting to QCOW2 (Proxmox/KVM)" + + cd "$OUTPUT_DIR" + + if ! command -v qemu-img &>/dev/null; then + print_error "qemu-img not found. Install: sudo apt install qemu-utils" + return 1 + fi + + print_info "Converting to QCOW2 format..." + qemu-img convert -f raw -O qcow2 -o preallocation=metadata "$IMG_FILE" "$QCOW2_FILE" + + print_success "QCOW2 created: $QCOW2_FILE" + print_info "Size: $(du -h "$QCOW2_FILE" | cut -f1)" +} + create_vmx_file() { print_header "Creating VMware Configuration" @@ -521,7 +539,9 @@ create_package() { cp -v "$VMDK_FILE" "$pkg_dir/" 2>/dev/null || true cp -v "$OVA_FILE" "$pkg_dir/" 2>/dev/null || true cp -v "$VDI_FILE" "$pkg_dir/" 2>/dev/null || true + cp -v "$QCOW2_FILE" "$pkg_dir/" 2>/dev/null || true cp -v "$OUTPUT_DIR/$VM_NAME.vmx" "$pkg_dir/" 2>/dev/null || true + cp -v "$BUILD_DIR/proxmox-import.sh" "$pkg_dir/" 2>/dev/null || true # Create README cat > "$pkg_dir/README.md" << 'README' @@ -543,6 +563,30 @@ create_package() { 3. Configure: Linux 64-bit, 2GB RAM, Bridged Network 4. Start the VM +### Proxmox VE + +**Option 1: GUI Import** +1. Upload `C3Box-SecuBox.qcow2` to Proxmox storage +2. Create VM: 2 cores, 2GB RAM, VirtIO SCSI +3. Import disk via Hardware → Add → Hard Disk + +**Option 2: CLI Import** +```bash +scp C3Box-SecuBox.qcow2 root@proxmox:/tmp/ +ssh root@proxmox +./proxmox-import.sh 200 local-lvm +qm start 200 +``` + +**Option 3: Manual qm commands** +```bash +qm create 200 --name secubox --memory 2048 --cores 2 \ + --net0 virtio,bridge=vmbr0 --ostype l26 +qm importdisk 200 C3Box-SecuBox.qcow2 local-lvm +qm set 200 --scsi0 local-lvm:vm-200-disk-0 --boot order=scsi0 +qm start 200 +``` + ### Default Credentials - **Username:** root @@ -614,6 +658,7 @@ cmd_convert() { convert_to_vmdk convert_to_vdi + convert_to_qcow2 create_vmx_file create_ova @@ -659,10 +704,11 @@ Examples: ./c3box-vm-builder.sh package Output: - c3box-vm/output/C3Box-SecuBox.vmdk - VMware disk - c3box-vm/output/C3Box-SecuBox.ova - VMware appliance - c3box-vm/output/C3Box-SecuBox.vdi - VirtualBox disk - c3box-vm/output/C3Box-SecuBox.vmx - VMware config + c3box-vm/output/C3Box-SecuBox.vmdk - VMware disk + c3box-vm/output/C3Box-SecuBox.qcow2 - Proxmox/KVM disk + c3box-vm/output/C3Box-SecuBox.ova - VMware appliance + c3box-vm/output/C3Box-SecuBox.vdi - VirtualBox disk + c3box-vm/output/C3Box-SecuBox.vmx - VMware config USAGE } diff --git a/secubox-tools/c3box-vm/proxmox-import.sh b/secubox-tools/c3box-vm/proxmox-import.sh new file mode 100755 index 00000000..bf28e15e --- /dev/null +++ b/secubox-tools/c3box-vm/proxmox-import.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# +# proxmox-import.sh - Import SecuBox VM into Proxmox VE +# +# Usage: ./proxmox-import.sh [VMID] [STORAGE] [QCOW2_FILE] +# +# Examples: +# ./proxmox-import.sh 200 local-lvm +# ./proxmox-import.sh 201 local-zfs C3Box-SecuBox.qcow2 +# + +set -e + +VMID="${1:-200}" +STORAGE="${2:-local-lvm}" +QCOW2_FILE="${3:-C3Box-SecuBox.qcow2}" + +# Colors +GREEN='\033[0;32m' +BLUE='\033[0;34m' +NC='\033[0m' + +echo -e "${BLUE}SecuBox VM Import for Proxmox VE${NC}" +echo "==================================" +echo "" +echo "VMID: $VMID" +echo "Storage: $STORAGE" +echo "Image: $QCOW2_FILE" +echo "" + +# Check if file exists +if [ ! -f "$QCOW2_FILE" ]; then + echo "Error: QCOW2 file not found: $QCOW2_FILE" + exit 1 +fi + +# Check if qm command exists +if ! command -v qm &>/dev/null; then + echo "Error: qm command not found. Are you running this on Proxmox?" + exit 1 +fi + +# Check if VMID already exists +if qm status $VMID &>/dev/null; then + echo "Error: VM $VMID already exists. Choose a different VMID." + exit 1 +fi + +echo "Creating VM $VMID..." + +# Create VM with basic settings +qm create $VMID \ + --name c3box-secubox \ + --memory 2048 \ + --cores 2 \ + --cpu host \ + --net0 virtio,bridge=vmbr0 \ + --ostype l26 \ + --agent enabled=1 + +echo "Importing disk..." + +# Import disk +qm importdisk $VMID "$QCOW2_FILE" $STORAGE --format qcow2 + +echo "Attaching disk..." + +# Attach disk with VirtIO SCSI +qm set $VMID \ + --scsihw virtio-scsi-pci \ + --scsi0 $STORAGE:vm-$VMID-disk-0 + +# Set boot order +qm set $VMID --boot order=scsi0 + +# Add serial console for troubleshooting +qm set $VMID --serial0 socket + +echo "" +echo -e "${GREEN}VM $VMID created successfully!${NC}" +echo "" +echo "Next steps:" +echo " 1. Start VM: qm start $VMID" +echo " 2. Open console: qm terminal $VMID" +echo " 3. Get IP: qm guest cmd $VMID network-get-interfaces" +echo " 4. Web UI: https://" +echo "" +echo "Default credentials: root / c3box" +echo "IMPORTANT: Change the password on first login!"