diff --git a/.github/workflows/build-ova.yml b/.github/workflows/build-ova.yml new file mode 100644 index 0000000..88e5a87 --- /dev/null +++ b/.github/workflows/build-ova.yml @@ -0,0 +1,234 @@ +name: Build ViPER OVA + +on: + push: + tags: + - 'v*.*.*' + - 'release-*' + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4.2.2 + + - name: Install build dependencies + run: | + sudo apt-get update + sudo apt-get install -y qemu-system-x86 qemu-utils ansible python3-pip wget unzip + pip3 install jmespath + + - name: Install Packer + run: | + PACKER_VERSION="1.11.2" + wget -q https://releases.hashicorp.com/packer/${PACKER_VERSION}/packer_${PACKER_VERSION}_linux_amd64.zip + unzip packer_${PACKER_VERSION}_linux_amd64.zip + sudo mv packer /usr/local/bin/ + packer version + + - name: Initialize Packer plugins + env: + PACKER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: packer init viper.pkr.hcl + + - name: Enable KVM + run: | + sudo apt-get install -y cpu-checker + sudo kvm-ok || echo "KVM not available, will use software emulation" + # Set KVM permissions for current user without requiring group membership + if [ -c /dev/kvm ]; then + sudo chmod 666 /dev/kvm + echo "KVM permissions set" + else + echo "KVM device not available" + fi + + - name: Validate Packer template + run: packer validate viper.pkr.hcl + + - name: Free up disk space before build + run: | + echo "Disk space before cleanup:" + df -h + # Remove unnecessary packages + sudo apt-get clean + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf /usr/local/share/boost + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + echo "Disk space after cleanup:" + df -h + + - name: Build VM with Packer + run: | + # Check if KVM is available + if [ -c /dev/kvm ] && [ -r /dev/kvm ] && [ -w /dev/kvm ]; then + echo "KVM is available, using hardware acceleration" + ACCELERATOR="kvm" + else + echo "KVM not available, using software emulation (TCG)" + ACCELERATOR="tcg" + fi + + # Run packer build (without sudo to avoid Ansible file transfer issues) + packer build \ + -var 'headless=true' \ + -var "accelerator=${ACCELERATOR}" \ + viper.pkr.hcl + timeout-minutes: 60 + + - name: Convert to OVA + run: ./scripts/convert-to-ova.sh + + - name: Get version from tag + id: version + run: | + VERSION="${GITHUB_REF#refs/tags/}" + # Sanitize version for filesystem safety (remove/replace special chars) + VERSION_SAFE=$(echo "$VERSION" | tr '/' '-' | tr ':' '-') + echo "VERSION=$VERSION" >> $GITHUB_OUTPUT + echo "VERSION_SAFE=$VERSION_SAFE" >> $GITHUB_OUTPUT + echo "Original version: $VERSION" + echo "Safe version: $VERSION_SAFE" + + - name: Parse repository info + id: repo + run: | + # Sanitize repo name for filesystem-safe paths (openpreserve/ViPER -> openpreserve-ViPER) + REPO_SAFE=$(echo "${{ github.repository }}" | tr '/' '-') + echo "REPO_SAFE=${REPO_SAFE}" >> $GITHUB_OUTPUT + echo "REPO_FULL=${{ github.repository }}" >> $GITHUB_OUTPUT + echo "Repository: ${{ github.repository }}" + echo "Safe name: ${REPO_SAFE}" + + - name: Get commit info + id: commit + run: | + echo "HASH=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + echo "SHORT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + echo "MESSAGE<> $GITHUB_OUTPUT + git log -1 --pretty=%B >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Upload tag metadata + run: | + WORKFLOW_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + + cat > tag-metadata.json <> $GITHUB_OUTPUT + + - name: Commit container to image + run: | + docker commit ${{ steps.container.outputs.container_id }} viper-build:latest + + - name: Tag and push to Docker Hub and GHCR + run: | + # Tag for Docker Hub + docker tag viper-build:latest ${{ env.REGISTRY_DOCKERHUB }}/${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest + docker push ${{ env.REGISTRY_DOCKERHUB }}/${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest + + # Tag for GitHub Container Registry + docker tag viper-build:latest ${{ env.REGISTRY_GHCR }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest + docker push ${{ env.REGISTRY_GHCR }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest + + # If this is a tagged release, also push the version tag + if [[ "${{ github.ref }}" == refs/tags/v* ]]; then + # Extract version from v1.2.0 -> 1.2.0 + VERSION=${GITHUB_REF#refs/tags/v} + docker tag viper-build:latest ${{ env.REGISTRY_DOCKERHUB }}/${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${VERSION} + docker push ${{ env.REGISTRY_DOCKERHUB }}/${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${VERSION} + docker tag viper-build:latest ${{ env.REGISTRY_GHCR }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${VERSION} + docker push ${{ env.REGISTRY_GHCR }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${VERSION} + fi + + - name: Clean up + if: always() + run: | + docker rm -f docker-viper || true + docker rmi viper-build:latest || true diff --git a/.github/workflows/test-api.yml b/.github/workflows/test-api.yml new file mode 100644 index 0000000..ef884c0 --- /dev/null +++ b/.github/workflows/test-api.yml @@ -0,0 +1,111 @@ +name: Test Artifact Upload + +on: + workflow_dispatch: + +jobs: + test-upload: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Parse repository info + id: repo + run: | + REPO_SAFE=$(echo "${{ github.repository }}" | tr '/' '-') + echo "REPO_SAFE=${REPO_SAFE}" >> $GITHUB_OUTPUT + echo "Repository: ${{ github.repository }}" + echo "Safe name: ${REPO_SAFE}" + + - name: Get commit info + id: commit + run: | + echo "HASH=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + echo "SHORT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + echo "MESSAGE<> $GITHUB_OUTPUT + git log -1 --pretty=%B >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Set test tag + id: test + run: | + TEST_TAG="test-$(date +%s)" + echo "TEST_TAG=${TEST_TAG}" >> $GITHUB_OUTPUT + echo "Test tag: ${TEST_TAG}" + + - name: Test metadata upload + env: + ARTIFACT_UPLOAD_TOKEN: ${{ secrets.ARTIFACT_UPLOAD_TOKEN }} + run: | + WORKFLOW_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + + echo "Testing metadata upload..." + echo "Repo: ${{ steps.repo.outputs.REPO_SAFE }}" + echo "Tag: ${{ steps.test.outputs.TEST_TAG }}" + echo "Token present: $([ -n "$ARTIFACT_UPLOAD_TOKEN" ] && echo 'YES' || echo 'NO')" + echo "Token length: ${#ARTIFACT_UPLOAD_TOKEN}" + # echo "Full token (INSECURE - FOR TESTING ONLY): ${ARTIFACT_UPLOAD_TOKEN}" + + cat > tag-metadata.json < test-file.txt + echo "Timestamp: $(date)" >> test-file.txt + ls -lh test-file.txt + md5sum test-file.txt + + - name: Test file upload + env: + ARTIFACT_UPLOAD_TOKEN: ${{ secrets.ARTIFACT_UPLOAD_TOKEN }} + run: | + MD5_HASH=$(md5sum test-file.txt | awk '{print $1}') + SHA256_HASH=$(sha256sum test-file.txt | awk '{print $1}') + + echo "Testing file upload..." + echo "Tag: ${{ steps.test.outputs.TEST_TAG }}" + echo "MD5: ${MD5_HASH}" + echo "SHA256: ${SHA256_HASH}" + echo "URL: https://artifacts.opf-labs.org/upload/${{ steps.repo.outputs.REPO_SAFE }}/${{ steps.test.outputs.TEST_TAG }}" + + curl -v -X POST "https://artifacts.opf-labs.org/upload/${{ steps.repo.outputs.REPO_SAFE }}/${{ steps.test.outputs.TEST_TAG }}" \ + -H "Authorization: Bearer ${ARTIFACT_UPLOAD_TOKEN}" \ + -F "file=@test-file.txt" \ + -F "md5=${MD5_HASH}" \ + -F "sha256=${SHA256_HASH}" \ + -F "file_type=Test File" \ + --fail-with-body + + - name: Show results + if: always() + run: | + echo "" + echo "Test completed!" + echo "Check: https://artifacts.opf-labs.org/browse/${{ steps.repo.outputs.REPO_SAFE }}" diff --git a/.gitignore b/.gitignore index 86a37fb..ebf2255 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,21 @@ .vagrant +# Docker +volumes/ + +# Packer +packer_cache/ +output-qemu/ +*.box +crash.log + +# Build outputs +output/ +seed-ovf/ +*.ova +*.vmdk +*.qcow2 +*.iso + +# Logs +/tmp/*.log diff --git a/PACKER_BUILD.md b/PACKER_BUILD.md new file mode 100644 index 0000000..e45c954 --- /dev/null +++ b/PACKER_BUILD.md @@ -0,0 +1,78 @@ +# ViPER Packer Build + +Packer configuration to build the ViPER VM using QEMU/KVM and export to OVA format. + +## Prerequisites + +```bash +# Install required tools +sudo apt-get update +sudo apt-get install -y qemu-system-x86 qemu-utils ansible python3-pip wget unzip +pip3 install jmespath + +# Install Packer +PACKER_VERSION="1.11.2" +wget https://releases.hashicorp.com/packer/${PACKER_VERSION}/packer_${PACKER_VERSION}_linux_amd64.zip +unzip packer_${PACKER_VERSION}_linux_amd64.zip +sudo mv packer /usr/local/bin/ +rm packer_${PACKER_VERSION}_linux_amd64.zip + +# Initialize Packer plugins +packer init viper.pkr.hcl + +# Validate the template +packer validate viper.pkr.hcl +``` + +## Building the VM + +### Basic build (with GUI) +```bash +packer build viper.pkr.hcl +``` + +### Headless build (no GUI - for CI/CD) +```bash +packer build -var 'headless=true' viper.pkr.hcl +``` + +### Custom configuration +```bash +packer build \ + -var 'vm_name=viper-custom' \ + -var 'cpus=4' \ + -var 'memory=8192' \ + -var 'headless=true' \ + viper.pkr.hcl +``` + +## Post-Build: Convert to OVA + +After Packer completes, convert the QCOW2 image to OVA: + +```bash +./scripts/convert-to-ova.sh +``` + +The OVA file will be created at: `output/viper-v1.2-alpha.ova` + +## Files Created + +- `viper.pkr.hcl` - Main Packer configuration (HCL2 format) +- `http/preseed.cfg` - Debian preseed for automated installation +- `ansible/packer.yml` - Ansible playbook for Packer builds +- `scripts/convert-to-ova.sh` - Script to convert QCOW2 to OVA + +## Workflow + +1. Packer downloads Debian ISO +2. QEMU boots the ISO and runs unattended installation via preseed +3. Ansible provisioning runs (using existing roles: viper.setup, viper.tools) +4. Conversion script creates VMDK and packages as OVA + +## Notes + +- The preseed configures a `vagrant` user (password: `vagrant`) with sudo access +- SSH key authentication is set up automatically +- The build takes approximately 20-30 minutes depending on network speed +- Final OVA is compatible with VirtualBox, VMware, and other OVF-compliant platforms diff --git a/README.md b/README.md index f16f5c9..4b002b9 100644 --- a/README.md +++ b/README.md @@ -26,12 +26,21 @@ You'll need [Virtual Box](https://www.virtualbox.org/) on your machine to act as - Check that you have hardware [virtualisation enabled in your BIOS](https://bce.berkeley.edu/enabling-virtualization-in-your-pc-bios.html). - Please install the [Extension Pack](https://www.virtualbox.org/manual/ch01.html#intro-installing). -### Downloading the virtual Machine +### Downloading the Virtual Machine -Download the latest version of ViPER at . ViPER is distributed as a [prebuilt OVF file](https://www.virtualbox.org/manual/ch01.html#ovf-about). The download takes some time as the file is about 5GB. +ViPER VM images (OVA and QCOW2) are available for download from the [OPF artifact server](https://artifacts.opf-labs.org/browse). + +- **OVA**: Universal OVF package for VirtualBox/VMware (recommended for most users). +- **QCOW2**: For QEMU/KVM environments (native format, best performance). + +The download takes some time as the files are about 5GB. [These instructions](https://www.virtualbox.org/manual/ch01.html#ovf) tell you how to import the OVA file into VirtualBox so you can start it. +### Docker + +ViPER is also available as a Docker image. See the [Docker Hub page](https://hub.docker.com/) for pull instructions and usage details. + ### Logging onto the machine Account login should be automatic. Regardless the account name is `viper` with a blank password. diff --git a/ansible/docker-viper.yml b/ansible/docker-viper.yml new file mode 100644 index 0000000..26f2a84 --- /dev/null +++ b/ansible/docker-viper.yml @@ -0,0 +1,32 @@ +- name: Run ViPER Ansible job against Docker container + hosts: localhost + tasks: + - name: Create a Docker container + community.docker.docker_container: + name: docker-viper + # image: locked in a version to Debian 12 Bookworkm due to libqt5webkit5 removal in Debian 13 Trixie breaking mediaconch-gui + image: linuxserver/webtop:amd64-debian-xfce-9a3339d4-ls122 + state: started + command: sleep infinity + published_ports: + - "3100:3000" + - "3101:3001" + volumes: + # - "../volumes/data-xfce:/config" # Mount host directory to container directory + # - "../ansible:/ansible" # Mount the ansible dir + env: + PUID: "1000" # Set PUID environment variable + PGID: "1000" # Set PGID environment variable + TZ: "Europe/London" # Set timezone environment variable + + - name: Add container to inventory + add_host: + name: docker-viper + ansible_connection: docker + ansible_user: root + +- hosts: docker-viper + become: true + roles: + - { role: viper.setup } + - { role: viper.tools } \ No newline at end of file diff --git a/ansible/inventory.docker.yml b/ansible/inventory.docker.yml new file mode 100644 index 0000000..d5b1ab6 --- /dev/null +++ b/ansible/inventory.docker.yml @@ -0,0 +1,3 @@ +plugin: community.docker.docker_containers +docker_host: unix://var/run/docker.sock + diff --git a/ansible/packer.yml b/ansible/packer.yml new file mode 100644 index 0000000..cbb6a6b --- /dev/null +++ b/ansible/packer.yml @@ -0,0 +1,15 @@ +--- +# Playbook for Packer builds - simplified without SSH key scanning +# File: ansible/packer.yml + +- hosts: default + become: true + gather_facts: yes + vars: + viper_env_timezone: "Europe/Berlin" + opf_server_hostname: "viper" + opf_server_hostdomain: "viper.test" + opf_server_fqdn: "{{ opf_server_hostname }}.{{ opf_server_hostdomain }}" + roles: + - { role: viper.setup } + - { role: viper.tools } diff --git a/ansible/roles/viper.setup/defaults/main.yml b/ansible/roles/viper.setup/defaults/main.yml index 8d79b32..d2ef5cf 100644 --- a/ansible/roles/viper.setup/defaults/main.yml +++ b/ansible/roles/viper.setup/defaults/main.yml @@ -1,7 +1,26 @@ --- # viper.setup default values -viper_env_apt_defaults: - # Source control a + +# Default packages for Docker containers (minimal set, for Debian Bookworm) +viper_env_apt_docker_defaults: + - "git" + - "apt-transport-https" + - "psmisc" + - "net-tools" + - "python3-psutil" + - "zip" + - "unzip" + - "keyboard-configuration" + - "console-setup" + - "openjdk-17-jre" + - "evince" + - "locales" + - "firefox-esr" + - "conky-all" + +# Default packages for VMs (includes desktop environment) +viper_env_apt_vm_defaults: + # Source control - "git" # Debian lacks the ability to download apt over HTTPS - "apt-transport-https" @@ -23,8 +42,29 @@ viper_env_apt_defaults: # GNOME desktop and Nemo add on for icon display - "task-gnome-desktop" - "nemo" + - "locales" + # GRUB tools for boot configuration + - "grub-common" + +# Use Docker defaults if running in Docker, otherwise use VM defaults +viper_env_apt_defaults: "{{ (ansible_virtualization_type == 'docker') | ternary(viper_env_apt_docker_defaults, viper_env_apt_vm_defaults) }}" + +# Packages to remove from Docker containers (XFCE cruft - save space) +# Note: aspell, aspell-en, dictionaries-common, emacsen-common cannot be removed as they are XFCE dependencies +# Note: locales-all kept (227MB) to avoid locale generation issues +# Note: Removing Chrome/Chromium because sandbox doesn't work in Docker +viper_env_apt_docker_remove: + - "nano" + - "vim-tiny" + - "vim-common" + - "w3m" + - "xterm" + - "chromium" + - "chromium-common" + - "chromium-sandbox" -viper_env_apt_remove: +# Packages to remove from VMs (Debian 12 cruft) +viper_env_apt_vm_remove: - "acpi" - "apcid" - "aptitude" @@ -99,6 +139,8 @@ viper_env_apt_remove: - "whois" - "zeroinstall-injector" +# Use Docker removal list if running in Docker, otherwise use VM removal list +viper_env_apt_remove: "{{ (ansible_virtualization_type == 'docker') | ternary(viper_env_apt_docker_remove, viper_env_apt_vm_remove) }}" viper: setup: diff --git a/ansible/roles/viper.setup/files/conky.conf b/ansible/roles/viper.setup/files/conky.conf new file mode 100644 index 0000000..ff81808 --- /dev/null +++ b/ansible/roles/viper.setup/files/conky.conf @@ -0,0 +1,87 @@ +-- Conky configuration for ViPER Docker +-- Simple system monitor with CPU, RAM, and Network stats + +conky.config = { + -- Window settings + alignment = 'top_right', + gap_x = 20, + gap_y = 60, + minimum_width = 250, + maximum_width = 250, + + -- Appearance + background = true, + border_width = 1, + default_color = '8A2BE2', + default_outline_color = 'white', + default_shade_color = 'white', + draw_borders = false, + draw_graph_borders = true, + draw_outline = false, + draw_shades = false, + + -- Transparency + own_window = true, + own_window_class = 'Conky', + own_window_type = 'normal', + own_window_transparent = false, + own_window_argb_visual = true, + own_window_argb_value = 180, + own_window_hints = 'undecorated,below,sticky,skip_taskbar,skip_pager', + + -- Font + use_xft = true, + font = 'DejaVu Sans Mono:size=9', + + -- Update interval + update_interval = 2.0, + cpu_avg_samples = 2, + net_avg_samples = 2, + + -- Double buffer to reduce flicker + double_buffer = true, + + -- Colors (all white) + default_color = 'FFFFFF', + color1 = 'FFFFFF', -- White + color2 = 'FFFFFF', -- White + color3 = 'FFFFFF', -- White +} + +conky.text = [[ +${color}${font DejaVu Sans:size=11:bold}ViPER SYSTEM${font} +${hr 2} + +${font DejaVu Sans:size=9:bold}CPU${goto 95}RAM${goto 175}NET${font} +${cpubar 60,70}${goto 90}${membar 60,70}${goto 170}${downspeedgraph eth0 60,70 FFFFFF FFFFFF -t} +${font DejaVu Sans:size=8}${cpu}%${goto 100}${memperc}%${goto 180}${downspeed eth0}${font} + +${voffset 10}${font DejaVu Sans:size=10:bold}Time & Location${font} +${time %A, %d %B %Y} +${time %H:%M:%S} +Timezone: ${tztime Europe/London %Z} + +${font DejaVu Sans:size=10:bold}System Temperature${font} +CPU Temp: ${hwmon 0 temp 1}°C + +${font DejaVu Sans:size=10:bold}CPU Usage${font} +CPU: ${cpu cpu0}% +${cpubar cpu0 8,} +${cpugraph cpu0 40,250 FFFFFF FFFFFF} + +${font DejaVu Sans:size=10:bold}Memory${font} +RAM: $mem / $memmax +${membar 8,} + +${font DejaVu Sans:size=10:bold}Disk I/O${font} +Read: ${diskio_read} +${diskiograph_read 30,250 FFFFFF FFFFFF} +Write: ${diskio_write} +${diskiograph_write 30,250 FFFFFF FFFFFF} + +${font DejaVu Sans:size=10:bold}Network${font} +Up: ${upspeed eth0} +${upspeedgraph eth0 30,250 FFFFFF FFFFFF} +Down: ${downspeed eth0} +${downspeedgraph eth0 30,250 FFFFFF FFFFFF} +]] diff --git a/ansible/roles/viper.setup/files/toggle-conky.desktop b/ansible/roles/viper.setup/files/toggle-conky.desktop new file mode 100644 index 0000000..143821f --- /dev/null +++ b/ansible/roles/viper.setup/files/toggle-conky.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=Toggle System Monitor +Comment=Show/Hide Conky system monitor +Exec=/usr/local/bin/toggle-conky.sh +Icon=utilities-system-monitor +Terminal=false +Categories=System;Monitor; diff --git a/ansible/roles/viper.setup/files/toggle-conky.sh b/ansible/roles/viper.setup/files/toggle-conky.sh new file mode 100644 index 0000000..8a73673 --- /dev/null +++ b/ansible/roles/viper.setup/files/toggle-conky.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Toggle Conky system monitor on/off + +if pgrep -x conky > /dev/null; then + # Conky is running, kill it + pkill conky + notify-send "Conky" "System monitor hidden" -i utilities-system-monitor +else + # Conky is not running, start it + if [ -f /config/.conkyrc ]; then + conky -c /config/.conkyrc > /dev/null 2>&1 & + notify-send "Conky" "System monitor shown" -i utilities-system-monitor + else + notify-send "Conky" "Config file not found" -i dialog-error + fi +fi diff --git a/ansible/roles/viper.setup/handlers/main.yml b/ansible/roles/viper.setup/handlers/main.yml index 7d6ca9e..9b24f2a 100644 --- a/ansible/roles/viper.setup/handlers/main.yml +++ b/ansible/roles/viper.setup/handlers/main.yml @@ -15,4 +15,15 @@ command: "{{ item }}" with_items: - dpkg-reconfigure locales -f noninteractive - - /usr/sbin/locale-gen \ No newline at end of file + - /usr/sbin/locale-gen + +- name: update grub configuration + ansible.builtin.shell: | + if [ -x /usr/sbin/update-grub ]; then + /usr/sbin/update-grub + elif [ -x /usr/sbin/grub-mkconfig ]; then + /usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg + else + echo "ERROR: No grub update tool found" >&2 + exit 1 + fi diff --git a/ansible/roles/viper.setup/tasks/desktop.yml b/ansible/roles/viper.setup/tasks/desktop.yml index e264bb1..9e97474 100644 --- a/ansible/roles/viper.setup/tasks/desktop.yml +++ b/ansible/roles/viper.setup/tasks/desktop.yml @@ -1,5 +1,6 @@ --- # desktop Environment setup for ViPER tools +# These tasks are for GNOME desktop (VMs) - Docker containers use XFCE which is pre-configured - name: "Desktop | Create Nemo config directory." file: @@ -8,21 +9,25 @@ mode: '0755' owner: "{{ viper.setup.limited_account.name }}" group: "{{ viper.setup.limited_account.name }}" + when: ansible_virtualization_type != "docker" - name: "Desktop | Add hidden desktop file for Nemo and icons" copy: src: "files/home/.config/autostart/nemo-autostart-with-gnome.desktop" dest: "/home/{{ viper.setup.limited_account.name }}/.config/autostart/nemo-autostart-with-gnome.desktop" + when: ansible_virtualization_type != "docker" - name: "Desktop | Create user shared backgrounds directory." file: path: "/usr/local/share/backgrounds" state: directory + when: ansible_virtualization_type != "docker" - name: "Desktop | Copy ViPER wallpaper to shared backgrounds directory." copy: src: "files/usr/local/share/backgrounds/viper-desktop.jpg" dest: "/usr/local/share/backgrounds/viper-desktop.jpg" + when: ansible_virtualization_type != "docker" - name: "Desktop | Set wallpaper for user." become: true @@ -31,6 +36,7 @@ key: "/org/gnome/desktop/background/picture-uri" value: "'file:///usr/local/share/backgrounds/viper-desktop.jpg'" state: present + when: ansible_virtualization_type != "docker" - name: "Desktop | Set dark theme wallpaper for user." become: true @@ -39,6 +45,7 @@ key: "/org/gnome/desktop/background/picture-uri-dark" value: "'file:///usr/local/share/backgrounds/viper-desktop.jpg'" state: present + when: ansible_virtualization_type != "docker" - name: "Desktop | Set screensaver wallpaper for user." become: true @@ -47,6 +54,7 @@ key: "/org/gnome/desktop/screensaver/picture-uri" value: "'file:///usr/local/share/backgrounds/viper-desktop.jpg'" state: present + when: ansible_virtualization_type != "docker" - name: "Desktop | Set dark theme screensaver wallpaper for user." become: true @@ -55,6 +63,7 @@ key: "/org/gnome/desktop/screensaver/picture-uri-dark" value: "'file:///usr/local/share/backgrounds/viper-desktop.jpg'" state: present + when: ansible_virtualization_type != "docker" - name: "Desktop | Tell Nemo to show mounted volumes on desktop." become: true @@ -63,3 +72,95 @@ key: "/org/nemo/desktop/volumes-visible" value: "true" state: present + when: ansible_virtualization_type != "docker" + +# Docker/XFCE Desktop Configuration +- name: "DOCKER | Create shared backgrounds directory." + file: + path: "/usr/local/share/backgrounds" + state: directory + when: ansible_virtualization_type == "docker" + +- name: "DOCKER | Copy ViPER wallpaper to shared backgrounds directory." + copy: + src: "files/usr/local/share/backgrounds/viper-desktop.jpg" + dest: "/usr/local/share/backgrounds/viper-desktop.jpg" + when: ansible_virtualization_type == "docker" + +- name: "DOCKER | Create Conky config directory." + file: + path: "/usr/local/share/conky" + state: directory + when: ansible_virtualization_type == "docker" + +- name: "DOCKER | Copy Conky system monitor config to system location." + copy: + src: "files/conky.conf" + dest: "/usr/local/share/conky/conky.conf" + mode: '0644' + when: ansible_virtualization_type == "docker" + +- name: "DOCKER | Copy Conky toggle script to system bin." + copy: + src: "files/toggle-conky.sh" + dest: "/usr/local/bin/toggle-conky.sh" + owner: "root" + group: "root" + mode: '0755' + when: ansible_virtualization_type == "docker" + +- name: "DOCKER | Copy Conky toggle desktop file to applications menu." + copy: + src: "files/toggle-conky.desktop" + dest: "/usr/share/applications/toggle-conky.desktop" + owner: "root" + group: "root" + mode: '0644' + when: ansible_virtualization_type == "docker" + +# Note: /config is a volume mount, so we cannot create files there during build +# All /config configuration is handled by the post-install script at runtime + +- name: "DOCKER | Install XFCE system monitor plugins." + apt: + name: + - xfce4-systemload-plugin + - xfce4-cpugraph-plugin + - xfce4-netload-plugin + state: present + when: ansible_virtualization_type == "docker" + +- name: "DOCKER | Configure Firefox as default browser in XFCE." + file: + src: "/usr/share/applications/firefox-esr.desktop" + dest: "/usr/share/applications/chromium.desktop" + state: link + force: yes + when: ansible_virtualization_type == "docker" + +- name: "DOCKER | Copy post-install script for desktop icon configuration." + copy: + src: "{{ playbook_dir }}/../docker-files/post-install.sh" + dest: "/usr/local/bin/viper-post-install.sh" + owner: "root" + group: "root" + mode: '0755' + when: ansible_virtualization_type == "docker" + +- name: "DOCKER | Create system-wide autostart desktop file for post-install script." + copy: + dest: "/etc/xdg/autostart/viper-post-install.desktop" + owner: "root" + group: "root" + mode: '0644' + content: | + [Desktop Entry] + Type=Application + Name=ViPER Desktop Setup + Exec=/usr/local/bin/viper-post-install.sh + Hidden=false + NoDisplay=true + X-GNOME-Autostart-enabled=true + X-GNOME-Autostart-Delay=10 + StartupNotify=false + when: ansible_virtualization_type == "docker" diff --git a/ansible/roles/viper.setup/tasks/main.yml b/ansible/roles/viper.setup/tasks/main.yml index 7262985..9a3f8c1 100644 --- a/ansible/roles/viper.setup/tasks/main.yml +++ b/ansible/roles/viper.setup/tasks/main.yml @@ -12,6 +12,7 @@ - name: "ViPER SETUP | Secure server." import_tasks: security/main.yml + when: ansible_virtualization_type != "docker" - name: "ViPER SETUP | Setup desktop environment." import_tasks: desktop.yml diff --git a/ansible/roles/viper.setup/tasks/prerequisites.yml b/ansible/roles/viper.setup/tasks/prerequisites.yml index 33f9a31..0d2afbb 100644 --- a/ansible/roles/viper.setup/tasks/prerequisites.yml +++ b/ansible/roles/viper.setup/tasks/prerequisites.yml @@ -1,6 +1,13 @@ --- # Prerequisites for viper.setup +# openjdk-17 setup requires this path exist before we continue inside a container. +- name: "APT | Ensure /usr/share/man/man1/ directory exists" + file: + path: /usr/share/man/man1/ + state: directory + mode: '0755' + - name: "APT | Install apt package pre-requisites." apt: name: "{{ viper.setup.prereqs.apt }}" @@ -26,4 +33,29 @@ - name: "LOCALE | Setup keyboard defaults." copy: src: "files/etc/default/keyboard" - dest: "/etc/default/keyboard" \ No newline at end of file + dest: "/etc/default/keyboard" + +- name: "GRUB | Configure GRUB to skip boot menu." + lineinfile: + path: /etc/default/grub + regexp: '^GRUB_TIMEOUT=' + line: 'GRUB_TIMEOUT=0' + backup: yes + notify: update grub configuration + when: ansible_virtualization_type != "docker" + +- name: "GRUB | Hide GRUB menu for faster boot." + lineinfile: + path: /etc/default/grub + regexp: '^GRUB_TIMEOUT_STYLE=' + line: 'GRUB_TIMEOUT_STYLE=hidden' + insertafter: '^GRUB_TIMEOUT=' + notify: update grub configuration + when: ansible_virtualization_type != "docker" + +- name: "APT | Remove unnecessary packages." + apt: + name: "{{ viper.setup.remove.apt }}" + state: "absent" + purge: yes + autoremove: yes diff --git a/ansible/roles/viper.setup/tasks/security/iptables/main.yml b/ansible/roles/viper.setup/tasks/security/iptables/main.yml index 4fe2682..afa9046 100644 --- a/ansible/roles/viper.setup/tasks/security/iptables/main.yml +++ b/ansible/roles/viper.setup/tasks/security/iptables/main.yml @@ -1,6 +1,11 @@ --- # Main task entry point for viper.setup +- name: "APT | Install iptables." + apt: + name: iptables + state: present + - name: "IMPORT | IPTABLES rules for IPV4." import_tasks: ipv4-rules.yml when: viper.setup.ip.v4 is defined and viper.setup.ip.v4 != "" diff --git a/ansible/roles/viper.setup/tasks/server.yml b/ansible/roles/viper.setup/tasks/server.yml index 2cf1651..dc1b5ba 100644 --- a/ansible/roles/viper.setup/tasks/server.yml +++ b/ansible/roles/viper.setup/tasks/server.yml @@ -15,10 +15,26 @@ src: "etc/hosts.j2" dest: "/etc/hosts" owner: "root" + when: ansible_virtualization_type != "docker" - name: "HOST | Set the timezone: {{ viper.setup.timezone }}." timezone: name: "{{ viper.setup.timezone }}" + when: ansible_virtualization_type != "docker" + +- name: "DOCKER | Set timezone via symlink" + ansible.builtin.file: + src: "/usr/share/zoneinfo/{{ viper.setup.timezone }}" + dest: /etc/localtime + state: link + force: yes + when: ansible_virtualization_type == "docker" + +- name: "DOCKER | Set timezone in /etc/timezone" + ansible.builtin.copy: + content: "{{ viper.setup.timezone }}\n" + dest: /etc/timezone + when: ansible_virtualization_type == "docker" - name: "APT | Remove unnecessary packages." apt: diff --git a/ansible/roles/viper.setup/tasks/user.yml b/ansible/roles/viper.setup/tasks/user.yml index 72189a5..7fc2936 100644 --- a/ansible/roles/viper.setup/tasks/user.yml +++ b/ansible/roles/viper.setup/tasks/user.yml @@ -18,11 +18,19 @@ update_password: always append: yes +- name: "USER | Check if SSH public key exists" + local_action: + module: stat + path: "{{ lookup('env','HOME') }}/.ssh/id_rsa.pub" + register: ssh_key_file + become: false + - name: "USER | Set authorized key for user: {{ viper.setup.limited_account.name }}, copied from current user." authorized_key: user: "{{ viper.setup.limited_account.name }}" state: present key: "{{ lookup('file', lookup('env','HOME') + '/.ssh/id_rsa.pub') }}" + when: ssh_key_file.stat.exists and ansible_virtualization_type != "docker" - name: "USER | Create user desktop dir." file: @@ -32,6 +40,46 @@ owner: "{{ viper.setup.limited_account.name }}" group: "{{ viper.setup.limited_account.name }}" +- name: "USER | Create .config directory for user." + file: + path: "{{ viper.setup.limited_account.home }}/.config" + state: directory + mode: '0755' + owner: "{{ viper.setup.limited_account.name }}" + group: "{{ viper.setup.limited_account.name }}" + +- name: "USER | Mark GNOME initial setup as completed." + file: + path: "{{ viper.setup.limited_account.home }}/.config/gnome-initial-setup-done" + state: touch + mode: '0644' + owner: "{{ viper.setup.limited_account.name }}" + group: "{{ viper.setup.limited_account.name }}" + when: ansible_virtualization_type != "docker" + +- name: "USER | Create autostart directory for user." + file: + path: "{{ viper.setup.limited_account.home }}/.config/autostart" + state: directory + mode: '0755' + owner: "{{ viper.setup.limited_account.name }}" + group: "{{ viper.setup.limited_account.name }}" + +- name: "USER | Disable GNOME session save/restore to prevent Activities view on login." + copy: + dest: "{{ viper.setup.limited_account.home }}/.config/autostart/gnome-session-restore-disable.desktop" + content: | + [Desktop Entry] + Type=Application + Name=Disable Session Restore + Exec=sh -c "dconf write /org/gnome/desktop/session/save-last-session false && dconf write /org/gnome/desktop/wm/preferences/num-workspaces 1 && dconf write /org/gnome/mutter/dynamic-workspaces false" + NoDisplay=true + X-GNOME-Autostart-enabled=true + mode: '0644' + owner: "{{ viper.setup.limited_account.name }}" + group: "{{ viper.setup.limited_account.name }}" + when: ansible_virtualization_type != "docker" + - name: "USER | Add execute privileges to the desktop dir." file: path: "{{ viper.setup.limited_account.home }}/Desktop" @@ -43,3 +91,4 @@ src: "etc/gdm3/daemon.j2" dest: "/etc/gdm3/daemon.conf" owner: "root" + when: ansible_virtualization_type != "docker" diff --git a/ansible/roles/viper.setup/templates/etc/gdm3/daemon.j2 b/ansible/roles/viper.setup/templates/etc/gdm3/daemon.j2 index e1f2eae..8a9a23f 100644 --- a/ansible/roles/viper.setup/templates/etc/gdm3/daemon.j2 +++ b/ansible/roles/viper.setup/templates/etc/gdm3/daemon.j2 @@ -4,7 +4,7 @@ [daemon] # Uncoment the line below to force the login screen to use Xorg -#WaylandEnable=false +WaylandEnable=false # Enabling automatic login AutomaticLoginEnable=True diff --git a/ansible/roles/viper.tools/defaults/main.yml b/ansible/roles/viper.tools/defaults/main.yml index 4cc15c8..fffd596 100644 --- a/ansible/roles/viper.tools/defaults/main.yml +++ b/ansible/roles/viper.tools/defaults/main.yml @@ -22,7 +22,7 @@ viper_debian_tools: # Debian GIMP installation - "gimp" -# Desktop icon files for the about to be copied to remote desktop +# Desktop icon files viper_debian_icons: - "org.gnome.Evince" - "gimp" @@ -54,6 +54,12 @@ tika_minor: "9" tika_patch: "2" tika_version: "{{ tika_major }}.{{ tika_minor }}.{{ tika_patch }}" +# FIDO version details +fido_version: "1.6.1" + +# jpylyzer version details +jpylyzer_version: "2.2.1" + viper: tools: jhove: @@ -126,6 +132,20 @@ viper: bin: "{{ viper_bin }}" lib: "{{ viper_lib }}/veraPDF/{{ verapdf_version }}" src: "{{ viper_src }}/veraPDF/{{ verapdf_version }}" + fido: + name: "fido" + version: "{{ fido_version }}" + package: "opf-fido" + source: + git: "https://github.com/openpreserve/fido.git" + tag: "v{{ fido_version }}" + jpylyzer: + name: "jpylyzer" + version: "{{ jpylyzer_version }}" + package: "jpylyzer" + source: + git: "https://github.com/openpreserve/jpylyzer.git" + tag: "{{ jpylyzer_version }}" mediaarea: - name: "libzen0" download_url: "https://mediaarea.net/download/binary/libzen0/0.4.41/libzen0v5_0.4.41-1_amd64.Debian_12.deb" diff --git a/ansible/roles/viper.tools/tasks/debTools.yml b/ansible/roles/viper.tools/tasks/debTools.yml index ac05d4e..47e37c7 100644 --- a/ansible/roles/viper.tools/tasks/debTools.yml +++ b/ansible/roles/viper.tools/tasks/debTools.yml @@ -8,7 +8,7 @@ name: "{{ viper_debian_tools }}" state: "latest" -- name: "Debian Tools | Add desktop icons for Debian tools." +- name: "Debian Tools | Add desktop icons for Debian tools (VM)." copy: src: "/usr/share/applications/{{ item }}.desktop" dest: "/home/{{ viper_account_name }}/Desktop/{{ item }}.desktop" @@ -18,9 +18,28 @@ remote_src: yes with_items: "{{ viper_debian_icons }}" -- name: "Debian Tools | Set executable permissions for Desktop icons." +- name: "Debian Tools | Set executable permissions for Desktop icons (VM)." ansible.builtin.file: path: "/home/{{ viper_account_name }}/Desktop/{{ item }}.desktop" state: file mode: "u+x,g+x,o+x" with_items: "{{ viper_debian_icons }}" + +- name: "Debian Tools | Copy desktop icons to /config/Desktop for Docker." + copy: + src: "/usr/share/applications/{{ item }}.desktop" + dest: "/config/Desktop/{{ item }}.desktop" + owner: "abc" + group: "abc" + mode: preserve + remote_src: yes + with_items: "{{ viper_debian_icons }}" + when: ansible_virtualization_type == "docker" + +- name: "Debian Tools | Set executable permissions for Desktop icons (Docker)." + ansible.builtin.file: + path: "/config/Desktop/{{ item }}.desktop" + state: file + mode: "u+x,g+x,o+x" + with_items: "{{ viper_debian_icons }}" + when: ansible_virtualization_type == "docker" diff --git a/ansible/roles/viper.tools/tasks/fido.yml b/ansible/roles/viper.tools/tasks/fido.yml new file mode 100644 index 0000000..be68578 --- /dev/null +++ b/ansible/roles/viper.tools/tasks/fido.yml @@ -0,0 +1,26 @@ +--- +# Install FIDO (Format Identification for Digital Objects) +# Python-based command-line tool for file format identification + +- name: "FIDO | Install Python3 pip if not present." + apt: + name: "python3-pip" + state: "present" + +- name: "FIDO | Install FIDO via pip (with --break-system-packages for Debian 12+)." + pip: + name: "{{ viper.tools.fido.package }}" + version: "{{ viper.tools.fido.version }}" + state: "present" + executable: pip3 + extra_args: "--break-system-packages" + +- name: "FIDO | Verify FIDO installation." + command: "fido -v" + register: fido_version_check + changed_when: false + failed_when: fido_version_check.rc != 0 + +- name: "FIDO | Display installed version." + debug: + msg: "{{ fido_version_check.stdout_lines }}" diff --git a/ansible/roles/viper.tools/tasks/jpylyzer.yml b/ansible/roles/viper.tools/tasks/jpylyzer.yml new file mode 100644 index 0000000..8f1af76 --- /dev/null +++ b/ansible/roles/viper.tools/tasks/jpylyzer.yml @@ -0,0 +1,26 @@ +--- +# Install jpylyzer (JP2 validator and properties extractor) +# Python-based command-line tool for JPEG 2000 validation + +- name: "jpylyzer | Install Python3 pip if not present." + apt: + name: "python3-pip" + state: "present" + +- name: "jpylyzer | Install jpylyzer via pip (with --break-system-packages for Debian 12+)." + pip: + name: "{{ viper.tools.jpylyzer.package }}" + version: "{{ viper.tools.jpylyzer.version }}" + state: "present" + executable: pip3 + extra_args: "--break-system-packages" + +- name: "jpylyzer | Verify jpylyzer installation." + command: "jpylyzer --version" + register: jpylyzer_version_check + changed_when: false + failed_when: jpylyzer_version_check.rc != 0 + +- name: "jpylyzer | Display installed version." + debug: + msg: "{{ jpylyzer_version_check.stdout_lines }}" diff --git a/ansible/roles/viper.tools/tasks/main.yml b/ansible/roles/viper.tools/tasks/main.yml index 9c43cf0..aa05284 100644 --- a/ansible/roles/viper.tools/tasks/main.yml +++ b/ansible/roles/viper.tools/tasks/main.yml @@ -17,3 +17,9 @@ - name: "ViPER TOOLS | Install veraPDF." import_tasks: verapdf.yml + +- name: "ViPER TOOLS | Install FIDO." + import_tasks: fido.yml + +- name: "ViPER TOOLS | Install jpylyzer." + import_tasks: jpylyzer.yml diff --git a/ansible/roles/viper.tools/tasks/tool.yml b/ansible/roles/viper.tools/tasks/tool.yml index a2c6364..b3c2ac1 100644 --- a/ansible/roles/viper.tools/tasks/tool.yml +++ b/ansible/roles/viper.tools/tasks/tool.yml @@ -24,10 +24,19 @@ dest: "{{ viper_apps_share }}/{{ item.name }}.desktop" mode: a+x -- name: "TOOL {{ item.name }} | Create user desktop file from template" +- name: "TOOL {{ item.name }} | Create user desktop file from template (VM)" template: src: "usr/share/applications/template.desktop" dest: "/home/{{ viper_account_name }}/Desktop/{{ item.name }}.desktop" owner: "{{ viper_account_name }}" group: "{{ viper_account_name }}" mode: a+x + +- name: "TOOL {{ item.name }} | Create user desktop file from template (Docker)" + template: + src: "usr/share/applications/template.desktop" + dest: "/config/Desktop/{{ item.name }}.desktop" + owner: "abc" + group: "abc" + mode: a+x + when: ansible_virtualization_type == "docker" diff --git a/docker-files/Dockerfile b/docker-files/Dockerfile new file mode 100644 index 0000000..f1089e2 --- /dev/null +++ b/docker-files/Dockerfile @@ -0,0 +1,9 @@ +FROM lscr.io/linuxserver/webtop:debian-xfce-48fa8b80-ls90 +# FROM docker-kasmvnc-darrenopf + +ENV TITLE="ViPER Cloud" + +COPY favicon.ico /ksuite/public/favicon.ico +COPY apple-touch-icon.png /ksuite/public/icon.png + +EXPOSE 3000 \ No newline at end of file diff --git a/docker-files/apple-touch-icon.png b/docker-files/apple-touch-icon.png new file mode 100644 index 0000000..d51be1e Binary files /dev/null and b/docker-files/apple-touch-icon.png differ diff --git a/docker-files/conky.conf b/docker-files/conky.conf new file mode 100644 index 0000000..ff81808 --- /dev/null +++ b/docker-files/conky.conf @@ -0,0 +1,87 @@ +-- Conky configuration for ViPER Docker +-- Simple system monitor with CPU, RAM, and Network stats + +conky.config = { + -- Window settings + alignment = 'top_right', + gap_x = 20, + gap_y = 60, + minimum_width = 250, + maximum_width = 250, + + -- Appearance + background = true, + border_width = 1, + default_color = '8A2BE2', + default_outline_color = 'white', + default_shade_color = 'white', + draw_borders = false, + draw_graph_borders = true, + draw_outline = false, + draw_shades = false, + + -- Transparency + own_window = true, + own_window_class = 'Conky', + own_window_type = 'normal', + own_window_transparent = false, + own_window_argb_visual = true, + own_window_argb_value = 180, + own_window_hints = 'undecorated,below,sticky,skip_taskbar,skip_pager', + + -- Font + use_xft = true, + font = 'DejaVu Sans Mono:size=9', + + -- Update interval + update_interval = 2.0, + cpu_avg_samples = 2, + net_avg_samples = 2, + + -- Double buffer to reduce flicker + double_buffer = true, + + -- Colors (all white) + default_color = 'FFFFFF', + color1 = 'FFFFFF', -- White + color2 = 'FFFFFF', -- White + color3 = 'FFFFFF', -- White +} + +conky.text = [[ +${color}${font DejaVu Sans:size=11:bold}ViPER SYSTEM${font} +${hr 2} + +${font DejaVu Sans:size=9:bold}CPU${goto 95}RAM${goto 175}NET${font} +${cpubar 60,70}${goto 90}${membar 60,70}${goto 170}${downspeedgraph eth0 60,70 FFFFFF FFFFFF -t} +${font DejaVu Sans:size=8}${cpu}%${goto 100}${memperc}%${goto 180}${downspeed eth0}${font} + +${voffset 10}${font DejaVu Sans:size=10:bold}Time & Location${font} +${time %A, %d %B %Y} +${time %H:%M:%S} +Timezone: ${tztime Europe/London %Z} + +${font DejaVu Sans:size=10:bold}System Temperature${font} +CPU Temp: ${hwmon 0 temp 1}°C + +${font DejaVu Sans:size=10:bold}CPU Usage${font} +CPU: ${cpu cpu0}% +${cpubar cpu0 8,} +${cpugraph cpu0 40,250 FFFFFF FFFFFF} + +${font DejaVu Sans:size=10:bold}Memory${font} +RAM: $mem / $memmax +${membar 8,} + +${font DejaVu Sans:size=10:bold}Disk I/O${font} +Read: ${diskio_read} +${diskiograph_read 30,250 FFFFFF FFFFFF} +Write: ${diskio_write} +${diskiograph_write 30,250 FFFFFF FFFFFF} + +${font DejaVu Sans:size=10:bold}Network${font} +Up: ${upspeed eth0} +${upspeedgraph eth0 30,250 FFFFFF FFFFFF} +Down: ${downspeed eth0} +${downspeedgraph eth0 30,250 FFFFFF FFFFFF} +]] diff --git a/docker-files/favicon-32x32.png b/docker-files/favicon-32x32.png new file mode 100644 index 0000000..8dcf2f8 Binary files /dev/null and b/docker-files/favicon-32x32.png differ diff --git a/docker-files/favicon.ico b/docker-files/favicon.ico new file mode 100644 index 0000000..9cddf66 Binary files /dev/null and b/docker-files/favicon.ico differ diff --git a/docker-files/post-install.sh b/docker-files/post-install.sh new file mode 100644 index 0000000..d58fcd0 --- /dev/null +++ b/docker-files/post-install.sh @@ -0,0 +1,142 @@ +#!/bin/bash +# Post-install script for ViPER Docker container +# This script runs on user login to configure desktop icons and permissions + +LOG_FILE="/config/viper-post-install.log" +DESKTOP_DIR="/config/Desktop" +MARKER_FILE="/config/.viper-desktop-configured" + +echo "$(date): Starting ViPER desktop configuration" >> "$LOG_FILE" + +# Detect if running as root or abc user (define function early for Conky startup) +CURRENT_USER=$(whoami) + +# Function to run command as abc user +run_as_abc() { + if [[ "$CURRENT_USER" == "root" ]]; then + runuser -u abc -- bash -c "$1" + else + bash -c "$1" + fi +} + +# Always start Conky if config exists (even if already configured) +if [[ -f /config/.conkyrc ]]; then + echo "$(date): Starting Conky system monitor" >> "$LOG_FILE" + run_as_abc 'export DISPLAY=:1 && pkill conky; sleep 1; conky -c /config/.conkyrc > /dev/null 2>&1 &' >> "$LOG_FILE" 2>&1 +fi + +# Exit if already configured +if [[ -f "$MARKER_FILE" ]]; then + echo "$(date): Desktop already configured, skipping" >> "$LOG_FILE" + exit 0 +fi + +echo "$(date): Script running as user: $CURRENT_USER" >> "$LOG_FILE" + +# Wait for desktop to be fully loaded +echo "$(date): Waiting for desktop environment to be ready" >> "$LOG_FILE" +for i in {1..30}; do + if pgrep -x xfdesktop > /dev/null && pgrep -x xfwm4 > /dev/null; then + echo "$(date): Desktop environment is ready" >> "$LOG_FILE" + break + fi + sleep 1 +done + +# Give it a few more seconds to stabilize +sleep 5 + +# Set ownership of gvfs metadata +chown -R abc:abc /config/.local/share/gvfs-metadata/ 2>/dev/null + +# Ensure Desktop directory exists and has correct ownership +mkdir -p "$DESKTOP_DIR" +chown abc:abc "$DESKTOP_DIR" +chmod 755 "$DESKTOP_DIR" + +# Copy desktop files from /home/viper/Desktop to /config/Desktop +echo "$(date): Copying desktop files" >> "$LOG_FILE" +if [[ -d /home/viper/Desktop ]]; then + cp -v /home/viper/Desktop/*.desktop "$DESKTOP_DIR/" 2>&1 >> "$LOG_FILE" + chown abc:abc "$DESKTOP_DIR"/*.desktop + chmod 755 "$DESKTOP_DIR"/*.desktop +fi + +# Copy Conky config from system location to user config (if it exists) +echo "$(date): Copying Conky configuration" >> "$LOG_FILE" +if [[ -f /usr/local/share/conky/conky.conf ]]; then + cp -v /usr/local/share/conky/conky.conf /config/.conkyrc 2>&1 >> "$LOG_FILE" + chown abc:abc /config/.conkyrc + chmod 644 /config/.conkyrc +fi + +# Create XFCE config directories +mkdir -p /config/.config/xfce4/xfconf/xfce-perchannel-xml +chown -R abc:abc /config/.config/xfce4 + +# Configure single workspace using xfconf-query +echo "$(date): Configuring single workspace" >> "$LOG_FILE" +run_as_abc 'export DISPLAY=:1 && xfconf-query -c xfwm4 -p /general/workspace_count -s 1' >> "$LOG_FILE" 2>&1 + +# Wait for X server and desktop to be ready +sleep 10 + +echo "$(date): Configuring desktop as abc user" >> "$LOG_FILE" + +# Hide desktop icons (Home, Filesystem, Trash) +# Run as abc user with proper environment +echo "$(date): Hiding desktop icons" >> "$LOG_FILE" +run_as_abc 'export DISPLAY=:1 && xfconf-query -c xfce4-desktop -p /desktop-icons/file-icons/show-home -n -t bool -s false' >> "$LOG_FILE" 2>&1 +run_as_abc 'export DISPLAY=:1 && xfconf-query -c xfce4-desktop -p /desktop-icons/file-icons/show-filesystem -n -t bool -s false' >> "$LOG_FILE" 2>&1 +run_as_abc 'export DISPLAY=:1 && xfconf-query -c xfce4-desktop -p /desktop-icons/file-icons/show-trash -n -t bool -s false' >> "$LOG_FILE" 2>&1 + +# Set desktop wallpaper +echo "$(date): Setting desktop wallpaper" >> "$LOG_FILE" +run_as_abc 'export DISPLAY=:1 && xfconf-query -c xfce4-desktop -p /backdrop/screen0/monitorVNC-0/workspace0/last-image -s /usr/local/share/backgrounds/viper-desktop.jpg' >> "$LOG_FILE" 2>&1 +run_as_abc 'export DISPLAY=:1 && xfconf-query -c xfce4-desktop -p /backdrop/screen0/monitorVNC-0/workspace0/image-style -s 5' >> "$LOG_FILE" 2>&1 + +# Configure XFCE panel - add system monitor plugins +echo "$(date): Configuring XFCE panel plugins" >> "$LOG_FILE" + +# Note: Panel plugin configuration is complex and may not persist properly +# The plugins are installed but need manual addition to the panel +# Users can right-click the panel > Panel > Add New Items > Choose system monitors + +# Set Firefox as default web browser using xdg-mime +run_as_abc 'export DISPLAY=:1 && xdg-mime default firefox-esr.desktop x-scheme-handler/http' >> "$LOG_FILE" 2>&1 +run_as_abc 'export DISPLAY=:1 && xdg-mime default firefox-esr.desktop x-scheme-handler/https' >> "$LOG_FILE" 2>&1 +run_as_abc 'export DISPLAY=:1 && xdg-mime default firefox-esr.desktop text/html' >> "$LOG_FILE" 2>&1 + +# Reload desktop settings by restarting desktop manager and window manager +echo "$(date): Reloading desktop settings" >> "$LOG_FILE" +# Restart xfdesktop to apply wallpaper and icon changes +run_as_abc 'export DISPLAY=:1 && pkill xfdesktop && sleep 1 && xfdesktop > /dev/null 2>&1 &' >> "$LOG_FILE" 2>&1 +# Restart window manager to apply workspace changes +sleep 1 +run_as_abc 'export DISPLAY=:1 && xfwm4 --replace' >> "$LOG_FILE" 2>&1 & + +echo "$(date): Desktop configuration completed and applied" >> "$LOG_FILE" + +# Make all desktop files executable and trusted +if [[ -d "$DESKTOP_DIR" ]]; then + for file in "$DESKTOP_DIR"/*.desktop; do + if [[ -f "$file" ]]; then + # Set correct ownership and permissions + chown abc:abc "$file" + chmod 755 "$file" + + # Generate checksum for XFCE + checksum=$(sha256sum "$file" | awk '{print $1}') + + echo "$(date): Processing $file with checksum: $checksum" >> "$LOG_FILE" + + # Set the metadata to mark as trusted + gio set -t string "$file" metadata::xfce-exe-checksum "$checksum" 2>&1 | tee -a "$LOG_FILE" + fi + done +fi + +# Create marker file to prevent re-running +touch "$MARKER_FILE" +echo "$(date): ViPER desktop configuration completed" >> "$LOG_FILE" diff --git a/docker-files/toggle-conky.desktop b/docker-files/toggle-conky.desktop new file mode 100644 index 0000000..143821f --- /dev/null +++ b/docker-files/toggle-conky.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=Toggle System Monitor +Comment=Show/Hide Conky system monitor +Exec=/usr/local/bin/toggle-conky.sh +Icon=utilities-system-monitor +Terminal=false +Categories=System;Monitor; diff --git a/docker-files/toggle-conky.sh b/docker-files/toggle-conky.sh new file mode 100644 index 0000000..8a73673 --- /dev/null +++ b/docker-files/toggle-conky.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Toggle Conky system monitor on/off + +if pgrep -x conky > /dev/null; then + # Conky is running, kill it + pkill conky + notify-send "Conky" "System monitor hidden" -i utilities-system-monitor +else + # Conky is not running, start it + if [ -f /config/.conkyrc ]; then + conky -c /config/.conkyrc > /dev/null 2>&1 & + notify-send "Conky" "System monitor shown" -i utilities-system-monitor + else + notify-send "Conky" "Config file not found" -i dialog-error + fi +fi diff --git a/http/preseed.cfg b/http/preseed.cfg new file mode 100644 index 0000000..6ac3613 --- /dev/null +++ b/http/preseed.cfg @@ -0,0 +1,61 @@ +#### Contents of the preconfiguration file (for bookworm) +### Localization +d-i debian-installer/locale string en_US.UTF-8 +d-i keyboard-configuration/xkb-keymap select us + +### Network configuration +d-i netcfg/choose_interface select auto +d-i netcfg/get_hostname string viper +d-i netcfg/get_domain string viper.test + +### Mirror settings +d-i mirror/country string manual +d-i mirror/http/hostname string deb.debian.org +d-i mirror/http/directory string /debian +d-i mirror/http/proxy string + +### Account setup +d-i passwd/root-login boolean false +d-i passwd/user-fullname string Vagrant User +d-i passwd/username string vagrant +d-i passwd/user-password password vagrant +d-i passwd/user-password-again password vagrant +d-i user-setup/allow-password-weak boolean true + +### Clock and time zone setup +d-i clock-setup/utc boolean true +d-i time/zone string Europe/Berlin +d-i clock-setup/ntp boolean true + +### Partitioning +d-i partman-auto/method string regular +d-i partman-auto/choose_recipe select atomic +d-i partman-partitioning/confirm_write_new_label boolean true +d-i partman/choose_partition select finish +d-i partman/confirm boolean true +d-i partman/confirm_nooverwrite boolean true + +### Package selection +tasksel tasksel/first multiselect standard, ssh-server +d-i pkgsel/include string sudo openssh-server build-essential +d-i pkgsel/upgrade select full-upgrade + +# Don't participate in package usage survey +popularity-contest popularity-contest/participate boolean false + +### Boot loader installation +d-i grub-installer/only_debian boolean true +d-i grub-installer/bootdev string default + +### Finishing up +d-i finish-install/reboot_in_progress note + +### Configure vagrant user +d-i preseed/late_command string \ + in-target sh -c 'mkdir -p /home/vagrant/.ssh'; \ + in-target sh -c 'wget -O /home/vagrant/.ssh/authorized_keys https://raw.githubusercontent.com/hashicorp/vagrant/master/keys/vagrant.pub'; \ + in-target sh -c 'chown -R vagrant:vagrant /home/vagrant/.ssh'; \ + in-target sh -c 'chmod 700 /home/vagrant/.ssh'; \ + in-target sh -c 'chmod 600 /home/vagrant/.ssh/authorized_keys'; \ + in-target sh -c 'echo "vagrant ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/vagrant'; \ + in-target sh -c 'chmod 440 /etc/sudoers.d/vagrant' diff --git a/scripts/convert-to-ova.sh b/scripts/convert-to-ova.sh new file mode 100755 index 0000000..f98905e --- /dev/null +++ b/scripts/convert-to-ova.sh @@ -0,0 +1,170 @@ +#!/bin/bash +# Script to convert QEMU output to VirtualBox-compatible OVA format. +# +# OVF structure based on a known-working VirtualBox export (ViPER v1.2). +# Uses only qemu-img and tar -- no VirtualBox installation required, +# so this runs in GitHub Actions without extra dependencies. + +set -e + +VM_NAME="${1:-viper-v1.2-alpha}" +OUTPUT_DIR="${2:-output-qemu}" +FINAL_DIR="output" +DISK_NAME="${VM_NAME}-disk001.vmdk" + +echo "Converting ${VM_NAME} to OVA format..." + +# Create output directory +mkdir -p "${FINAL_DIR}" + +# Step 1: Convert QCOW2 to stream-optimised VMDK +echo "Step 1: Converting QCOW2 to VMDK..." +qemu-img convert -O vmdk -o subformat=streamOptimized \ + "${OUTPUT_DIR}/${VM_NAME}.qcow2" \ + "${FINAL_DIR}/${DISK_NAME}" + +VMDK_SIZE=$(stat -c%s "${FINAL_DIR}/${DISK_NAME}") +DISK_CAPACITY="53687091200" # 50 GB in bytes + +# Generate unique UUIDs (works on Linux without uuidgen) +VM_UUID=$(cat /proc/sys/kernel/random/uuid) +DISK_UUID=$(cat /proc/sys/kernel/random/uuid) + +echo " VM UUID: ${VM_UUID}" +echo " Disk UUID: ${DISK_UUID}" + +# Step 2: Create OVF descriptor +# Structure matches known-working VirtualBox OVA exports: +# - vbox:uuid on must equal in StorageControllers +# - SATA/AHCI controller (ResourceType 20) instead of SCSI +# - VirtualSystemType = virtualbox-2.2 +echo "Step 2: Creating OVF descriptor..." +cat > "${FINAL_DIR}/${VM_NAME}.ovf" << EOF + + + + + + + List of the virtual disks used in the package + + + + Logical networks used in the package + + Logical network used by this appliance. + + + + A virtual machine + + Meta-information about the installed software + ViPER + Open Preservation Foundation + v1.2 + https://viper.openpreservation.org + https://openpreservation.org + + + The kind of installed guest operating system + Debian_64 + Debian_64 + + + Virtual hardware requirements for a virtual machine + + Virtual Hardware Family + 0 + ${VM_NAME} + virtualbox-2.2 + + + 2 virtual CPU + Number of virtual CPUs + 2 virtual CPU + 1 + 3 + 2 + + + MegaBytes + 4096 MB of memory + Memory Size + 4096 MB of memory + 2 + 4 + 4096 + + + 0 + sataController0 + SATA Controller + sataController0 + 3 + AHCI + 20 + + + 0 + disk1 + Disk Image + disk1 + /disk/vmdisk1 + 4 + 3 + 17 + + + true + Ethernet adapter on 'NAT' + NAT + Ethernet adapter on 'NAT' + 5 + E1000 + 10 + + + + Complete VirtualBox machine configuration in VirtualBox format + + + + + + + + + + + + + + + + + + + + + + + + + + + +EOF + +# Step 3: Package as OVA (tar archive: OVF first, then disk) +echo "Step 3: Creating OVA package..." +cd "${FINAL_DIR}" +tar -cvf "${VM_NAME}.ova" "${VM_NAME}.ovf" "${DISK_NAME}" + +# Cleanup intermediate files +rm -f "${VM_NAME}.ovf" +rm -f "${DISK_NAME}" + +echo "" +echo "OVA created successfully: ${FINAL_DIR}/${VM_NAME}.ova" +echo "Size: $(du -h ${VM_NAME}.ova | cut -f1)" +echo "Cleaned up intermediate VMDK and OVF files" diff --git a/scripts/install-guest-additions.sh b/scripts/install-guest-additions.sh new file mode 100755 index 0000000..ab5574a --- /dev/null +++ b/scripts/install-guest-additions.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# Install VirtualBox Guest Additions +# This script installs the guest additions from Debian repositories +# Debian 12 Bookworm requires the Fasttrack repository for VirtualBox packages + +set -e + +echo "Installing VirtualBox Guest Additions..." + +# Write a known-good deb822 sources file with contrib enabled +sudo tee /etc/apt/sources.list.d/debian.sources > /dev/null << 'EOF' +Types: deb +URIs: http://deb.debian.org/debian +Suites: bookworm bookworm-updates +Components: main contrib non-free non-free-firmware + +Types: deb +URIs: http://deb.debian.org/debian-security +Suites: bookworm-security +Components: main contrib non-free non-free-firmware +EOF + +# Clear any conflicting traditional sources.list +if [ -f /etc/apt/sources.list ] && [ -s /etc/apt/sources.list ]; then + sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak + sudo truncate -s 0 /etc/apt/sources.list +fi + +# Add Debian Fasttrack repository (required for VirtualBox packages on Bookworm) +# First install the keyring from the main repos before adding the fasttrack source +sudo apt-get update +sudo apt-get install -y fasttrack-archive-keyring + +sudo tee /etc/apt/sources.list.d/fasttrack.sources > /dev/null << 'EOF' +Types: deb +URIs: https://fasttrack.debian.net/debian-fasttrack +Suites: bookworm-fasttrack +Components: main contrib +EOF + +sudo apt-get update + +# Install kernel headers and build tools required for guest additions +sudo apt-get install -y \ + build-essential \ + linux-headers-$(uname -r) \ + dkms + +# Install VirtualBox Guest Additions from Debian repos +sudo apt-get install -y virtualbox-guest-utils virtualbox-guest-x11 + +# Enable and start the VirtualBox guest services +sudo systemctl enable virtualbox-guest-utils +sudo systemctl start virtualbox-guest-utils || true + +echo "VirtualBox Guest Additions installed successfully" diff --git a/viper.pkr.hcl b/viper.pkr.hcl new file mode 100644 index 0000000..42f9c06 --- /dev/null +++ b/viper.pkr.hcl @@ -0,0 +1,155 @@ +packer { + required_plugins { + qemu = { + version = ">= 1.0.0" + source = "github.com/hashicorp/qemu" + } + ansible = { + version = ">= 1.1.0" + source = "github.com/hashicorp/ansible" + } + } +} + +variable "vm_name" { + type = string + default = "viper-v1.2-alpha" +} + +variable "cpus" { + type = number + default = 2 +} + +variable "memory" { + type = number + default = 4096 +} + +variable "disk_size" { + type = string + default = "25G" +} + +variable "headless" { + type = bool + default = false +} + +variable "accelerator" { + type = string + default = "kvm" + description = "QEMU accelerator (kvm, tcg, none)" +} + +variable "output_directory" { + type = string + default = "output-qemu" +} + +variable "iso_url" { + type = string + default = "https://cdimage.debian.org/cdimage/archive/12.8.0/amd64/iso-cd/debian-12.8.0-amd64-netinst.iso" +} + +variable "iso_checksum" { + type = string + default = "sha256:04396d12b0f377958a070c38a923c227832fa3b3e18ddc013936ecf492e9fbb3" +} + +source "qemu" "debian-bookworm" { + vm_name = var.vm_name + iso_url = var.iso_url + iso_checksum = var.iso_checksum + output_directory = var.output_directory + + disk_size = var.disk_size + disk_interface = "virtio" + disk_compression = true + format = "qcow2" + + cpus = var.cpus + memory = var.memory + + headless = var.headless + accelerator = var.accelerator + + # Use VNC for display + vnc_bind_address = "127.0.0.1" + vnc_port_min = 5900 + vnc_port_max = 5900 + + # Networking + net_device = "virtio-net" + + # SSH settings for provisioning + ssh_username = "vagrant" + ssh_password = "vagrant" + ssh_timeout = "30m" + ssh_port = 22 + + # Shutdown command + shutdown_command = "echo 'vagrant' | sudo -S shutdown -P now" + + # Boot command for Debian preseed + boot_wait = "5s" + boot_command = [ + "", + "auto ", + "console-setup/ask_detect=false ", + "console-keymaps-at/keymap=us ", + "debconf/frontend=noninteractive ", + "debian-installer=en_US.UTF-8 ", + "fb=false ", + "install ", + "kbd-chooser/method=us ", + "keyboard-configuration/xkb-keymap=us ", + "locale=en_US.UTF-8 ", + "netcfg/get_hostname=${var.vm_name} ", + "netcfg/get_domain=viper.test ", + "preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg ", + "" + ] + + # Serve preseed file via HTTP + http_directory = "http" +} + +build { + sources = ["source.qemu.debian-bookworm"] + + # Wait for system to be ready + provisioner "shell" { + inline = [ + "echo 'Waiting for system to be ready...'", + "sudo apt-get update" + ] + } + + # Install VirtualBox Guest Additions + provisioner "shell" { + script = "scripts/install-guest-additions.sh" + } + + # Run Ansible provisioning + provisioner "ansible" { + playbook_file = "ansible/packer.yml" + user = "vagrant" + extra_arguments = ["-vv"] + } + + # Rename QCOW2 file to include .qcow2 extension + post-processor "shell-local" { + inline = [ + "mv ${var.output_directory}/${var.vm_name} ${var.output_directory}/${var.vm_name}.qcow2 || true", + "echo 'QCOW2 file: ${var.output_directory}/${var.vm_name}.qcow2'" + ] + } + + # Post-processor to convert to VMDK and create OVA + post-processor "shell-local" { + inline = [ + "echo 'Build complete. Run ./scripts/convert-to-ova.sh to create OVA file.'" + ] + } +}